SpringBoot實現文件下載以及前台對接方案
文件的下載返回:
這里用了一個ResponseEntity實體進行數據返回(當時就是不知道用什么對象返回文件,所以很惡心)。
請求頭使用Content-Disposition,fileName標記返回時的文件名稱;
ContentType使用octer-stream;
ContentLength...可以選擇不填,長度如果填錯了會報錯
Body,返回文件的InputStream流
fun downloadFile(@RequestBody fileId: Int): ResponseEntity<InputStreamResource> {
val dest = fileService.getDownloadFile(fileId)
return ResponseEntity.ok()
.header("Content-Disposition", "attachment;filename=" + dest.name)
.contentType(MediaType.parseMediaType("application/octet-stream"))
.contentLength(dest.length())
.body(InputStreamResource(dest.inputStream()))
}
當然,我還看到了一種字節流的下載方式,也挺有趣的,但是用了之后好像出了點問題,就沒用了(可行,但是數據不對)。
文件接收(百度用的一套代碼)
let filename = new Date().getTime().toString() + '.zip'
let blob = new Blob([await downloadFile(res.data.data).data], { type: 'application/force-download' }) // 接收的是blob,若接收的是文件流,需要轉化一下
if (typeof window.chrome !== 'undefined') {
// Chrome version
let link = document.createElement('a')
link.href = window.URL.createObjectURL(blob)
link.download = filename
link.click()
// console.log(link)
} else if (typeof window.navigator.msSaveBlob !== 'undefined') {
// IE version
let blob = new Blob([data], { type: 'application/force-download' })
window.navigator.msSaveBlob(blob, filename)
} else {
// Firefox version
let file = new File([data], filename, { type: 'application/force-download' })
window.open(URL.createObjectURL(file))
}
雖然做出來了,但是很多地方都不完美,我遇到了一堆非常惡心的事情:
因為這個是一個接口請求,只有請求完全結束(文件已經被下載下來之后),下載的文件才會突然顯示。
這並不符合我的預期,也是一個非常糟糕的交互。
結局思路:
- 創建文件鏈接
- 開放文件下載接口,靠token和filepath進行下載
Q&A
為什么要寫這篇文章
- 作為一條孤龍來說,所有的解決方案都是自己去一點點翻查的。然后,網上居然一點類似的文章都沒有!!
參考文章&視頻
- https://www.bilibili.com/video/BV1As411s77T?from=search&seid=7435414762924071490 - SpringBoot返回文件
- https://segmentfault.com/a/1190000015852421 - 前端對接下載文件
適用人群:一些和我一樣不喜歡走按部就班的學習路徑,選擇自學,且只看目錄的人們。。。
后來用了接口的形式實現了不跳轉界面的下載(瞬間跳轉一下,然后自動關上的那種,我有一個可以優化的思路,無需跳轉的,等下次完成了斷點下載功能的時候一起更新了)