【问题标题】:Can I set the filename of a PDF object displayed in Chrome?我可以设置在 Chrome 中显示的 PDF 对象的文件名吗?
【发布时间】:2019-05-02 01:22:16
【问题描述】:

在我的 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 查看器的标题栏中看到一个哈希,当我使用“右键单击”下载文件时-> Save as...' 或查看器的控件,它使用 blob 的哈希(cda675a6-10af-42f3-aa68-8795aa8c377d 或类似)保存文件。

查看器和文件名的工作方式与我在 Firefox 中所希望的一样;只有 Chrome 没有使用文件名。

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

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

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

【问题讨论】:

  • 您是否通过来自特定服务器的 HTTP 请求接收 PDF?
  • 是的,来自我自己的服务器,通过 GET 请求。
  • 你能在这里添加如何,你正在获取文件吗?您是否尝试更改 Content-Disposition: inline; ?

标签: javascript google-chrome pdf ecmascript-6 blob


【解决方案1】:

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

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

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

【讨论】:

  • 2021 年底有任何变化还是仍然有效?
【解决方案2】:

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

因此,如果您的原始 URI 包含该文件名,最简单的方法可能是简单地将您的 的 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 );
  })() );
});

Live example (source)

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

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


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

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


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

const file = new File([blob], filename);
const url = new URL(blob);
object.data = url;

【讨论】:

  • 这非常彻底——谢谢。知道服务人员是一个选项很有用,我认为使用 pdf.js(或类似的)可能是我的情况的最佳解决方案。知道 Chrome 从 URI 获取文件名也很有帮助(考虑到当前的行为,这是有道理的)。
  • 我实现了这个解决方案。它可以显示 PDF。但是当单击“下载”图标(在 Chrome 或 Edge 中)时,它说该文档不存在。而且我看不到外面的请求。即使通过调试工人,也不会调用侦听器。当然,它在 Firefox 中完美运行。如果有人知道为什么……
  • @AdrienClerc 你是对的......虽然我有一个更好的解决方案,但实际上,虽然它确实在对话框中正确设置了文件名,但它确实无法将文件保存在磁盘上。 ..他们似乎没有向软件提出新的请求,所以我不知道它在哪里中断...
【解决方案3】:

我正在使用库pdf-lib,您可以click here了解更多关于该库的信息。

我通过使用 api Document.setTitle("Some title text you want"),

解决了部分问题

浏览器正确显示我的标题,但是当单击下载按钮时,文件名仍然是以前的 UUID。可能库中还有其他 api 可以让您修改下载文件名。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-05-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-03-23
    • 1970-01-01
    相关资源
    最近更新 更多