【问题标题】:Javascript - Open PDF blob in browser with a nice looking urlJavascript - 在浏览器中打开 PDF blob 并带有漂亮的 url
【发布时间】:2020-07-28 17:01:53
【问题描述】:

我正在使用 Node 从服务器获取 PDF 并将其发送到我的 React 前端。然后我想在浏览器中的新选项卡中显示该 PDF。它工作得相当好,除了带有 PDF 的新选项卡的 URL 不理想。新标签的 URL 看起来像:blob:http://localhost:3000/71659,但我希望它看起来像 http://localhost:3000/71659.pdf。没有“blob”,并且带有 pdf 扩展名,就像我在网络上单击 pdf 时一样,就像这里的示例一样:https://file-examples.com/index.php/sample-documents-download/sample-pdf-download/

我当前处理 blob 保存和打开的代码是这样的:

.then((res) => {
    console.log(res);
    const file = new Blob([res.data], {
        type: 'application/pdf'
    });
    //Build a URL from the file
    const fileURL = URL.createObjectURL(file);
    window.open(fileURL, '_blank');
});

这是我发送流的节点路由:

router.get('/openPDFFile', async (req, res) => {
    console.log('we got to the server!!  with: ', req.query);
    const pdfFilename = req.query.pdf;
    const pdfFilepath = `./path/to/pdf/${pdfFilename}`;
    router.get();
    const src = fs.createReadStream(pdfFilepath);

    res.writeHead(200, {
        'Content-Type': 'application/pdf',
        'Content-Disposition': 'inline; filename=sample.pdf'
    });

    src.pipe(res);
});

现在我想知道是否可以简单地从 Node.js 创建到该 PDF 的路由,而不是通过网络发送流并将其转换为 blob。类似于/PDF/${pdfFilename}。然后我的 React 会在新标签页中打开那个 URL?

更新 - 这是我基于 x00 回答的最新节点路由:

router.get('/openPDFFile', async (req, res) => {
    console.log('we got to the server!!  with: ', req.query);
    const pretty_PDF_name = req.query.pdf;
    const pdfFilename = (await SDS.getPDFFileName({ pretty_PDF_name }))
        .dataValues.sheet_file_name;
    console.log('pdfFilename: ', pdfFilename);

    const cleanPDFName =
        pretty_PDF_name
            .substring(0, pretty_PDF_name.length - 4)
            .replace(/[ ,.]/g, '') + '.pdf';

    const pdfFilepath = '\\path\\to\\file\\' + pdfFilename;
    const fullFilePath = path.join(__dirname + '/../' + pdfFilepath);
    console.log(cleanPDFName, fullFilePath);
    router.get('/pdf/' + cleanPDFName, async (req, res) => {
        res.sendFile(fullFilePath);
    });
    // router.get('/pdf/' + cleanPDFName, express.static(fullFilePath));
    // const src = fs.createReadStream(pdfFilepath);
    //
    // res.writeHead(200, {
    //  'Content-Type': 'application/pdf',
    //  'Content-Disposition': 'inline; filename=sample.pdf'
    // });
    //
    // src.pipe(res);
    // return res.json({ fileuploaded: cleanPDFName });
});

我也看到了 express.static 方式,并且也在尝试。

