In my Vue app I receive a PDF as a blob, and want to display it using the browser\'s PDF viewer.
I convert it to a file, and generate an object url:
Chrome's extension seems to rely on the resource name set in the URI, i.e the file.ext
in protocol://domain/path/file.ext
.
So if your original URI contains that filename, the easiest might be to simply make your <object>'s data
to the URI you fetched the pdf from directly, instead of going the Blob's way.
Now, there are cases it can't be done, and for these, there is a convoluted way, which might not work in future versions of Chrome, and probably not in other browsers, requiring to set up a Service Worker.
As we first said, Chrome parses the URI in search of a filename, so what we have to do, is to have an URI, with this filename, pointing to our BlobURI.
And to be able to do it, the only way I found for now, is to
src
of an <iframe>1 to that fake URI.1. I wasn't able to see the requests made from an <object> tag in Chrome, so probably better to use an iframe here (would have better result in IE too anyway).
Or in code,
In 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);
})
}
In the 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))
}
});
I was unfortunately unable to make a ServiceWorker work on plnkr.co, but you can still find the whole setup here that you should be able to copy to your localhost.
And an other solution, I didn't took the time to check by myself, would be to run your own pdf viewer.
Mozilla has made its js based plugin pdf.js available, so from there we should be able to set the filename (even though once again I didn't dug there yet).
And as final note, Firefox is able to use the name
property of a File
Object a blobURI points to.
So even though it's not what OP asked for, in FF all it requires is
const file = new File([blob], filename);
const url = new URL(blob);
object.data = url;
In Chrome, the filename is derived from the URL, so as long as you are using a blob URL, the short answer is "No, you cannot set the filename of a PDF object displayed in Chrome." You have no control over the UUID assigned to the blob URL and no way to override that as the name of the page using the object
element. It is possible that inside the PDF a title is specified, and that will appear in the PDF viewer as the document name, but you still get the hash name when downloading.
This appears to be a security precaution, but I cannot say for sure.
Of course, if you have control over the URL, you can easily set the PDF filename by changing the URL.