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

IT技术 javascript google-chrome pdf ecmascript-6 blob
2021-01-15 01:21:28

在我的 Vue 应用程序中,我收到一个 Blob 格式的 PDF,并希望使用浏览器的 PDF 查看器显示它。

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

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

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

<object
  :data="invoiceUrl"
  type="application/pdf"
  width="100%"
  style="height: 100vh;">
</object>

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

查看器和文件名就像我在 Firefox 中所希望的那样工作;只有 Chrome 没有使用文件名。

有什么方法可以使用原生 Javascript(包括 ES6,但没有除 Vue 以外的第 3 方依赖项)在 Chrome 中为 blob/对象元素设置文件名?

[编辑] 如果有帮助,响应具有以下相关标题:

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
2个回答

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

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

现在,有些情况是做不到的,对于这些,有一个复杂的方法,它可能在未来版本的 Chrome 中不起作用,也可能在其他浏览器中不起作用,需要设置一个Service Worker

正如我们首先所说,Chrome 解析 URI 以搜索文件名,所以我们要做的就是拥有一个 URI,该文件名指向我们的 blob:// URI。

为此,我们可以使用缓存 API,使用我们的 URL 将我们的文件作为请求存储在那里,然后从 ServiceWorker 的缓存中检索该文件。

或者在代码中,

从主页面

// register our ServiceWorker
navigator.serviceWorker.register('/sw.js')
  .then(...
...

async function displayRenamedPDF(file, filename) {
  // we use an hard-coded fake path
  // to not interfere with legit requests
  const reg_path = "/name-forcer/";
  const url = reg_path + filename;
  // store our File in the Cache
  const store = await caches.open( "name-forcer" );
  await store.put( url, new Response( file ) );

  const frame = document.createElement( "iframe" );
  frame.width = 400
  frame.height = 500;
  document.body.append( frame );
  // makes the request to the File we just cached
  frame.src = url;
  // not needed anymore
  frame.onload = (evt) => store.delete( url );
}

在 ServiceWorker sw.js 中

self.addEventListener('fetch', (event) => {
  event.respondWith( (async () => {
    const store = await caches.open("name-forcer");
    const req = event.request;
    const cached = await store.match( req );
    return cached || fetch( req );
  })() );
});

现场示例来源

编辑:这实际上在 Chrome 中不起作用......

虽然它确实在对话框中正确设置了文件名,但在将文件保存到磁盘时他们似乎无法检索文件......
他们似乎没有执行网络请求(因此我们的软件没有捕捉到任何东西) ,我现在真的不知道去哪里看。
尽管如此,这可能是未来这方面工作的良好基础。


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

Mozilla 已经提供了基于 js 的插件pdf.js,所以从那里我们应该能够设置文件名(即使我再次没有在那里挖掘)。


最后要注意的是,Firefox 能够使用blobURI 指向nameFile对象属性因此,即使这不是 OP 所要求的,在 FF 中它所需要的只是

const file = new File([blob], filename);
const url = new URL(blob);
object.data = url;
我实施了这个解决方案。它可以显示PDF。但是当单击“下载”图标(在 Chrome 或 Edge 中)时,它表示该文档不存在。而且我看不到外面的请求。即使通过调试工作程序,也不会调用侦听器。当然,它在 Firefox 中完美运行。如果有人知道为什么...
2021-03-28 01:21:28
这真是令人难以置信的彻底-谢谢。知道服务工作者是一种选择是有用的,我认为使用 pdf.js(或类似的)可能是我的情况的最佳解决方案。了解 Chrome 从 URI 获取文件名也很有帮助(鉴于当前行为,这是有道理的)。
2021-04-01 01:21:28
@AdrienClerc han 你是对的......我虽然我有一个更好的解决方案,但实际上,虽然它在对话框中正确设置了文件名,但它确实无法将文件保存在磁盘上......他们似乎没有向软件发出新请求,所以我不知道它在哪里中断......
2021-04-03 01:21:28

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

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

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

2021 年底有什么变化还是仍然有效?
2021-04-07 01:21:28