blob 图片_前端JS实现字符串/图片/excel文件下载
編者按:本文轉(zhuǎn)載自SF專欄,由作者 趙帥強(qiáng) 授權(quán)奇舞周刊轉(zhuǎn)載
在web開發(fā)中,如果你想讓用戶下載或者導(dǎo)出一個文件,應(yīng)該怎么做呢?
傳統(tǒng)的做法是在后端存儲或者即時生成一個文件來提供下載功能,這樣的優(yōu)勢是可以做權(quán)限控制、數(shù)據(jù)二次處理,但缺點(diǎn)是需要額外發(fā)起請求、增大服務(wù)端壓力、下載速度慢。
但隨著HTML5的標(biāo)準(zhǔn)發(fā)布,我們已經(jīng)能夠做到只前端來下載各種文件了。
后端響應(yīng)式下載
在常規(guī)的HTTP應(yīng)答中,Content-Disposition 消息頭指示回復(fù)的內(nèi)容該以何種形式展示,是以內(nèi)聯(lián)的形式(即網(wǎng)頁或者頁面的一部分),還是以附件的形式下載并保存到本地。
在HTTP場景中,第一個參數(shù)或者是inline(默認(rèn)值,表示回復(fù)中的消息體會以頁面的一部分或者整個頁面的形式展示),或者是attachment(意味著消息體應(yīng)該被下載到本地;大多數(shù)瀏覽器會呈現(xiàn)一個“保存為”的對話框,將filename的值預(yù)填為下載后的文件名)。
我們在后端響應(yīng)頭中只要設(shè)置該頭部信息,即可下載為文件,而不是請求并展示:
Content-Type:?text/html;?charset=utf-8
Content-Disposition:?attachment;?filename="cool.html"
但需要注意的是,如果想要用這種方式下載文件,不能使用AJAX的方式,而是應(yīng)該新建一個標(biāo)簽,模擬點(diǎn)擊下載。原因?yàn)樘幱诎踩钥紤],JavaScript無法與磁盤進(jìn)行交互,因此AJAX得到的內(nèi)容將被保留在內(nèi)存中,而不是磁盤上。
Nginx添加header頭下載
location?~?\.(jpg|jpeg|png|bmp|ico|gif|swf)$?{
????add_header Content-Disposition?'attachment; filename="cool.html"';
}
和后端一樣的原理,只不過頭部信息通過Nginx統(tǒng)一添加。
前端下載:標(biāo)簽的download屬性
此屬性指示瀏覽器下載URL而不是導(dǎo)航到它,因此將提示用戶將其保存為本地文件。如果屬性有一個值,那么它將作為下載的文件名使用。此屬性對允許的值沒有限制,但是/和\會被轉(zhuǎn)換為下劃線。
此屬性僅適用于同源 URLs。
盡管HTTP URL需要位于同一源中,但是可以使用 blob: URLs 和 data: URLs ,以方便用戶下載 JavaScript 方式生成的內(nèi)容(例如使用在線繪圖的Web應(yīng)用創(chuàng)建的照片)。
常規(guī)的標(biāo)簽,用于鏈接的跳轉(zhuǎn),如新的頁面,那么如果我們給標(biāo)簽加上download屬性,就能很簡單的讓用戶保存新的html頁面。
<a download="PHP實(shí)現(xiàn)并發(fā)請求.html"?href="https://segmentfault.com/a/1190000016343861">PHP實(shí)現(xiàn)并發(fā)請求</a>
生成并下載字符串文件
首先我們需要了解一個特殊的數(shù)據(jù)格式:Blob。
Blob數(shù)據(jù)
Blob(Binary Large Object,二進(jìn)制類型的大對象),表示一個不可變的原始數(shù)據(jù)的類文件對象,我們上傳文件時常用的File對象就繼承于Blob,并進(jìn)行了擴(kuò)展用于支持用戶系統(tǒng)上的文件。
我們只能通過Blob()構(gòu)造函數(shù)來創(chuàng)建一個新的Blob對象:
Blob(blobParts[, options])
// 創(chuàng)建一個json類型的Blob對象,支持傳入同類型數(shù)據(jù)的一個數(shù)組
var?debug?=?{hello:?"world"};
var?blob?=?new?Blob([JSON.stringify(debug,?null,?2)],
??{type?:?'application/json'});
// 此時blob的值
// Blob(22) {size: 22, type: 'application/json'}
Blob對象存在兩個只讀屬性:
size: Blob 對象中所包含數(shù)據(jù)的大小(字節(jié))。type: 一個字符串,表明該Blob對象所包含數(shù)據(jù)的MIME類型。如果類型未知,則該值為空字符串。
URL對象和下載字符串文件
URL 接口是一個用來創(chuàng)建 URLs 的對象,包含兩個靜態(tài)方法:
objectURL = URL.createObjectURL(blob) ?
創(chuàng)建一個 URL(DOMString),包含一個唯一的blob鏈接(該鏈接協(xié)議為以blob:,后跟唯一標(biāo)識瀏覽器中的對象的掩碼)。這個 URL 的生命周期和創(chuàng)建它的窗口中的 document 綁定。
URL.revokeObjectURL(objectURL) ?
銷毀之前使用URL.createObjectURL()方法創(chuàng)建的URL實(shí)例。瀏覽器會在文檔退出的時候自動釋放它們,但是為了獲得最佳性能和內(nèi)存使用狀況,你應(yīng)該在安全的時機(jī)主動釋放掉它們。
var?url?=?URL.createObjectURL(blob);
// 此時url的值,跟document綁定,所以每個頁面創(chuàng)建的字符串均不同
// blob:https://developer.mozilla.org/defe53c2-2882-43c6-b275-db2a57959789
此時,我們在頁面中創(chuàng)建一個新標(biāo)簽,點(diǎn)擊即可下載我們想要的文件:
<a href="blob:https://developer.mozilla.org/58702010-433d-4097-990f-e483d84cd02a"?download="file.json">下載文件鏈接</a>
FileReader讀取Blob數(shù)據(jù)
想要讀取Blob數(shù)據(jù)的唯一方法是FileReader。
FileReader 對象允許Web應(yīng)用程序異步讀取存儲在用戶計算機(jī)上的文件(或原始數(shù)據(jù)緩沖區(qū))的內(nèi)容,使用 File 或 Blob 對象指定要讀取的文件或數(shù)據(jù)。
其中File對象可以是來自用戶在一個元素上選擇文件后返回的FileList對象,也可以來自拖放操作生成的 DataTransfer對象,還可以是來自在一個HTMLCanvasElement上執(zhí)行mozGetAsFile()方法后返回結(jié)果。
該對象包含3個屬性:
FileReader.error ?
一個DOMException,表示在讀取文件時發(fā)生的錯誤 。
FileReader.readyState ?
表示FileReader狀態(tài)的數(shù)字。取值如下:
????常量名 ? ?值 ? ?描述
????EMPTY ? ?0 ? ?還沒有加載任何數(shù)據(jù).
????LOADING ? ?1 ? ?數(shù)據(jù)正在被加載.
????DONE ? ?2 ? ?已完成全部的讀取請求.
FileReader.result ?
文件的內(nèi)容。該屬性僅在讀取操作完成后才有效,數(shù)據(jù)的格式取決于使用哪個方法來啟動讀取操作。
包含6個事件處理:onabort,onerror,onload,onloadstart,onloadend,onprogress,這些不再詳細(xì)說明,因?yàn)?FileReader 繼承自EventTarget,所以所有這些事件也可以通過addEventListener方法使用。
包含5個方法:
FileReader.abort() ?
中止讀取操作。在返回時,readyState屬性為DONE。
FileReader.readAsArrayBuffer() ?
開始讀取指定的 Blob中的內(nèi)容, 一旦完成, result 屬性中保存的將是被讀取文件的 ArrayBuffer 數(shù)據(jù)對象.
FileReader.readAsBinaryString() ?
開始讀取指定的Blob中的內(nèi)容。一旦完成,result屬性中將包含所讀取文件的原始二進(jìn)制數(shù)據(jù)。
FileReader.readAsDataURL() ?
開始讀取指定的Blob中的內(nèi)容。一旦完成,result屬性中將包含一個data: URL格式的字符串以表示所讀取文件的內(nèi)容。
FileReader.readAsText() ?
開始讀取指定的Blob中的內(nèi)容。一旦完成,result屬性中將包含一個字符串以表示所讀取的文件內(nèi)容。
因此我們可以直接讀取Blob對象的數(shù)據(jù):
var?reader?=?new?FileReader();
reader.addEventListener("loadend",?function()?{
???console.log(reader.result);
});
reader.readAsDataURL(blob);
// 此時result的值
// data:application/json;base64,ewogICJoZWxsbyI6ICJ3b3JsZCIKfQ==
reader.readAsText(blob);
// 此時result的值
// {
// "hello": "world"
// }
下載圖片
除了下載手動生成的字符串或?qū)ο?#xff0c;我們還能提供下載圖片的功能,一方面能用于支持Canvas繪圖的保存功能,一方面能提供批量下載圖片等高級功能。
除了瀏覽器自帶的右鍵保存,我們還可以這么做來下載圖片:
// 通過src獲取圖片的blob對象
function?getImageBlob(url,?cb)?{
????var?xhr?=?new?XMLHttpRequest();
????xhr.open("get",?url,?true);
????xhr.responseType?=?"blob";
????xhr.onload?=?function()?{
????????if?(this.status?==?200)?{
????????????cb(this.response);
????????}
????};
????xhr.send();
}
let?reader?=?new?FileReader();
reader.addEventListener("loadend",?function()?{
???console.log(reader.result);
});
getImageBlob('https://cdn.segmentfault.com/v-5c4ec07f/global/img/user-64.png',?function(blob){
????// 讀取來看下下載的內(nèi)容
????reader.readAsDataURL(blob);
????// 最終生成的字符串
????// data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAA...
????// 生成下載用的URL對象
????let?url?=?URL.createObjectURL(blob);
????// 生成一個a標(biāo)簽,并模擬點(diǎn)擊,即可下載,批量下載同理
????let?aDom?=?aDom?=?document.createElement('a');
????aDom.href?=?url;
????aDom.download?=?'download.json';
????aDom.text?=?'下載文件';
????document.getElementsByTagName('body')[0].appendChild(aDom);
????aDom.click();
});
下載excel文件等
如果你明白了下載的原理,那么所有的內(nèi)容都能夠理解,只不過是轉(zhuǎn)換成對應(yīng)的格式而已,當(dāng)然,復(fù)雜格式的文檔不需要你自己去配置,可以引入第三方庫,在excel文檔方面我選擇用 tableExport庫(https://tableexport.v5.travismclarke.com/#javascript):
// 引入CDN文件
'https://cdn.bootcss.com/xlsx/0.14.1/xlsx.core.min.js',
'https://cdn.bootcss.com/FileSaver.js/2014-11-29/FileSaver.min.js',
'https://cdn.bootcss.com/TableExport/5.2.0/js/tableexport.min.js'
// 綁定下載事件,這個是我自己的場景下代碼,可能不適合大家,具體的參考官方文檔
const?tableDom?=?$('#table');
$('.table-exportBtn',?tableDom).on('click',?function?()?{
????const?tableExport?=?tableDom.tableExport({
????????formats:?['xlsx',?'txt'],
????????filename:?'表格下載',
????????exportButtons:?false
????});
????const?type?=?$(this).data().type;
????const?exportData?=?tableExport.getExportData()[tableDom[0].id][type];
????const?{data,?mimeType,?filename,?fileExtension,?merges,?RTL,?sheetname}?=?exportData;
????// 源碼里才能看到完整參數(shù),官方文檔沒有寫全,導(dǎo)致下載的文件格式錯誤
????tableExport.export2file(data,?mimeType,?filename,?fileExtension,?merges,?RTL,?sheetname);
});
默認(rèn)的方法會自動生成下載按鈕,但如果你想自定義下載功能,參考 exportButtons: false 設(shè)置(https://tableexport.v3.travismclarke.com/examples/exportButtons.html) 一節(jié),但這個文檔有問題,export2file參數(shù)不完整,導(dǎo)致下載的xlsx文件一直格式錯誤,通過查看源碼,需要寫全參數(shù)才可以,上面的示例里已經(jīng)寫出。
tableExport庫源碼
我們可以看下tableExport導(dǎo)出文件的核心代碼,其導(dǎo)出為excel格式比較復(fù)雜,由xlsx.core.min.js來完成:
/**
?????* Exports and downloads the file
?????* @memberof TableExport.prototype
?????* @param data {String}
?????* @param mime {String} mime type
?????* @param name {String} filename
?????* @param extension {String} file extension
?????* @param merges {Object[]}
?????* @param RTL {Boolean}
?????*/
????export2file:?function(data,?mime,?name,?extension,?merges,?RTL,?sheetname)?{
??????var?format?=?extension.slice(1);
??????data?=?this.getRawData(data,?extension,?name,?merges,?RTL,?sheetname);
??????if?(_isMobile?&&?(format?===?_FORMAT.CSV?||?format?===?_FORMAT.TXT))?{
????????// 拼湊指定格式的data:類型 URI
????????var?dataURI?=?"data:"?+?mime?+?";"?+?this.charset?+?","?+?data;
????????this.downloadDataURI(dataURI,?name,?extension);
??????}?else?{
????????// TODO: error and fallback when `saveAs` not available
????????saveAs(new?Blob([data],?{?type:?mime?+?";"?+?this.charset?}),?name?+?extension,?true);
??????}
????},
????// 先創(chuàng)建標(biāo)簽,然后提供href和download屬性,并模擬點(diǎn)擊
????downloadDataURI:?function(dataURI,?name,?extension)?{
??????var?encodedUri?=?encodeURI(dataURI);
??????var?link?=?document.createElement("a");
??????link.setAttribute("href",?encodedUri);
??????link.setAttribute("download",?name?+?extension);
??????document.body.appendChild(link);
??????link.click();
????},
xlsx文件導(dǎo)出導(dǎo)出
還沒有仔細(xì)研究,感興趣的可以查看其js-xlsx Github項(xiàng)目(https://github.com/SheetJS/js-xlsx/blob/master/README.md)
第三方庫
上面我們主要講了下載背后的原理,你可以自己封裝,也可以使用現(xiàn)成的第三方庫,如 download.js(https://github.com/rndme/download) ,這個能提供大部分常用數(shù)據(jù)的下載;但如果你是要下載表格數(shù)據(jù)為excel格式,還是推薦 tableExport.js(https://tableexport.v5.travismclarke.com/#javascript) 及其依賴組件。
參考資料
MDN-a: (https://developer.mozilla.org/zh-CN/docs/Web/HTML/Element/a)
MDN-blob: (https://developer.mozilla.org/zh-CN/docs/Web/API/Blob)
掘金-細(xì)說Web API中的Blob:(https://juejin.im/post/59e35d0e6fb9a045030f1f35)
MDN-URL: (https://developer.mozilla.org/zh-CN/docs/Web/API/URL)
MDN-FileReader: (https://developer.mozilla.org/zh-CN/docs/Web/API/FileReader)
博客園-js 獲取圖片url的Blob值并預(yù)覽:(https://www.cnblogs.com/tujia/p/6483255.html)
tableExport文檔:(https://tableexport.v5.travismclarke.com/#javascript)
感謝 @Oliveryoung 提供的其他解決方案
MDN-Content-Disposition: (https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Headers/Content-Disposition)
Ajax請求無法下載文件的原因: (https://blog.csdn.net/w405722907/article/details/77366843)
Github-download.js:(https://github.com/rndme/download)
關(guān)于奇舞周刊
《奇舞周刊》是360公司專業(yè)前端團(tuán)隊(duì)「奇舞團(tuán)」運(yùn)營的前端技術(shù)社區(qū)。關(guān)注公眾號后,直接發(fā)送鏈接到后臺即可給我們投稿。
總結(jié)
以上是生活随笔為你收集整理的blob 图片_前端JS实现字符串/图片/excel文件下载的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: mysql 服务无法启动 没有报告任何错
- 下一篇: html5语言基础教程,HTML5基础教