将Excel文件从后端发送到前端并在前端下载

人气:440 发布:2022-10-16 标签: node.js rest http-post express exceljs

问题描述

我已经使用Exceljs npm模块在后端(Express JS)创建了一个Excel文件.我将其存储在临时目录中.现在,我想将文件从后端发送到前端,并在用户单击按钮时将其下载到前端.我在两件事上感到震惊 1.如何通过HTTP POST请求将文件从后端发送到前端 2.然后如何在前端

I had created an Excel file at the backend (Express JS) using Exceljs npm module. I have it stored in a temp directory. Now I would want to send the file from the back-end to the front-end and download it there when the user clicks a button. I am struck on two things 1. How to send the file from the backend to the frontend through an HTTP POST request 2. How to then download the file in the front-end

编辑内容:

我需要将前端作为一个按钮,将文件附加到该按钮,然后再下载.这就是我的代码的样子,我没有从后端到前端正确获取文件

I need the front end to be a button that appends the file to it and then download it. This is how my code looks, I am not getting the file properly from the backend to the front-end

前端文件:

 function(parm1,parm2,parm3){
 let url =${path}?parmA=${parm1}&parmB=${parm2}&parmC=${parm3};
 let serviceDetails = {};
 serviceDetails["method"] = "GET";
 serviceDetails["mode"] = "cors";
 serviceDetails["headers"] = {
 "Content-Type": "application/json"
 };

fetch(url, serviceDetails)
  .then(res => {
    if (res.status != 200) {
      return false;
    }
    var file = new Blob([res], { type : 'application/octet-stream' });
    a = document.createElement('a'), file;
    a.href = window.URL.createObjectURL(file);
    a.target = "_blank"; 
    a.download = "excel.xlsx";
    document.body.appendChild(a);
    a.click(); 
    document.body.removeChild(a); 

  }).catch(error => {
     return false;
  });

}`

router.js

var abc = ... // this is a object for the controller.js file
router.get('/path', function(req, res) {
abc.exportintoExcel(req, res);
});

controller.js

let xyz = ... //this is a object for the service.js file
 exports.exportintoExcel = function(req, res) {
 xyz.exportintoExcel(reqParam,res);
 }

service.js

exportintoExcel(req,response){
       //I have a excel file in my server root directory
        const filepath = path.join(__dirname,'../../nav.txt');
        response.sendFile(filepath);
    })
}

推荐答案

好,现在我看到了您的代码,我可以尝试一些帮助.我对您的示例进行了一些重构,以使我更容易理解,但可以随时根据您的需求进行调整.

OK, now that I see your code, I can try and help out a little. I have refactored your example a little bit to make it easier for me to understand, but feel free to adjust to your needs.

index.html

我不知道您正在使用的页面是什么,但是在您的示例中,您看起来像是在fetch()调用期间使用JavaScript创建锚元素.我只是在实际页面中用HTML创建一个,是否有原因您不能执行此操作?

I don't know what the page looks like that you're working with, but it looks like in your example you are creating an anchor element with JavaScript during the fetch() call. I'm just creating one with HTML in the actual page, is there a reason you can't do this?

<body>
  <a id="downloadLink" download="excel.xlsx" href="#">Download Excel File</a>
  <script type="text/javascript" src="/javascripts/test.js"></script>
</body

有了这个,这是我的前端JS文件的版本:

With that in hand, here is my version of your front end JS file:

test.js

const downloadLink = document.getElementById('downloadLink');

sendFetch('a', 'b', 'c');

function sendFetch(param1, param2, param3) {

  const path = 'http://localhost:3000/excelTest';
  const url = `${path}?parmA=${param1}&parmB=${param2}&parmC=${param3}`;
  const serviceDetails = {};
  serviceDetails.method = "GET";
  serviceDetails.mode = "cors";
  serviceDetails.headers = {
    "Content-Type": "application/json"
  };

  fetch(url, serviceDetails).then((res) => {
    if (res.status != 200) {
      return false;
    }
    res.blob().then((excelBlob) => {
      const excelBlobURL = URL.createObjectURL(excelBlob);
      downloadLink.href = excelBlobURL;
    });
  }).catch((error) => {
     return false;
  });
}

我不得不填写一些细节,因为我无法分辨代码中发生了什么.这是我更改的内容:

I had to fill in some details because I can't tell what is going on from your code. Here are the things I changed:

选择了DOM元素而不是创建它:

您的版本:

a = document.createElement('a'), file;

我的版本:

index.html

<a id="downloadLink" download="excel.xlsx" href="#">Download Excel File</a>

test.js

const downloadLink = document.getElementById('downloadLink');

这为我们省去了创建元素的麻烦.除非您出于某种原因需要这样做,否则我不会.我也不确定file在您的原始作品中的作用.

This saves us the trouble of creating the element. Unless you need to do that for some reason, I wouldn't. I'm also not sure what that file is doing in your original.

命名函数并更改参数->参数列表的参数

您的版本:

function(parm1,parm2,parm3){

我的版本:

function sendFetch(param1, param2, param3) {

我不确定您实际上如何调用函数,所以我将其命名.另外,parm还不清楚.参数也不是很好,应该描述它是什么,但是我从您的代码中不知道.

I wasn't sure how you were actually calling your function, so I named it. Also, parm isn't clear. Param isn't great either, should describe what it is, but I don't know from your code.

创建一个path变量并将url赋值放在反引号中 Create a path variable and enclose url assignment in backticks

您的版本:

let url =${path}?parmA=${parm1}&parmB=${parm2}&parmC=${parm3};

我的版本:

const path = 'http://localhost:3000/excelTest';
const url = `${path}?parmA=${param1}&parmB=${param2}&parmC=${param3}`;

在您的版本中,该url分配应该引发错误.看起来您想使用字符串插值,但是您需要使用反引号,这是我添加的.另外,我必须定义一个path变量,因为在您的代码中没有看到一个变量.

In your version, that url assignment should throw an error. It looks like you want to use string interpolation, but you need backticks for that, which I added. Also, I had to define a path variable, because I didn't see one in your code.

清理了一些格式

我在serviceDetails中使用了点"符号,但这只是个人喜好.我还更改了fetch()调用的间距,但无需在此处重新打印.不应该有任何影响.

I used 'dot' notation for the serviceDetails, but that was just personal preference. I also changed the spacing of the fetch() call, but no need to reprint that here. Shouldn't effect anything.

从获取响应中创建一个斑点

您的版本:

var file = new Blob([res], { type : 'application/octet-stream' });

我的版本:

res.blob().then((excelBlob) => {

我不确定为什么要调用Blob构造函数以及[res]应该是什么.从fetch()返回的Response对象具有一个blob()方法,该方法返回一个承诺,该承诺将解析为带有数据所在的MIME类型的Blob.在Excel文档中,这是application/vnd.openxmlformats-officedocument.spreadsheetml.sheet./p>

I'm not sure why you are calling the Blob constructor and what that [res] is supposed to be. The Response object returned from fetch() has a blob() method that returns a promise that resolves to a Blob with whatever MIME-type the data was in. In an Excel documents case, this is application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.

Blob创建一个ObjectURL,并将此URL添加到定位标记的href. Create an ObjectURL from the Blob and add this URL to the href of the anchor tag.

您的版本:

a = document.createElement('a'), file;
a.href = window.URL.createObjectURL(file);
a.target = "_blank"; 
a.download = "excel.xlsx";
document.body.appendChild(a);
a.click(); 
document.body.removeChild(a);

我的版本:

const excelBlobURL = URL.createObjectURL(excelBlob);
downloadLink.href = excelBlobURL;

您必须进行大量的DOM操作,但我不确定为什么需要这样做.如果确实必须动态创建此元素,那么我不确定为什么要单击"该元素,然后删除它(如果用户应该能够单击它的话).也许可以为我澄清您为什么这样做,或者是否确实需要这样做.无论哪种方式,在我的版本中,我都创建ObjectURL然后分配它,但是您也可以很容易地不将其存储在变量中.

You have to do a bunch of DOM manipulation, which I'm not sure why you need. If you do have to dynamically create this element, then I'm not sure why you are 'clicking' it, then removing it, if the user is supposed to be able to click it. Maybe clarify for me why you are doing this, or if you really need to do it. Either way, in my version I create the ObjectURL and then assign it, but you could just as easily not store it in a variable.

调用发送获取请求的函数.

因为我的功能签名是:

function sendFetch(param1, param2, param3)

我需要在某个地方调用它以触发请求,所以我这样做是这样的:

I needed to call it somewhere in order to fire off the request, so I did so like this:

sendFetch('a', 'b', 'c'); 

页面加载后的正确时间,如您从服务器日志中看到的那样:

Right when the page loads, as you can see from the server logs:

GET / 304 0.448 ms - -
GET /javascripts/test.js 304 1.281 ms - -
GET /excelTest?parmA=a&parmB=b&parmC=c 304 0.783 ms - -

前两个请求是针对index.html页面和test.js文件的,然后使用我传入的参数触发提取请求.我不确定您如何在应用程序中执行此操作,因为不包含在您的代码中.

The first two requests are for the index.html page and the test.js file, then the fetch request is fired with the param's I passed in. I'm not sure how you are doing this in your app, because that is not included in your code.

我刚才介绍的所有内容都是前端.我假设您的服务器端代码实际上是通过调用service.js中的response.sendFile()来发送一个excel文件.如果您确定文件正在发送,那么根据应用程序调整后,我给您的代码应该可以使用.

Everything I just covered is Front-End. I'm assuming your server-side code is actually sending an excel file with your call to response.sendFile() in service.js. If you are sure that the file is getting sent, then the code I've given you should work, when adjusted to your app.

因此,总而言之,这段代码的作用是:

So, in conclusion, what this code does is:

加载未设置href属性的带有定位标记的HTML页面. 向服务器发送fetch()请求. 将获取响应转换为Blob,然后从该Blob创建ObjectURL. 将该ObjectURL分配给锚标记的href属性. Load an HTML page with an anchor tag with no href attribute set. Send off a fetch() request to the server. Turn the fetch response into a Blob, then create an ObjectURL from this Blob. Assign that ObjectURL to the anchor tag's href attribute.

当用户单击下载Excel文件"链接时,应下载Excel工作表.如果您不希望他们在fetch请求之后才看到链接,则可以肯定地在JS中创建锚标记,如果您想了解如何做,请告诉我.

When the user clicks the 'Download Excel File' link, the Excel sheet should be downloaded. If you didn't want them to see the link until after the fetch request, you could definitely do create the anchor tag in JS instead, let me know if you want to see how to do that.

像以前一样,这是一张gif,显示它在我的计算机上的外观(这与您的版本和我的修改有关):

As before, here is a gif showing how it looks on my machine (this is with your version and my modifications):

787