javascript - ¿Puedo configurar el nombre de archivo de un objeto PDF que se muestra en Chrome?
google-chrome ecmascript-6 (3)
En Chrome, el nombre de archivo se deriva de la URL, por lo tanto, mientras esté usando una URL de blob, la respuesta corta es "No, no puede establecer el nombre de archivo de un objeto PDF que se muestra en Chrome".
No tiene control sobre el UUID asignado a la URL del blob y no hay forma de anularlo como el nombre de la página utilizando el elemento de
object
.
Es posible que dentro del PDF se especifique un título, y que aparezca en el visor de PDF como nombre del documento, pero aún así obtendrá el nombre de hash al descargar.
Esto parece ser una precaución de seguridad, pero no puedo decir con seguridad.
Por supuesto, si tiene control sobre la URL, puede configurar fácilmente el nombre de archivo PDF cambiando la URL.
En mi aplicación Vue recibo un PDF como un blob, y quiero mostrarlo usando el visor de PDF del navegador.
Lo convierto en un archivo, y genero una url de objeto:
const blobFile = new File([blob], `my-file-name.pdf`, { type: ''application/pdf'' })
this.invoiceUrl = window.URL.createObjectURL(blobFile)
Luego lo muestro configurando esa URL como el atributo de
data
de un elemento de objeto.
<object
:data="invoiceUrl"
type="application/pdf"
width="100%"
style="height: 100vh;">
</object>
El navegador muestra el PDF utilizando el visor de PDF.
Sin embargo, en Chrome, el nombre de archivo que proporciono (aquí, my-file-name.pdf) no se usa: veo un hash en la barra de título del visor de PDF, y cuando descargo el archivo haciendo clic con el botón derecho -> Guardar como ... ''o los controles del espectador, guarda el archivo con el hash del blob (
cda675a6-10af-42f3-aa68-8795aa8c377d
o similar).
El visor y el nombre del archivo funcionan como esperaba en Firefox; solo es Chrome en el que no se usa el nombre del archivo.
¿Hay alguna forma, usando JavaScript nativo (incluido ES6, pero no dependencias de terceros que no sea Vue), para establecer el nombre de archivo para un elemento blob / object en Chrome?
[editar] Si ayuda, la respuesta tiene los siguientes encabezados relevantes:
Content-Type: application/pdf; charset=utf-8
Transfer-Encoding: chunked
Content-Disposition: attachment; filename*=utf-8''''Invoice%2016246.pdf;
Content-Description: File Transfer
Content-Encoding: gzip
La extensión de Chrome parece depender del
nombre de recurso
establecido en el URI, es decir, el
file.ext
en el
protocol://domain/path/file.ext
.
Entonces, si su URI original contiene ese nombre de archivo, lo más fácil sería simplemente hacer que los datos de su <objeto> a la URI de la que obtuvo el pdf directamente, en lugar de seguir el camino del Blob.
Ahora, hay casos en que no se puede hacer, y para estos, hay una forma complicada , que podría no funcionar en futuras versiones de Chrome, y probablemente no en otros navegadores, que requieren configurar un trabajador de servicio .
Como dijimos por primera vez, Chrome analiza el URI en busca de un nombre de archivo, así que lo que tenemos que hacer es tener un URI, con este nombre de archivo, apuntando a nuestro BlobURI.
Y para poder hacerlo, la única forma que encontré por ahora, es
- Desde el documento, realice una solicitud POST, que enviará el Blob a nuestro ServiceWorker.
- Desde el ServiceWorker, almacene en caché el Blob que se envió
- Aún desde el ServiceWorker, devuelva un nuevo URI falso que mapearemos con el Blob en caché en 2.
-
Desde el documento, establezca el
src
de un <iframe> 1 a ese URI falso. - Desde el ServiceWorker, capture la solicitud y envíe nuestro Blob en caché.
- Desde el documento, disfrutar.
1. No pude ver las solicitudes realizadas desde una etiqueta <object> en Chrome, así que probablemente sea mejor usar un iframe aquí (de todos modos, también tendría un mejor resultado en IE).
O en código,
En document.html
// register our ServiceWorker
navigator.serviceWorker.register(''/sw.js'')
.then(...
...
function displayRenamedPDF(file, filename) {
// we use an hard-coded fake path
// that we will check in every requests from the SW
const reg_path = ''/nameForcer_register/''
// pass the filename
const url = reg_path + encodeURIComponent(filename);
const xhr = new XMLHttpRequest();
xhr.open(''POST'', url);
return new Promise((res, rej) => {
xhr.onload = e => {
const frame = document.createElement(''iframe'');
frame.src = xhr.response;
document.body.append(frame);
return frame;
};
xhr.send(file);
})
}
En el ServiceWorker sw.js
self.addEventListener(''fetch'', function(event) {
const req = event.request,
url = new URL(req.url),
// parse the /path/ from every request url
pathes = url.pathname.split(''/''),
// are we registering
nameRegIndex = pathes.indexOf(''nameForcer_register''),
// or fetching
nameFetcherIndex = pathes.indexOf(''nameForcer_fetch'');
if(nameRegIndex > -1) { // register
event.respondWith(
req.blob() // grab the POSTed Blob
.then((blob) => {
const filename = pathes[nameRegIndex+1] || '''';
// store in our db object
db[filename] = blob;
return filename;
})
.then((filename) =>
new Response(''/nameForcer_fetch/'' + filename)
)
);
}
else if(nameFetcherIndex > -1) { // fetch
const filename = pathes[nameFetcherIndex + 1];
const cached = db[filename];
// just for Firefox, Chrome doesn''t care...
const headers = new Headers({
''Content-Disposition'': ''inline; filename="'' + decodeURIComponent(filename) + ''"''
});
event.respondWith(
new Response(cached, {
headers: headers
})
);
delete db[filename]; // !! one time URI !!
}
else { // normal requests
event.respondWith(fetch(event.request))
}
});
Desafortunadamente, no pude hacer que un ServiceWorker funcione en plnkr.co, pero aún puede encontrar toda la configuración aquí que debería poder copiar en su host local.
Y otra solución, no me tomé el tiempo de comprobarlo, sería ejecutar su propio visor de pdf.
Mozilla ha puesto a disposición su plugin basado en js pdf.js , por lo que desde allí deberíamos poder establecer el nombre de archivo (aunque una vez más no lo hice).
Y como nota final, Firefox puede usar la propiedad de
name
de un objeto de
File
al que apunta blobURI.
Entonces, aunque no es lo que pidió OP,
en FF
todo lo que requiere es
const file = new File([blob], filename);
const url = new URL(blob);
object.data = url;
Si solicita el recurso a través de su propio servidor, puede usar el encabezado de respuesta de
Content-Disposition
para pasar el nombre del archivo en la respuesta.
Content-Disposition: inline
Content-Disposition: attachment
Content-Disposition: attachment; filename="filename.jpg"
Puede reemplazarlo con su nombre de archivo y extensión. Puede echar un vistazo a este link para explorar más este encabezado según su necesidad.