如何从浏览器FETCH触发浏览器下载



我正在进行一个项目,该项目使用Vue.jsTypescript作为前端,使用Java Spring为后端。

我的java控制器从数据库中检索给定的报告,然后将其复制到HTML响应中。我希望浏览器下载CSV,因此我在响应中添加了内容处置标题。

@GetMapping('/download')
public void downloadCSV(HttpServletRequest response){
Report r = reportService.findById(85);
response.addHeader("Content-Disposition", "attachment; filename=myCSV.csv");
response.setContentType("text/csv");
try {
InputStream stream = new ByteArrayInputStream(r.getDocument());
IOUtils.copy(stream, response.getOutputStream());
response.flushBuffer();
} catch(Exception e) {...}
}

我有两个按钮:一个简单的超链接标签,href链接到download((,还有一个b按钮(来自bootstrap vue(,一旦cliccked就会触发download2((

<a :href="download" role="button"> Download CSV </a>
<b-button @click="event => download2()">
Download CSV v2
</b-button>
get download(): string {
return 'http://localhost:8080/download';
}

async download2() {
const rHeaders = new Headers();
rHeaders.append('Accept', 'text/csv');
const configInit = RequestInit = {
method: 'GET',
headers: rHeaders
};
try {
const res = await fetch('http://localhost:8080/download', configInit);
return res.text();
} catch (e) {...}
}

现在,如果我点击第一个按钮"下载csv",浏览器就会正确下载csv。javascript控制台打印以下内容:

Resource interpreted as Document but transferred with MIME type text/csv

并且在响应体中什么都没有。

相反,如果我点击第二个按钮"下载csv v2",下载不会开始,但我在响应体中有csv。

这里,是两者在请求头中的差异。

*Header*                   *Download csv*        *Download csv v2*
Sec-Fetch-Dest                document               empty
Sec-Fetch-Mode                navigate               cors
Sec-Fetch-User                   ?1                    -
Upgrade-Insecure-Requests        1                     -

其他标头是相同的。即使我在javascript方法中设置了这些头,也不可能更改这些头;它们保持不变。怎么了?非常感谢。

我通过"模拟"<a>元素的行为找到了一个解决方案:

这样,它就可以正常工作:

async download2() {
const configInit: RequestInit = {
method: 'GET'
};
try {
await fetch('http://localhost:8080/download', configInit)
.then(response => response.blob())
.then(blob => {
const url = window.URL.createObjectURL(blob);
const a = document.createElement('a');
a.style.display = 'none';
a.href = url;
a.download = 'report.csv';
document.body.appendChild(a);
a.click();
window.URL.revokeObjectURL(url);
})
} catch (e) {...}

最新更新