使用 express js 在浏览器中显示 Pdf

Posted

技术标签:

【中文标题】使用 express js 在浏览器中显示 Pdf【英文标题】:Display Pdf in browser using express js 【发布时间】:2012-07-20 20:20:05 【问题描述】:

我正在尝试通过 Express 以在浏览器中显示的方式提供 PDF:

app.post('/asset', function(request, response)
  var tempFile="/home/applmgr/Desktop/123456.pdf";
  fs.readFile(tempFile, function (err,data)
     response.contentType("application/pdf");
     response.send(data);
  );
);

但是,浏览器显示二进制内容。如何正确处理?

【问题讨论】:

试试response.end(data); express 的send 方法可能正在做一些事后的猜测。 我添加了一个关于如何正确设置浏览器对您提供给它的文件的反应方式的答案。您可以在标题中设置这些内容,如果您有任何问题,请告诉我。 【参考方案1】:

指定如何处理文件下载都归结为Content-disposition 标头。您也可以在此处指定文件的名称。我们还设置了Content-type 以确保浏览器知道如何处理给它的文件。

Express.js 示例:

app.post('/url/to/hit', function(req, res, next) 
  var stream = fs.readStream('/location/of/pdf');
  var filename = "WhateverFilenameYouWant.pdf"; 
  // Be careful of special characters

  filename = encodeURIComponent(filename);
  // Ideally this should strip them

  res.setHeader('Content-disposition', 'inline; filename="' + filename + '"');
  res.setHeader('Content-type', 'application/pdf');

  stream.pipe(res);
);

现在,如果您更仔细地查看Content-disposition,您会注意到inline; 字段设置了浏览器对文件的反应方式。如果要强制下载,可以通过将inline; 设置为attachment; 来实现

我还发现(被烧了几次),如果您在文件名中设置特殊字符,它可能会损坏。所以我encodeURIComponent() 文件名以确保不会发生这种情况。

希望能帮助其他人尝试解决同样的问题!

编辑

从我最初发布到现在,我发现了如何正确编码content-disposition 的文件名参数。 According to the spec, the filename should be RFC5987 encoded. 我最终从 MDN 找到了一个 example code snippet,它正确处理了此处的编码(encodeURIComponent() 不是该字段的完全正确格式)。

MDN 片段

var fileName = 'my file(2).txt';
var header = "Content-Disposition: attachment; filename*=UTF-8''" 
             + encodeRFC5987ValueChars(fileName);

console.log(header); 
// logs "Content-Disposition: attachment; filename*=UTF-8''my%20file%282%29.txt"

function encodeRFC5987ValueChars (str) 
    return encodeURIComponent(str).
        // Note that although RFC3986 reserves "!", RFC5987 does not,
        // so we do not need to escape it
        replace(/['()]/g, escape). // i.e., %27 %28 %29
        replace(/\*/g, '%2A').
            // The following are not required for percent-encoding per RFC5987, 
            // so we can allow for a little better readability over the wire: |`^
            replace(/%(?:7C|60|5E)/g, unescape);

除此之外的另一个注意事项,浏览器也不完全符合规范。有些字符仍然会从下载中错误地返回(至少在我测试时)。

您可以通过更新下载的工作方式来解决这个问题。如果您的下载 URL 以文件名结尾(并且您没有在标头中提供 filename 属性),它将正确地从 URL 编码值中获取文件名。即'http://subdomain.domain-url.com/some/path/to/downloads/' + encodeURIComponent("You're there, download this!.pdf")

Jeeze,为您的下载提供文件名!

【讨论】:

只是指出一个错字,它应该是内容处置标题中的“附件”,而不是“附件”。【参考方案2】:

我测试了您的代码,它在 chrome 中适用于我,只需进行一项更改: 将app.post 更改为app.get

编辑:由于您似乎认为仅 POST 服务器是个好主意,请阅读:http://net.tutsplus.com/tutorials/other/a-beginners-introduction-to-http-and-rest/ 向下滚动直到 HTTP 动词并检查 GET 和 POST 之间的区别。 :)

一些快速研究表明其他浏览器可能存在其他问题,例如 IE 可能期望 URL 以 .pdf 结尾。因为我在我的 Mac 上,所以我无法为你测试 ;)

【讨论】:

是的,在从 post 更改以使其在我的 firefox、safari、opera 和 chrome 中工作后 所以 app.post 不可能?因为我将服务器和维护配置为只接受发布请求。 hmm... 不知道为什么要将服务器配置为仅执行 POST 请求:浏览器获取您发送到的任何 URL。这就是http的工作方式。 :) POST 通常用于表单提交,或者当您使用 HTTP 创建某些东西时,而不是 GET 它。 公平地说,在某些用例中,您希望获取 PDF 以响应 POST。 我对 Node.js 还是很陌生。是什么让这种提供 pdf 的方法与我一直使用的 sendfile(some.html) 如此不同。有人可以解释这是如何工作的以及为什么我不必从文件系统中读取 html 文件吗?【参考方案3】:

我将 PDF 直接发送到浏览器的解决方案:

app.get('/my/pdf', function (req, res) 
    var doc = new Pdf();
    doc.text("Hello World", 50, 50);

    doc.output( function(pdf) 
        res.type('application/pdf');
        res.end(pdf, 'binary');
    );
);

res.end() 与第二个参数“二进制”在我的案例中起到了作用。否则表示将其解释为字符串

【讨论】:

方法doc.output 现已弃用。 谢谢! 'binary' 参数真的拯救了我的一天!【参考方案4】:

其实 Express 已经有this feature 用于发送文件。您只需要:

app.get('/sendMePDF', function(req, res) 
  res.sendFile(__dirname + "/static/pdf/Rabbi.pdf");
)

这里服务器将发送文件“Rabbi.pdf”,它会在浏览器中打开,就像您在浏览器中打开 pdf 一样。我将文件放在“静态”文件夹中,但您可以将它放在任何地方,知道 sendFile() 将绝对路径(不是相对路径)作为参数。

【讨论】:

这会在同一窗口中打开 pdf 文件。有没有办法在新窗口中打开它? 在某些浏览器中它只会要求您下载它【参考方案5】:

就这么简单,如下代码:

var express = require('express'),
    fs = require('fs'),
    app = express();

app.get('/', function (req, res) 
    var filePath = "/files/my_pdf_file.pdf";

    fs.readFile(__dirname + filePath , function (err,data)
        res.contentType("application/pdf");
        res.send(data);
    );
);

app.listen(3000, function()
    console.log('Listening on 3000');
);

完整的回购

克隆节点作弊pdf_browser,运行node app,然后运行npm install express

乐于助人!

【讨论】:

【参考方案6】:

根据 Express js 文档,您可以在一个函数中设置 Content Type 和 Content Disposition,如下所示

 fs.readFile(filePath, (err, data) => 
res.set(
  "Content-Type": "application/pdf", //here you set the content type to pdf
  "Content-Disposition": "inline; filename=" + fileName, //if you change from inline to attachment if forces the file to download but inline displays the file on the browser
);
res.send(data); // here we send the pdf file to the browser
);

【讨论】:

以上是关于使用 express js 在浏览器中显示 Pdf的主要内容,如果未能解决你的问题,请参考以下文章

pdf.js在IIS中配置使用笔记

在 Express.js 项目中使用 Socket.io

前端使用pdf.js预览pdf文件

如何使用反应返回文件?或 express 的 `npm unload` 类似物

pdf.js使用小结

pdf.js在国际化的时候,显示不了中文的解决办法