我可以设置Chrome中显示的PDF对象的文件名吗?

fal*_*ure 11 javascript pdf google-chrome blob ecmascript-6

在我的Vue应用程序中,我收到PDF作为blob,并希望使用浏览器的PDF查看器显示它.

我将其转换为文件,并生成一个对象url:

const blobFile = new File([blob], `my-file-name.pdf`, { type: 'application/pdf' })
this.invoiceUrl = window.URL.createObjectURL(blobFile)
Run Code Online (Sandbox Code Playgroud)

然后我通过将该URL设置为data对象元素的属性来显示它.

<object
  :data="invoiceUrl"
  type="application/pdf"
  width="100%"
  style="height: 100vh;">
</object>
Run Code Online (Sandbox Code Playgroud)

然后,浏览器使用PDF查看器显示PDF.但是,在Chrome中,我没有使用我提供的文件名(此处为my-file-name.pdf):我在PDF查看器的标题栏中看到一个哈希值,当我使用"右键单击"下载文件时- >另存为...'或查看器的控件,它使用blob的哈希(cda675a6-10af-42f3-aa68-8795aa8c377d或类似)保存文件.

浏览器和文件名的工作方式与我在Firefox中所希望的一样; 它只是不使用文件名的Chrome.

有没有办法,使用原生Javascript(包括ES6,但没有Vue以外没有第三方依赖),为Chrome中的blob/object元素设置文件名?

[edit]如果有帮助,则响应具有以下相关标头:

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
Run Code Online (Sandbox Code Playgroud)

Old*_*Pro 8

在 Chrome 中,文件名源自 URL,因此只要您使用 blob URL,简短的回答就是“不,您不能设置 Chrome 中显示的 PDF 对象的文件名”。您无法控制分配给 blob URL 的 UUID,也无法将其覆盖为使用该object元素的页面名称。可能在 PDF 中指定了标题,该标题将作为文档名称出现在 PDF 查看器中,但在下载时您仍会获得哈希名称。

这似乎是一种安全预防措施,但我不能肯定地说。

当然,如果您可以控制 URL,则可以通过更改 URL 轻松设置 PDF 文件名。


Kai*_*ido 6

Chrome的扩展名似乎依赖于URI中设置的资源名称,即file.extin protocol://domain/path/file.ext

因此,如果您的原始URI包含该文件名,那么最简单的方法就是将您的<object> data设为直接从中获取pdf的URI,而不是采用Blob的方式。

现在,有些情况是无法完成的,为此,有一种复杂的方法,这种方法可能无法在将来的Chrome版本中使用,并且可能在其他浏览器中也无法使用,因此需要设置Service Worker

就像我们首先说的那样,Chrome浏览器会解析URI以查找文件名,因此我们要做的就是使用一个带有该文件名的URI指向我们的BlobURI。

为了能够做到这一点,我目前发现的唯一方法是

  1. 从文档发出POST请求,该请求会将Blob发送到我们的ServiceWorker。
  2. 在ServiceWorker中,缓存已发送的Blob
  3. 仍然从ServiceWorker,返回一个新的伪URI,我们将使用2中缓存的Blob进行映射
  4. 在文档中,将src<iframe> 1的设置为该假URI。
  5. 从ServiceWorker中捕获请求,然后发送我们的缓存Blob。
  6. 从文档中享受。

1. 我无法在Chrome中看到从<object>标记发出的请求,因此在这里使用iframe可能更好(无论如何在IE中也会有更好的结果)。

或在代码中

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);
  })
}
Run Code Online (Sandbox Code Playgroud)

在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))
  }
});
Run Code Online (Sandbox Code Playgroud)

不幸的是,我无法在plnkr.co上运行ServiceWorker,但是您仍然可以在此处找到应该可以复制到本地主机的整个设置


另外一种解决方案是运行您自己的pdf查看器,我没有花时间自己检查。

Mozilla已使其基于js的插件pdf.js可用,因此从那里我们应该可以设置文件名(即使我再也没有在那儿挖过)。


最后一点,Firefox能够使用blobURI指向nameFileObject 的属性。因此,即使这不是OP所要求的,但在FF中,它所需要的只是

const file = new File([blob], filename);
const url = new URL(blob);
object.data = url;
Run Code Online (Sandbox Code Playgroud)

  • 这真是令人难以置信的彻底-谢谢。知道服务工作者是一种选择是有用的,我认为使用 pdf.js(或类似的)可能是我的情况的最佳解决方案。知道 Chrome 从 URI 获取文件名也很有帮助(考虑到当前的行为,这是有道理的)。 (2认同)