【问题讨论】:

  • 无法与反应部分对话,但我会在创建新资源(mime 类型“application/pdf”)时执行以下操作,该资源应该是 REST 可访问的:1. 将 fs 文件路径链接到一个身份证。 2. 将 ID 存储在数据库中,根据需要将 ID 与其他搜索/访问键关联 3. 从应用程序中,根据需要列出 IDS 4. 从应用程序通过重构解析为文件资源的 http URL 来获取特定 ID上面的 mime 类型
  • 我想我实际上已经在做大部分事情了!! :) 我将唯一 ID 与数据库中的文件名一起保存。在前端,用户单击文件名并调用传递该文件名的节点服务器。所以我特别在节点代码上苦苦挣扎。我以为我在某个地方读到了可以在 Express 中创建到文件的路由。 app.get ('/pdf/filename.pdf') 类似的东西...
  • 您真的需要blob 部分吗?为什么不只是window.open('/pdf/71659.pdf', '_blank'); 你想要某种预加载或错误处理之类的吗?那么有什么要求呢?
  • 看到这个答案:stackoverflow.com/a/14788331/560435 使用 req.ID 和一个只有 ID app.get('/openfile/:id' 的简单路由,你可以添加一个 fetch 到 DB ,解决路径,然后流式传输 pdf 或 http 302 以重定向到文件的完整 url
  • @x00 我不想要 blob 部分,这是我问题的全部基础。 /pdf/71659.pdf 不是有效路径,但我想让它成为有效路径。

标签: javascript node.js pdf


【解决方案1】:

据我从 cmets 了解到,您没有任何特殊要求(至少您在回答我的评论时没有提及任何特殊要求)。所以你可以这样做:

  1. 客户端
    window.open(`/pdf/${pdfFilepath}`, '_blank');
    // no need for
    //   fetch('/openPDFFile', ... pdf: pdfFilepath ... })
    //     .then(res => ... Blob ... )
    // or whatever you where doing
    
  2. 服务器
    router.get('/pdf/:pdfFilename', async (req, res) => {
      ...
      res.sendFile(__dirname + `/path/to/pdf/${req.params.pdfFilename}`)
    })
    

因此,您将获得http://localhost:3000/pdf/71659.pdf 形式的网址。您也可以获得没有/pdf/ 部分的网址,但我看不出有任何原因。

更新

关于冒号:请参阅此处的“路由参数”部分https://expressjs.com/en/guide/routing.html

完整的工作示例:

<!DOCTYPE html>
<html lang="en">
  <head></head>
  <body>
    <div id="get_pdf">Get PDF</div>
  </body>
  <script>
    // here can be your business logic
    // for example the name of pdf can be entered by the user through <input>
    const pdfFile = "1"

    // click will open new window with url = `http://localhost:3000/pdf/1.pdf`
    document
      .getElementById("get_pdf")
      .addEventListener("click", () => {
        window.open(`http://localhost:3000/pdf/${pdfFile}.pdf`, '_blank')
          // if you want the ".pdf" extension on the url - you must add it yourself
      })
  </script>
</html>
const express = require('express')
const app     = express()
app.get('/pdf/:pdf', async (req, res) => {
  const requested_pdf = req.params.pdf // === "1.pdf"
  console.log(requested_pdf)

  // here can be your business logic for mapping
  // requested_pdf from request to filepath of pdf
  // or maybe even to generated pdf with no file underneath
  // but I'll simply map to some static path
  const map_to_pdf_path = name => __dirname + `/path/to/pdf/${name}`

  res.sendFile(map_to_pdf_path(requested_pdf))
})
const listener = app.listen(process.env.PORT || constants.server_port, err => {
  if (err) return console.error(err)
  console.log(`Find the server at: http://localhost:${listener.address().port}`)
})

【讨论】:

  • 这看起来很有希望。我会测试一下并回复你。
  • 努力让它工作,我试图完全摆脱前端。所以我有我的节点路线,我只是用 Postman 打,并传入 pdf 文件名的单个参数。我不明白的一件事是那个冒号在router.get 调用中做了什么?
  • @dmikester1 你为什么挣扎?我的代码中缺少什么?照原样使用它。用完整的工作示例更新了答案。您只需要使用SDS 和人员添加您的特定业务逻辑即可。
【解决方案2】:

如果您出于in this older solution 所示的目的劫持一些 DOM,您可以获得一个漂亮的文件名,但您会在不同的浏览器中遇到许多问题。 FileSaver.js project 可能是您想要实现的目标获得近乎普遍支持的最佳选择。它以跨浏览器的方式处理带有名称的 blob 下载,如果您需要 IE

【讨论】:

    【解决方案3】:

    这是一个简单的方法......我绝不是想说这是一个好方法;但是。

    您可以在加载后更改 URL 的名称:

     window.history.pushState("","","/71659.pdf");
    

    假设您可以通过访问该 url 来加载 pdf,这就是您所要做的。 (您不希望共享该 url 的人共享损坏的 url)否则,您需要创建一个新路由来接受您想要的 url。

    如果你愿意,你可以添加一些错误检查来查看加载的 url 是否是你想要更改的:

    window.location.href
    

    【讨论】:

      猜你喜欢
      • 2016-06-09
      • 1970-01-01
      • 2011-10-22
      • 1970-01-01
      • 2011-06-12
      • 1970-01-01
      • 1970-01-01
      • 2014-09-24
      • 2013-07-09
      相关资源
      最近更新 更多