NodeJs/Express 编写 xlsx 模板并将其发送回浏览器下载

Posted

技术标签:

【中文标题】NodeJs/Express 编写 xlsx 模板并将其发送回浏览器下载【英文标题】:NodeJs/Express writing xlsx-template and sending it back for browser download 【发布时间】:2019-09-26 14:10:30 【问题描述】:

我有这个 excel,我想将标量值替换为 xlsx-template 文档所说的:

| Extracted on: | $extractDate | 我想通过响应对象发送它。该文件在输出路径中正确写入,但在客户端它给了我一个二进制输出。 Bellow 是对 Url 的后端调用

this.downloadSheet = async (req, res) => 

    let seriesNumber = Number(req.body.seriesNumber);
    let sheetPath = path.resolve('../filePath/excel.xlsx');
    let sheetFinalOuput = path.resolve('../filePath/excelOutput.xlsx');
    #Get data needed to swap for the excel
    let excelDataStream = await ReportService.getBalanceSheetAllInOne(balanceSheetPath, seriesNumber);
    fs.readFile(path.resolve(balanceSheetPath), function(err, data) 
      // Create a template
      let template = new XlsxTemplate(data);

      // Replacements take place on first sheet
      const sheetNumber = 1;

      // Perform substitution
      template.substitute(sheetNumber, excelDataStream);

      // Get binary data
      var mydata = template.generate( type: 'nodebuffer');

      fs.writeFile(path.resolve(balanceSheetFinalOuput), mydata, function(err) 
        if(err) 
          return console.log(err);
        
        console.log(`Wrote data in file, check please!`);

        // Send File & set headers
        res.set('Content-Disposition': `attachment; filename=balanceIncome$seriesNumber.csv`, 'Content-Type': 'text/csv');
        res.write(mydata);
        res.end();
      );
    );
  ;

客户端函数http post请求

$http.post(`$settings.apiHost/api/panel/report/balanceSheet/allInOne/false`,seriesNumber: $scope.seriesNumber)
        .success(function (response) 
          var file = new Blob([response], type: 'text/csv');

          var isGoogleChrome = window.chrome != null && window.navigator.vendor === "Google Inc.";
          var isIE = /*@cc_on!@*/false || !!document.documentMode;
          var isEdge = !isIE && !!window.StyleMedia;


          if (isGoogleChrome)
            var url = window.URL || window.webkitURL;

            var downloadLink = angular.element('<a></a>');
            downloadLink.attr('href',url.createObjectURL(file));
            downloadLink.attr('target','_self');
            downloadLink.attr('download', `balanceSheet_$$scope.seriesNumber.csv`);
            downloadLink[0].click();
          
          else if(isEdge || isIE)
            window.navigator.msSaveOrOpenBlob(file,`balanceSheet_$$scope.seriesNumber.csv`);

          
          else 
            var fileURL = URL.createObjectURL(file);
            window.open(fileURL);
          
        )

【问题讨论】:

res.write? res.send 是你需要的expressjs.com/en/api.html#res.send 我试过了,我也试过 res.sendFile(path.resolve('dataPath.xlsx'));下载文件乱了 我会将此 api 更改为 GET 方法并使用 window.open 下载它,而不是 blob 链接。如果必须这样做,请尝试***.com/a/14737423/11303576 解决响应类型问题。 我忘记 res.send(buffer) 不需要将 header 作为文件发送,删除它以防止 $http.post 对其进行解码,然后尝试将 nodebuffer 转换为 arraybuffer,并通过 arraybuffer 构建 blob 你介意发布一个更具体的例子吗?或者多解释一下一般流程? 【参考方案1】:

客户端

const url = `$settings.apiHost/api/panel/report/balanceSheet/allInOne/false/$$scope.seriesNumber`;
window.open(url);

服务器端(将路由器参数和方法更改为 GET)

let seriesNumber = Number(req.params.seriesNumber);
... 
fs.writeFile(path.resolve(balanceSheetFinalOuput), mydata, function(err) 
  if(err) 
    return console.log(err);
  
  console.log(`Wrote data in file, check please!`);

  // Send File & set headers
  res.set('Content-Disposition': `attachment; filename=balanceIncome$seriesNumber.csv`, 'Content-Type': 'text/csv');
  res.send(mydata);
);

另一种方式

客户端

...
.success(function (nodeBuffer) 
  let arraybuffer = Uint8Array.from(nodeBuffer).buffer;
  var file = new Blob(arraybuffer, type: 'text/csv');
  ....

服务器端

...
fs.writeFile(path.resolve(balanceSheetFinalOuput), mydata, function(err) 
  if(err) 
    return console.log(err);
  
  console.log(`Wrote data in file, check please!`);
  res.send(mydata);
);
...

【讨论】:

第一个解决方案就像一个魅力!出于某种原因,我在 excel 中有不同的二进制输出。无论哪种方式,感谢您抽出时间并回答。我需要 url 端点是安全的,用于内部身份验证。我可以发送带有窗口对象的 http 标头吗?我认为在这种情况下,解决方案 2 效果最好。我会努力让它发挥作用。谢谢 不,window.open 方法不接受自定义标题。解决方案 2 点是 @feecks 您从服务器输出的内容以及您在浏览器中获得的内容,浏览器是否可以读取该缓冲区?如果没有,如何让它读取,转换成什么?如何将缓冲区转换为blob?这可能需要一些时间来尝试。希望这些提示对您有所帮助。 感谢您的帮助!

以上是关于NodeJs/Express 编写 xlsx 模板并将其发送回浏览器下载的主要内容,如果未能解决你的问题,请参考以下文章

nodejs+Express中使用mustache模板引擎

nodejs+express+ejs生成项目

Nodejs / Express:错误:无法在视图目录中查找视图“错误”

nodejs+express使用ejsexcel做复杂导出Excel

nodejs+express+MongoDB实现todolist

Nodejs,Express + Angular POST 404