前端下載有兩類拓提,一類是直接讓瀏覽器接管的(點(diǎn)擊a鏈接觸發(fā)),另一類是先在內(nèi)存里下載好(blob)框喳,然后調(diào)用瀏覽器的保存坎弯。
1.window.open
我最初使用的方法就是這個(gè)酒朵,只要提供了文件的服務(wù)器地址桦锄,使用window.open
也就是在新窗口打開,這時(shí)瀏覽器會(huì)自動(dòng)執(zhí)行下載蔫耽。
2.a標(biāo)簽
其實(shí)window.open和a標(biāo)簽是一樣的结耀,只是a標(biāo)簽是要用戶點(diǎn)擊觸發(fā),而window.open可以主動(dòng)觸發(fā)
后端如果設(shè)置了Content-Disposition 匙铡,那么不需要download屬性也能下載图甜。而且后端還可以設(shè)置文件名。
<a href="https:xxx.mp4" download="test">下載文件</a>
3.xhr(axios)下載
這個(gè)時(shí)候鳖眼,請(qǐng)求發(fā)送的時(shí)候需要注明responseType = "blob"
,如果沒有設(shè)置的情況下黑毅,new Blob
的時(shí)候需要傳入第二個(gè)參數(shù)。比如new Blob([res], { type: xhr.getResponseHeader("Content-Type") });
只是這時(shí)后端就沒法通過body報(bào)錯(cuò)了钦讳。只能通過狀態(tài)碼和響應(yīng)頭來傳遞信息了矿瘦。
最后我還是選擇用json來傳遞信息,設(shè)置這個(gè)responseType: 'blob',
以后,返回值會(huì)被轉(zhuǎn)為blob愿卒,這時(shí)我們log可以看到type缚去,是application/json
的情況就是報(bào)錯(cuò)的情形。然后我們轉(zhuǎn)化一遍json可以拿到報(bào)錯(cuò)信息琼开,其實(shí)也可以把這個(gè)邏輯加到axios攔截器里面
export const DOWNLOAD_ITEM = async (data: FileItem): Promise<any> => {
const res: Blob = await request.post(`${PROXY_SUFFIX}/downloadItem`, data, {
responseType: 'blob',
})
// json的情況說明是報(bào)錯(cuò)
if (res.type !== 'application/json') {
downloadFile(res, data.name)
} else {
const r = await res.text()
message.error(JSON.parse(r)?.msg)
}
}
這邊我后端用的是golang的gin框架
返回文件流調(diào)用c.File
,文件類型不用傳易结,c.Header("Content-Disposition", "attachment; filename="+req.Name)
這個(gè)設(shè)置可以返回文件名。
func (f *FileListAPI) DownloadItem(c *gin.Context) {
var req response.FileInfo
err := c.ShouldBindJSON(&req)
if err != nil {
response.FailWithMessage(err.Error(), c)
return
}
if req.Path == "" {
response.FailWithMessage("路徑不能為空", c)
return
}
if req.IsFolder {
response.FailWithMessage("路徑不能為文件夾", c)
return
} else {
c.Header("Content-Disposition", "attachment; filename="+req.Name)
// c.Header("Content-Transfer-Encoding", "binary")
// c.Header("Content-Type", "application/octet-stream")
c.File(req.Path)
}
fmt.Println("req", req)
}
下面是blob對(duì)象下載的邏輯,使用createObjectURL轉(zhuǎn)換為url,然后綁到a鏈接上搞动,通過點(diǎn)擊a鏈接的方式觸發(fā)下載躏精。
/**
* 使用bolb方式下載
* @param res
* @param filename
* @returns
*/
export function downloadFile(res: Blob, filename: string) {
const url = window.URL.createObjectURL(new Blob([res]))
const a = document.createElement('a')
a.style.display = 'none'
a.href = url
a.download = filename
document.body.appendChild(a)
a.click()
document.body.removeChild(a)
window.URL.revokeObjectURL(url) // 釋放blob對(duì)象
}