JS處理檔案流
阿新 • • 發佈:2018-11-23
最近做一個專案,遇到了一個問題,就是匯出Excel功能。多普通呀,多大眾化,哪裡都有,可惜我們後臺說給我JSON資料,自己處理。我果斷拒絕了,拒絕的裡有是我菜,實現不了啊。然後後臺開發看不下去了,就是轉成檔案流給我吧。他們那裡是分散式部署,也沒有辦法持久化儲存。遂發生了一下的故事
百度
沒有怎麼做過,肯定是百度啦,然後找打了一段程式碼,程式碼內容如下
function download() {
var xmlResquest = new XMLHttpRequest();
xmlResquest.open("POST", "/eksoft/fileUpload/download" , true);
xmlResquest.setRequestHeader("Content-type", "application/json");
xmlResquest.setRequestHeader("Authorization", "Bearer 6cda86e3-ba1c-4737-972c-f815304932ee");
xmlResquest.responseType = "blob";
xmlResquest.onload = function (oEvent) {
var content = xmlResquest.response;
var elink = document.createElement('a');
elink.download = "test.xlsx";
elink.style.display = 'none';
var blob = new Blob([content]);
elink.href = URL.createObjectURL(blob);
document.body.appendChild(elink);
elink.click();
document.body.removeChild(elink);
} ;
xmlResquest.send();
}
- 簡單的分析了一下,自己有用的程式碼如下:
var content = 'content';
var elink = document.createElement('a');
var blob = new Blob([content]);
elink.download = "test.xlsx";
elink.style.display = 'none';
elink.href = URL.createObjectURL(blob);
elink.click();
- 看到這裡,發現有兩個不認識的api,果斷去mdn取經
createObjectURL
URL.createObjectURL()
, 靜態方法,會建立一個DOMString
,其中包含一個表示引數中給出的物件URL。這個URL的生命週期和建立他的視窗中的document
繫結,這個新的URL物件表示File
物件或者Blob
物件。- 語法:
objectURL = URL.createObjectURL(blob);
revokeObjectURL
URL.revokeObjectURL
靜態方法。 來釋放之前通過呼叫createObjectURL
建立的已經存在的物件。當結束使用某個URL物件時,應該通過這個方法來訪瀏覽器知道不再需要這個檔案的引用了。- 語法:
window.URL.revokeObjectURL(objectURL);
bjectURL: 是一個DOMString
,表示通過呼叫URL.createObjectURL
方法產生的 URL 物件
DOMString
- DOMString 是一個UTF-16字串。由於JavaScript已經使用了這樣的字串,所以DOMString 直接對映到 一個
String
。 - 將
null
傳遞給接受DOMString的方法或引數時通常會把其stringifies為“null”。
動手改造階段
- 通過有了以上的條件,具備了自己動手改造的條件。
- 我的思路如下,
- 通過fetch請求拿到資料流
- 將下載的程式碼封裝為一個函式
- 將下載拿到的檔案流直接傳入該函式
- 函式內部處理下載,然後刪除該連結
/**
* 匯出檔案工具方法
* 需要將返回的檔案流物件直接傳入,
* 如果沒有資料, 返回一個物件
*/
export let exportFile = (data, name = 'name') => {
return data.blob().then((blob) => {
// js無法判斷檔案劉是否存在,只能通過型別
// 檔案流沒有資料的時候轉碼是/html結尾,我這個直接返回一個物件,方便呼叫的時候處理
if (blob.type.endsWith('/html')) {
return {
msg: "暫無資料"
}
}
let downloadUrl = window.URL.createObjectURL(blob);
let anchor = document.createElement("a");
let filename = data.headers.get('Content-Disposition');
anchor.href = downloadUrl;
anchor.download = filename.replace('filename=', '');
anchor.click();
window.URL.revokeObjectURL(blob);
})
}
// 呼叫檔案匯出方法
async function export () {
let res = await axios.post('http://xxx.com', {})
let err = await exportFile(res.data, '推送日誌')
if (err) message.warning(err.msg)
}
export()
- 改造後的方法如上,在本地實現了檔案流儲存到磁碟。
- 但是我程式碼到生產環境的時候發現fetch的response的type變了,在本地,response.type是base, 但是在線上去成了cors,嗯麼麼,具體別的沒有感受到區別,就是報錯了
- 到了這裡,當然是檢查自己的程式碼…ok,檢查響應頭…ok,百度… 沒有類似的情況。找後端同事商量…沒有個所以然, 然後回頭仔細看了一眼程式碼,提示filename.replace沒有這個方法,索性自己慢慢的使用console.log檢視,就是無法從data.headers中拿到檔案描述了,然後在下載賦值檔名的時候報錯了。
- 遂自己和後臺商量了一下,採用簡單的規則,我自己本地指定檔名,不讀取相應頭了。更在如下
/**
* 匯出檔案工具方法
* 需要將返回的檔案流物件直接傳入,
* 如果沒有資料, 返回一個物件
* 檔案命名規範為手動傳入一個檔名,然後加上日期,時分秒
*/
export let exportFile = (data, name = 'name') => {
return data.blob().then((blob) => {
if (blob.type.endsWith('/html')) {
return {
msg: "暫無資料"
}
}
let downloadUrl = window.URL.createObjectURL(blob);
let anchor = document.createElement("a");
anchor.href = downloadUrl;
anchor.download = `${name}${Format(new Date(), 'yyyyMMddhhmmss')}.csv`;
anchor.click();
window.URL.revokeObjectURL(blob);
})
}
總結
到了這裡,簡單的檔案處理就結束了,後臺又差了一下,說這個會有相容性問題,然後有一段處理相容的程式碼,我這裡目前沒有管ie,遂沒有驗證,但是還是把程式碼貼過來,留著總是好的
if (window.navigator.msSaveOrOpenBlob) {
navigator.msSaveBlob(blob, filename);
} else {
var a = document.createElement('a');
blob.type = "application/excel";
var url = createObjectURL(blob);
a.href = url;
a.download = filename;
a.click();
window.URL.revokeObjectURL(url);
}