前言
導出功能其實在開發過程中是很常見的,平時我們做導出功能的時候基本都是后台生成,我們直接只需要調一支接口后台把生成的文件放到服務器或者數據庫mongodb中,如果是放到mongodb中的話,我們需要從mongodb中通過唯一生成的id去拿到文件,最后window.location.href就完事了。如果是放到服務器上,直接從服務器上下載就好了。下面我們使用另一種 H5 的新特性blob[1]對象來實現一下導出功能。
Blob: https://developer.mozilla.org/zh-CN/docs/Web/API/Blob/Blob
什么是 Blob
Blob() 構造函數返回一個新的 Blob 對象。blob 的內容由參數數組中給出的值的串聯組成。
var aBlob = new Blob( array, options );
兼容性
mimeType[2]
在 Blob 的構造函數中options參數的接受一個參數type,這個參數代表的是媒體類型,告訴瀏覽器是什么類型的文件,常見的有
{".3gp", "video/3gpp"},
{".apk", "application/vnd.android.package-archive"},
{".asf", "video/x-ms-asf"},
{".avi", "video/x-msvideo"},
{".bin", "application/octet-stream"},
{".bmp", "image/bmp"},
{".c", "text/plain"},
{".class", "application/octet-stream"},
{".conf", "text/plain"},
{".cpp", "text/plain"},
{".doc", "application/msword"},
{".docx", "application/vnd.openxmlformats-officedocument.wordprocessingml.document"},
{".xls", "application/vnd.ms-excel"},
{".xlsx", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"},
{".exe", "application/octet-stream"},
{".gif", "image/gif"},
{".gtar", "application/x-gtar"},
{".gz", "application/x-gzip"},
{".h", "text/plain"},
{".htm", "text/html"},
{".html", "text/html"},
{".jar", "application/java-archive"},
{".java", "text/plain"},
{".jpeg", "image/jpeg"},
{".jpg", "image/jpeg"},
{".js", "application/x-javascript"},
{".log", "text/plain"},
{".m3u", "audio/x-mpegurl"},
{".m4a", "audio/mp4a-latm"},
{".m4b", "audio/mp4a-latm"},
{".m4p", "audio/mp4a-latm"},
{".m4u", "video/vnd.mpegurl"},
{".m4v", "video/x-m4v"},
{".mov", "video/quicktime"},
{".mp2", "audio/x-mpeg"},
{".mp3", "audio/x-mpeg"},
{".mp4", "video/mp4"},
{".mpc", "application/vnd.mpohun.certificate"},
{".mpe", "video/mpeg"},
{".mpeg", "video/mpeg"},
{".mpg", "video/mpeg"},
{".mpg4", "video/mp4"},
{".mpga", "audio/mpeg"},
{".msg", "application/vnd.ms-outlook"},
{".ogg", "audio/ogg"},
{".pdf", "application/pdf"},
{".png", "image/png"},
{".pps", "application/vnd.ms-powerpoint"},
{".ppt", "application/vnd.ms-powerpoint"},
{".pptx", "application/vnd.openxmlformats-officedocument.presentationml.presentation"},
{".prop", "text/plain"},
{".rc", "text/plain"},
{".rmvb", "audio/x-pn-realaudio"},
{".rtf", "application/rtf"},
{".sh", "text/plain"},
{".tar", "application/x-tar"},
{".tgz", "application/x-compressed"},
{".txt", "text/plain"},
{".wav", "audio/x-wav"},
{".wma", "audio/x-ms-wma"},
{".wmv", "audio/x-ms-wmv"},
{".wps", "application/vnd.ms-works"},
{".xml", "text/plain"},
{".z", "application/x-compress"},
{".zip", "application/x-zip-compressed"},
{"", "*/*"}
導出
我們需要調取接口來獲取導出文件的內容,如果我們先后端分離的話,我們需要接口給我們返回Buffer, Blob, DOMString類型的數據,DOMStrings會被編碼為UTF-8。
let blob = new Blob([接口返回的數據], {
type: "application/vnd.ms-excel;charset=utf-8"
});
使用a標簽,模擬點擊a標簽完成導出功能,通過URL.createObjectURL()[3]方法創建一個下載的鏈接地址,最后在不需要的時候URL.revokeObjectURL釋放掉
let downloadElement = document.createElement("a");
let href = window.URL.createObjectURL(blob); //創建下載的鏈接
downloadElement.href = href;
document.body.appendChild(downloadElement);
downloadElement.click(); //點擊下載
document.body.removeChild(downloadElement); //下載完成移除元素
window.URL.revokeObjectURL(href); //釋放掉blob對象
文件名的設置
文件名稱通過報文頭設置content-disposition屬性設置,Content-Disposition參數:
attachment --- 作為附件下載
inline --- 在線打開
setHeader("Content-Disposition","inline; filename=文件名.mp3");
setHeader("Content-Disposition","attachment;filename=test.xls");
前端通過截取報文頭里的content-disposition字段獲取文件名稱:
downloadElement.download =decodeURI(
res.headers["content-disposition"].split("filename=")[1]
) || ""; //下載后文件名
完整代碼
這里的res代表后台返回的數據:
config: {url: "/ExportExcel", method: "post", data: "", headers: {…}, baseURL: "/api/", …}
data: Blob {size: 5120, type: "application/vnd.ms-excel"}
headers: {connection: "close", content-disposition: "attachment;filename=xxx.xls", content-encoding: "gzip", content-length: "1455", content-type: "application/vnd.ms-excel;charset=UTF-8", …}
request: XMLHttpRequest {readyState: 4, timeout: 30000, withCredentials: false, upload: XMLHttpRequestUpload, onreadystatechange: ƒ, …}
status: 200
statusText: "OK"
前端代碼
let blob = new Blob([res.data], {
type: "application/vnd.ms-excel;charset=utf-8"
});
let downloadElement = document.createElement("a");
let href = window.URL.createObjectURL(blob); //創建下載的鏈接
downloadElement.href = href;
downloadElement.download =
decodeURI(
res.headers["content-disposition"].split("filename=")[1]
) || ""; //下載后文件名
document.body.appendChild(downloadElement);
downloadElement.click(); //點擊下載
document.body.removeChild(downloadElement); //下載完成移除元素
window.URL.revokeObjectURL(href); //釋放掉blob對象
轉載至:https://zhuanlan.zhihu.com/p/342980679
按照文中做法導出,會出現導出的文件亂碼的問題,並且我確定blob的type值是沒有問題的,type對照參考:http://tools.jb51.net/table/http_content_type

最后經過排查,時接收的數據類型未設置為blob
export function exportProgressExcel(
params: SearchProjectProgressReq
): Promise<any> {
return new Promise(resolve => {
http
.get('/url', { params ,responseType:"blob"})
.then(res => resolve(res?.data));
});
}
文件實現簡單導出后,想要修改導出的文件名,於是加一行代碼:
downloadElement.download = '項目進展.xls'
為什么這樣就可以修改文件名了呢?翻了下文檔
https://developer.mozilla.org/zh-CN/docs/Web/HTML/Element/a
繼續完善,打開一個新的標簽頁用於下載文件
