好的,现在我看到了你的代码,我可以尝试一点帮助。我稍微重构了你的示例,以便更容易理解,但请随意根据自己的需要进行调整。
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;
});
}
我需要填写一些细节,因为从你的代码中无法判断正在发生什么。这是我所做的更改:
- 选择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
是干什么用的。
2. 给函数命名并将“parm”更改为“param”以表示参数列表。
function(parm1,parm2,parm3){
我的版本:
function sendFetch(param1, param2, param3) {
我不确定你如何实际调用函数,所以我给它命名了。此外,parm并不清晰。Param也不太好,应该描述它是什么,但我从你的代码中无法知道。
- 创建一个
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()调用的间距,但这里不需要重印。不会影响任何东西。
创建一个blob从fetch响应中。
var file = new Blob([res], { type : 'application/octet-stream' });
我的版本:
res.blob().then((excelBlob) => {
我不确定为什么要调用
Blob
构造函数以及
[res]
应该是什么。从
fetch()
返回的
Response
对象具有一个
blob()
方法,该方法返回一个 Promise,该 Promise 解析为一个
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,然后将其分配,但你也可以很容易地不将其存储在变量中。
7. 调用发送fetch请求的函数。
因为我的函数签名是:
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文件的,然后使用我传递的参数触发了fetch请求。我不确定你在应用程序中如何实现这一点,因为它没有包含在你的代码中。
我刚刚讲解的所有内容都是前端相关的。我假设你的服务器端代码实际上是通过调用service.js中的
response.sendFile()
发送excel文件的。如果你确定文件已经被发送,那么我给你的代码应该可以工作,只需要根据你的应用进行调整。
因此,总之,这段代码的作用是:
- 加载一个带有未设置
href
属性的锚标签的HTML页面。
- 向服务器发送一个
fetch()
请求。
- 将fetch响应转换为
Blob
,然后从这个Blob
创建一个ObjectURL
。
- 将该
ObjectURL
分配给锚标签的href
属性。
当用户点击“下载Excel文件”链接时,应下载Excel表格。如果您不希望在
fetch
请求之前让他们看到该链接,您绝对可以使用JS创建锚标记,如果您想知道如何做,请告诉我。
与之前一样,这里是一个gif,展示了它在我的机器上的效果(这是使用您的版本和我的修改后的版本):
![enter image description here](https://istack.dev59.com/j7hxQ.gif)