我已经使用Exceljs npm模块在后端(Express JS)创建了一个Excel文件。我将其存储在临时目录中。现在,我想将文件从后端发送到前端,并在用户单击按钮时将其下载到前端。我在两件事上感到震惊 1.如何通过HTTP POST请求将文件从后端发送到前端 2.然后如何在前端
下载文件编辑内容:
我需要前端为一个按钮,它将文件附加到该按钮,然后下载。这就是我的代码的样子,我没有从后端到前端正确获取文件
前端文件:
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);
})
}
答案 0 :(得分:0)
这是对先前答案的完整重写,如果有人需要该答案,对不起,但是此版本更好。我正在使用一个由express-generator创建的项目,并在以下三个文件中工作:
index.ejs
从锚标签开始,该锚标签具有download
属性,所需的文件名和空的href
属性。稍后,我们将用表示URL的ObjectURL填充main.js文件中的href
:
<body>
<a id="downloadExcelLink" download="excelFile.xlsx" href="#">Download Excel File</a>
<script type="text/javascript" src="/javascripts/main.js"></script>
</body>
public / javascripts / main.js
选择锚元素,然后向路由fetch()
发出/downloadExcel
请求。将响应转换为Blob
,然后从该ObjectURL
创建一个Blob
。然后,您可以将锚标记的href
属性设置为此ObjectURL
:
const downloadExcelLink = document.getElementById('downloadExcelLink');
(async () => {
const downloadExcelResponse = await fetch('/downloadExcel');
const downloadExcelBlob = await downloadExcelResponse.blob();
const downloadExcelObjectURL = URL.createObjectURL(downloadExcelBlob);
downloadExcelLink.href = downloadExcelObjectURL;
})();
routes / index.js
在索引路由器中,您只需要调用res.sendFile()
函数并将其传递到服务器上Excel文件的路径即可。
router.get('/downloadExcel', (req, res, next) => {
const excelFilePath = path.join(__dirname, '../tmp/excel.xlsx');
res.sendFile(excelFilePath, (err) => {
if (err) console.log(err);
});
});
就是这样!您可以找到该项目的git repo here。如果无法按原样在您的项目中使用此代码,请对其进行克隆并尝试一下。
工作原理
页面加载后,将向服务器发出4个请求,如控制台输出所示:
GET / 200 2.293 ms - 302
GET /stylesheets/style.css 200 1.123 ms - 111
GET /javascripts/main.js 200 1.024 ms - 345
GET /downloadExcel 200 2.395 ms - 4679
前三个请求是对index.ejs(/),CSS样式表和我们的main.js文件的请求。第四个请求通过我们的调用发送到main.js文件中的fetch('/downloadExcel')
:
const downloadExcelResponse = await fetch('/downloadExcel');
我在该路由的route / index.js中有一个路由处理程序设置,该路由使用res.sendFile()
从我们的文件系统发送文件作为响应:
router.get('/downloadExcel', (req, res, next) => {
const excelFilePath = path.join(__dirname, '../tmp/excel.xlsx');
res.sendFile(excelFilePath, (err) => {
if (err) console.log(err);
});
});
excelFilePath
必须是您系统上文件的路径。在我的系统上,这是路由器文件和Excel文件的布局:
/
/routes/index.js
/tmp/excel.xlsx
从Express服务器发送的响应存储为downloadExcelResponse
,作为对main.js文件中对fetch()
的调用的返回值:
const downloadExcelResponse = await fetch('/downloadExcel');
downloadExcelResponse
是Response
对象,出于我们的目的,我们想使用Blob
方法将其变成Response.blob()
对象:
const downloadExcelBlob = await downloadExcelResponse.blob();
现在我们有了Blob
,我们可以调用URL.convertObjectURL()
将此Blob
转换成我们可以用作href
的下载链接:
const downloadExcelObjectURL = URL.createObjectURL(downloadExcelBlob);
这时,我们有了一个表示浏览器中Excel文件的URL,并且可以通过将href
指向该URL,方法是将其添加到我们先前选择的href
属性的DOM元素中:
页面加载后,我们在此行中选择了锚元素:
<a id="downloadExcelLink" download="excelFile.xlsx" href="#">Download Excel File</a>
因此,我们在发出href
请求的函数中,将URL添加到fetch
:
downloadExcelLink.href = downloadExcelObjectURL;
您可以在浏览器中检出元素,并在页面加载时看到href
属性已被更改:
注意,在我的计算机上,锚标记现在为:
<a id="downloadExcelLink" download="excelFile.xlsx" href="blob:http://localhost:3000/aa48374e-ebef-461a-96f5-d94dd6d2c383">Download Excel File</a>
由于链接上存在download
属性,因此,单击链接时,浏览器将下载href
指向的内容,在我们的示例中是{{1}的URL },代表Excel文档。
我从以下来源获得了我的信息:
以下是下载过程在我的计算机上的外观的gif图像:
答案 1 :(得分:0)
好的,现在我看到了您的代码,我可以尝试一些帮助。我对您的示例进行了一些重构,以使我更容易理解,但可以随时根据您的需求进行调整。
index.html
我不知道您正在使用的页面是什么样子,但是在您的示例中,您似乎在fetch()
调用期间使用JavaScript创建了锚元素。我只是在实际页面中使用HTML创建一个页面,有没有原因您不能这样做?
<body>
<a id="downloadLink" download="excel.xlsx" href="#">Download Excel File</a>
<script type="text/javascript" src="/javascripts/test.js"></script>
</body
有了这个,这是我的前端JS文件版本:
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;
});
}
我不得不填写一些细节,因为我无法分辨代码中发生了什么。这是我更改的内容:
您的版本:
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
在您的原始作品中的作用。
您的版本:
function(parm1,parm2,parm3){
我的版本:
function sendFetch(param1, param2, param3) {
我不确定您实际上如何调用函数,所以我将其命名。另外,parm还不清楚。参数也不是很好,应该描述它的含义,但是我不知道您的代码。
path
变量并将url
赋值放在反引号中您的版本:
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
变量,因为在您的代码中没有看到一个变量。
我在serviceDetails中使用了“点”符号,但这只是个人喜好。我还更改了fetch()调用的间距,但无需在此处重新打印。不应该有任何影响。
您的版本:
var file = new Blob([res], { type : 'application/octet-stream' });
我的版本:
res.blob().then((excelBlob) => {
我不确定您为什么要调用Blob
构造函数以及该[res]
应该是什么。从Response
返回的fetch()
对象具有一个blob()
方法,该方法返回一个承诺,该承诺可以解析为Blob
并具有数据所处的MIME类型。在Excel文档中,这是application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
。
Blob
创建一个ObjectURL并将此URL添加到定位标记的href
。您的版本:
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然后分配它,但是您也可以很容易地不将其存储在变量中。
因为我的功能签名是:
function sendFetch(param1, param2, param3)
我需要在某个地方调用它以触发请求,所以我这样做是这样的:
sendFetch('a', 'b', 'c');
页面加载后的正确时间,如您从服务器日志中看到的那样:
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文件的,然后使用我传入的参数触发提取请求。我不确定您如何在应用程序中执行此操作,因为不包含在您的代码中。
我刚才介绍的所有内容都是前端。我假设您的服务器端代码实际上是通过调用service.js中的response.sendFile()
发送一个excel文件。如果您确定文件正在发送,那么根据应用程序调整后,我给您的代码应该可以使用。
因此,总而言之,这段代码的作用是:
href
属性的锚标记的HTML页面。fetch()
请求。Blob
,然后从该ObjectURL
创建一个Blob
。ObjectURL
分配给锚标记的href
属性。当用户单击“下载Excel文件”链接时,应下载Excel工作表。如果您不希望他们在fetch
请求之后才看到链接,则可以肯定地在JS中创建锚标记,如果您想了解如何做,请告诉我。
像以前一样,这是一张gif,显示它在我的计算机上的外观(这与您的版本和我的修改有关):