简体   繁体   English

使用Express从Node.js向客户端发送文件

[英]Sending a file to the client from Node.js with Express

I have a unique situation in terms of difficulty. 就困难而言,我有一个独特的处境。

I need to send HTML to the server, have the server convert the HTML to a PDF, send that PDF back to the client, and then download the PDF using client-side code. 我需要将HTML发送到服务器,让服务器将HTML转换为PDF,再将PDF发送回客户端,然后使用客户端代码下载PDF

I have to do it this way because I'm using client-side routing, so the only way I can access my endpoint that should perform this action is via a GET Request with Ajax or Fetch from client-side JavaScript. 我必须这样做,因为我使用的是客户端路由,因此,我可以执行应该执行此操作的端点的唯一方法是通过带有Ajax的GET Request或从客户端JavaScript进行获取。 I am aware of res.sendFile() , but that attempts to render the file in the browser - I don't want that - rather, I want to be able to use client-side code to download the file. 我知道res.sendFile() ,但是它试图在浏览器中呈现文件-我不想要-而是希望能够使用客户端代码下载文件。

Is it possible, then, to send a PDF file from temporary storage on the server down to the client, allowing client-side code to do whatever it wants to the file thereafter - in my case, downloading it? 那么,是否可以将服务器上的临时存储中的PDF文件发送到客户端,从而允许客户端代码对文件执行任何操作(对于我而言,是下载文件)?

I don't believe I have to provide any code because this is more of a theoretical question. 我认为我不必提供任何代码,因为这更多是一个理论问题。

My issue stemmed from the fact that I could not just use res.sendFile() or res.download() from Express because the route was not being accessed by the browser URL bar, rather, my application uses client-side routing, and thus I had to make an HTTP GET Request via Fetch or XMLHttpRequest. 我的问题源于以下事实:我不能仅使用Express的res.sendFile()res.download() ,因为浏览器URL栏未访问该路由,而是我的应用程序使用了客户端路由,因此我必须通过Fetch或XMLHttpRequest发出HTTP GET请求。

The second issue is that I needed to build the PDF file on the server based on an HTML string sent from the client - so again, I need to make a GET Request sending along a request body. 第二个问题是我需要基于从客户端发送的HTML字符串在服务器上构建PDF文件-因此,我再次需要沿着请求正文发送GET请求。

My solution, then, using Fetch, was to make the Get Request from the client: 然后,我的解决方案使用Fetch,是从客户端发出Get Request:

fetch('/route' , {
    method: 'GET',
    body: 'My HTML String'
});

On the server, I have my code that converts the HTML string to a PDF, using the HTML-PDF Node module, and then, I convert that file to a Base64 String, setting the MIME Type and appending data:application/pdf;base64, . 在服务器上,我有使用HTML-PDF节点模块将HTML字符串转换为PDF的代码,然后将文件转换为Base64字符串,设置MIME类型并附加data:application/pdf;base64,

app.get('/route', (req, res) => {
    // Use req.body to build and save PDF to temp storage (os.tempdir())
    // ...
    fs.readFile('./myPDF.pdf', (err, data) => {
        if (err) res.status(500).send(err);
        res.contentType('application/pdf')
           .send(`data:application/pdf;base64,${new Buffer.from(data).toString('base64')}`);
    });
});

Back on the client, I have my aforementioned Fetch Request, meaning I just need to tack on the promise to get the response: 回到客户端,我有我前面提到的Fetch Request,这意味着我只需要遵守承诺就可以得到响应:

fetch('/route', {
    method: 'POST',
    body: 'My HTML String' // Would define object and stringify.
})
.then(res => res.text())
.then(base64String => {
    // Now I just need to download the base64String as a PDF.
});

To make the download, I dynamically create an anchor tag, set its href attribute to the Base64 String in the response from the server, give it a title, and then programmatically click it: 为了进行下载,我动态创建了一个锚标记,在服务器的响应中将其href属性设置为Base64 String,为其指定标题,然后以编程方式单击它:

const anchorTag = document.createElement('a');
anchorTag.href = base64String;
anchorTag.download = "My PDF File.pdf"; 
anchorTag.click();

So, all together and on the client: 因此,所有这些以及在客户端上:

fetch('/route', {
    method: 'POST',
    body: 'My HTML String' // Would define object and stringify.
})
.then(res => res.text())
.then(base64String => {
    const anchorTag = document.createElement('a');
    anchorTag.href = base64String;
    anchorTag.download = "My PDF File.pdf"; 
    anchorTag.click();
});

The solution for using an anchor tag to trigger the download came from another StackOverflow answer. 使用定位标记触发下载的解决方案来自另一个StackOverflow答案。 It's also important to note that Base64 Encoding is not very efficient. 还需要注意的是,Base64编码不是非常有效。 Better solutions exist, but for my purposes, Base64 will work fine. 存在更好的解决方案,但是出于我的目的,Base64可以正常工作。

It is also imperative to note that Base64 Encoding is precisely that - an Encoding Scheme, not , I repeat, not an Encryption Scheme. 还必须注意的是,Base64编码恰恰是-一种编码方案, 不是我要重复的, 不是一种加密方案。 So if your PDF files contain privileged information, you would likely want to add token authentication to the endpoint and encrypt the file. 因此,如果您的PDF文件包含特权信息,则您可能希望向端点添加令牌认证并加密文件。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM