Can I set the filename of a PDF object displayed in Chrome?

南笙酒味 提交于 2019-11-27 02:01:34

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

  1. From the document, make a POST request, which will send the Blob to our ServiceWorker.
  2. From the ServiceWorker, cache the Blob that got sent
  3. Still from the ServiceWorker, return a new, fake URI, that we will map with the Blob cached in 2.
  4. From the document, set the src of an <iframe>1 to that fake URI.
  5. From he ServiceWorker, catch the request and send our cached Blob instead.
  6. From the document, enjoy.

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;

If you are requesting the resource via your own server, you can use Content-Disposition response header to pass the file name in the response.

Content-Disposition: inline
Content-Disposition: attachment
Content-Disposition: attachment; filename="filename.jpg"

You can replace it with you file name and extension. You can have a look at this link to explore this header further as per your need.

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.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!