前端 javascript 實(shí)現(xiàn)文件下載的示例
在 html5 中,a 標(biāo)簽新增了 download 屬性,包含該屬性的鏈接被點(diǎn)擊時(shí),瀏覽器會(huì)以下載文件方式下載 href 屬性上的鏈接。示例:
<a rel="external nofollow" download="baidu.html">下載</a>
1. 前端 js 下載實(shí)現(xiàn)與示例
通過(guò) javascript 動(dòng)態(tài)創(chuàng)建一個(gè)包含 download 屬性的 a 元素,再觸發(fā)點(diǎn)擊事件,即可實(shí)現(xiàn)前端下載。
代碼示例:
function download(href, title) { const a = document.createElement('a'); a.setAttribute('href', href); a.setAttribute('download', title); a.click(); }
說(shuō)明:
- href 屬性設(shè)置要下載的文件地址。這個(gè)地址支持多種方式的格式,因此可以實(shí)現(xiàn)豐富的下載方法。
- download 屬性設(shè)置了下載文件的名稱。但 href 屬性為普通鏈接并且跨域時(shí),該屬性值設(shè)置多數(shù)情況下會(huì)被瀏覽器忽略。
1.1 普通連接下載示例
// 下載圖片 download('https://lzw.me/images/gravatar.gif', 'lzwme-gravatar'); // 下載一個(gè)連接 download('https://lzw.me', 'lzwme-index.html');
1.2 href 為 data URIs 示例
data URI 是前綴為 data:scheme 的 URL,允許內(nèi)容創(chuàng)建者在文檔中嵌入小文件。數(shù)據(jù)URI由四個(gè)部分組成:前綴(數(shù)據(jù):),指示數(shù)據(jù)類(lèi)型的MIME類(lèi)型,如果非文本則為可選的base64令牌,數(shù)據(jù)本身:
data:[<mediatype>][;base64],<data>
鏈接的 href 屬性為 data URIs 時(shí),也可以實(shí)現(xiàn)文件內(nèi)容的下載。示例:
download('data:,Hello%2C%20World!', 'data-uris.txt'); download('data:text/plain;base64,SGVsbG8sIFdvcmxkIQ%3D%3D', 'data-uris.txt');
1.3 canvas 下載示例
對(duì)于 canvas 可以通過(guò) toDataURL 方法取得 data URIs 格式的內(nèi)容。
1.4 二進(jìn)制內(nèi)容下載
URL.createObjectURL 方法會(huì)根據(jù)傳入的參數(shù)創(chuàng)建一個(gè)指向該參數(shù)對(duì)象的 URL。新的對(duì)象 URL 指向執(zhí)行的 File 對(duì)象或者是 Blob 對(duì)象。
URL.createObjectURL 的參數(shù)是 File 對(duì)象或者 Blob 對(duì)象,F(xiàn)ile 對(duì)象也就是通過(guò) input[type=file] 選擇的文件,Blob 對(duì)象是二進(jìn)制數(shù)據(jù)。
將URL.createObjectURL 返回值設(shè)為 href 屬性的值,即可實(shí)現(xiàn)二進(jìn)制內(nèi)容下載。示例:
const content = 'Welcome to lzw.me!'; const blob = new Blob([content]); const href = URL.createObjectURL(blob); download(href, 'download-text.txt'); URL.revokeObjectURL(href);
1.5 前端下載方法示例
綜合上述討論,這里給出一個(gè)前端實(shí)現(xiàn)下載的 saveAs 方法的 TypeScript 示例:
/** * 通過(guò)創(chuàng)建 a dom 對(duì)象方式實(shí)現(xiàn)前端文件下載 * @param href 要下載的內(nèi)容鏈接。當(dāng)定義了 toBlob 時(shí),可以為純文本或二進(jìn)制數(shù)據(jù)(取決于 toBlob 格式 * @param fileName 下載后的文件名稱 * @param toBlob 如設(shè)置該參數(shù),則通過(guò) blob 方式將 href 轉(zhuǎn)換為要保存的文件內(nèi)容,該參數(shù)將入?yún)?new Blob([href], toBlob) 的第二個(gè)參數(shù) * @example * ```js * saveAs('abc', 'abc.txt', {}); * saveAs('data:,Hello%2C%20World!', 'hello.txt'); * saveAs('https://lzw.me/images/avatar/lzwme-80x80.png', 'lzwme-logo.png'); * ``` */ export function saveAs(href: string | Blob, fileName?: string, toBlob?: PlainObject) { const isBlob = href instanceof Blob || toBlob; if (!fileName && typeof href === 'string' && href.startsWith('http')) { fileName = href.slice(href.lastIndexOf('/') + 1); } fileName = decodeURIComponent(fileName || 'download'); if (typeof href === 'string' && toBlob) href = new Blob([href], toBlob); if (href instanceof Blob) href = URL.createObjectURL(href); const aLink = document.createElement('a'); aLink.setAttribute('href', href); aLink.setAttribute('download', fileName); aLink.click(); // const evt = document.createEvent("HTMLEvents"); // evt.initEvent("click", false, false); // aLink.dispatchEvent(evt); if (isBlob) setTimeout(() => URL.revokeObjectURL(aLink.href), 100); return aLink; }
2.檢測(cè)瀏覽器是否支持 download 屬性
download 屬性為 html5 新增內(nèi)容,瀏覽器支持情況可參考:http://caniuse.com/#feat=download
<img src="https://lzw.me/wp-content/uploads/2017/04/a-download.png" alt="" width="879" height="346" class="aligncenter size-full wp-image-2330" />
判斷瀏覽器是否支持該屬性,只需要檢測(cè) a 標(biāo)簽是否存在 download 屬性。示例:
const downloadAble = 'download' in document.createElement('a');
對(duì)于不支持的瀏覽器,只能另想他法或者予以降級(jí)處理了。
3.使用 serviceWorker 和 fetch API 代理實(shí)現(xiàn)
前端下載更多的需求是因?yàn)閮?nèi)容產(chǎn)生于前端。那么可以在后端實(shí)現(xiàn)一個(gè)這樣的 API ,它在接收到前端發(fā)出的內(nèi)容后返回下載格式的數(shù)據(jù)。這種實(shí)現(xiàn)就不存在瀏覽器兼容問(wèn)題。
利用 serviceWorker 和 fetch API 截?cái)r瀏覽器請(qǐng)求,只需實(shí)現(xiàn)好約定邏輯,也可實(shí)現(xiàn)這種功能需求。示例:
在頁(yè)面中,通過(guò) fetch API 構(gòu)造請(qǐng)求:
fetch('lzwme.txt', { isDownload: true, body: { data: new Blob('hi!') } })
在 serviceWorker 中,截?cái)r附帶 isDownload 頭信息的請(qǐng)求,構(gòu)造下載回應(yīng):
self.addEventListener('fetch', function(event) { const req = event.request; if (!req.headers.get('isDownload')) { retrun fetch(req); } const filename = encodeURIComponent(req.url); const contentType = req.headers.get('Content-Type') || 'application/force-download'; const disposition = "inline;filename=" + filename + ";filename*=utf-8''" + filename const myBody = req.headers.get(body).data; event.respondWith( new Response(myBody, { headers: { 'Content-Type': contentType, 'Content-Disposition': disposition } }) ); });
4 使用 ajax (xhr與fetch API) 方式下載服務(wù)器文件
以上主要討論的是純前端實(shí)現(xiàn)下載保存文件的方法。對(duì)于下載服務(wù)器文件,最簡(jiǎn)的方式就是 window.open(url) 和 location.href=url 了,但是其的弊端也很明顯,當(dāng)出錯(cuò)時(shí)整個(gè)頁(yè)面都會(huì)掛掉,而且也無(wú)法獲得下載狀態(tài)與進(jìn)度,下載時(shí)間稍長(zhǎng)時(shí)體驗(yàn)相當(dāng)不好。
下面介紹一下使用 xhr 和 fetch API 實(shí)現(xiàn)文件下載的方法。其主要思路為:將請(qǐng)求結(jié)果設(shè)為 Blob 類(lèi)型,然后采用前端下載保存 Blob 類(lèi)型數(shù)據(jù)的方式實(shí)現(xiàn)下載。
4.1 使用 xhr 下載遠(yuǎn)程服務(wù)器文件
代碼示例:
/** 前端下載/保存文件 */ function saveAs(href, fileName) { const isBlob = href instanceof Blob; const aLink = document.createElement('a'); aLink.href = isBlob ? window.URL.createObjectURL(href) : href; aLink.download = fileName; aLink.click(); if (isBlob) setTimeout(() => URL.revokeObjectURL(aLink.href), 100); } function xhrDownload(url, options = {}) { options = Object.assign({ method: 'get', headers: {} }, options); return new Promise((reslove, reject) => { const xhr = new XMLHttpRequest(); xhr.responseType = 'blob'; // options.responseType; if (options.headers) { for (const key in options.headers) xhr.setRequestHeader(key, options.headers[key]); } xhr.onload = () => { // 從 Content-Disposition 中獲取文件名示例 const cd = xhr.getResponseHeader('Content-Disposition'); if (cd && cd.includes('fileName') && !options.fileName) options.fileName = cd.split('fileName=')[1]; options.fileName = decodeURIComponent(options.fileName || 'download-file'); if (+xhr.status == 200) { saveAs(xhr.response, options.fileName); reslove(options.fileName);
使用 fecth API 下載遠(yuǎn)程服務(wù)器文件
function fetchDownload(url, options = {}) { options = Object.assign({ credentials: 'include', method: 'get', headers: {} }, options); return fetch(url, options).then(response => { return response.blob().then(blob => { if (!blob || !blob.size) return Promise.reject('empty'); // 從 Content-Disposition 中獲取文件名示例 const cd = response.headers.get('Content-Disposition'); if (cd && cd.includes('fileName') && !options.fileName) options.fileName = cd.split('fileName=')[1]; options.fileName = decodeURIComponent(options.fileName || 'download-file'); saveAs(blob, options.fileName); return options.fileName; }); }); } // 測(cè)試 fetchDownload('https://lzw.me/images/avatar/lzwme-80x80.png', { // method: 'post', // headers: { // 'Content-Type': 'application/json' // }, // body: JSON.stringify({ // pageSize: 100000, // startPage: 0 // }) })
以上就是前端 javascript 實(shí)現(xiàn)文件下載的示例的詳細(xì)內(nèi)容,更多關(guān)于JavaScript 文件下載的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
JavaScrip報(bào)錯(cuò):module?is?not?defined的原因及解決
這篇文章主要給大家介紹了關(guān)于JavaScrip報(bào)錯(cuò):module?is?not?defined的原因及解決方法,文中通過(guò)代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-09-09琥珀無(wú)限級(jí)聯(lián)動(dòng)菜單-JavaScript版
琥珀無(wú)限級(jí)聯(lián)動(dòng)菜單-JavaScript版...2006-11-11ClearTimeout消除閃動(dòng)實(shí)例代碼
本文給大家介紹ClearTimeout消除閃動(dòng)相關(guān)知識(shí),本文介紹的非常詳細(xì),具有參考借鑒價(jià)值,感興趣的朋友一起學(xué)習(xí)吧2016-02-02在js代碼拼接dom對(duì)象到頁(yè)面上的模板總結(jié)
今天小編就為大家分享一篇關(guān)于在js代碼拼接dom對(duì)象到頁(yè)面上的模板總結(jié),小編覺(jué)得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來(lái)看看吧2018-10-10使用JavaScript實(shí)現(xiàn)獲取audio時(shí)長(zhǎng)
這篇文章主要為大家詳細(xì)介紹了如何使用JavaScript實(shí)現(xiàn)獲取audio時(shí)長(zhǎng),并且轉(zhuǎn)換為分鐘00:00:00格式,感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2024-04-04深入理解JavaScript系列(26):設(shè)計(jì)模式之構(gòu)造函數(shù)模式詳解
這篇文章主要介紹了深入理解JavaScript系列(26):設(shè)計(jì)模式之構(gòu)造函數(shù)模式詳解,本文講解了基本用法、構(gòu)造函數(shù)與原型、只能用new嗎?、強(qiáng)制使用new、原始包裝函數(shù)等內(nèi)容,需要的朋友可以參考下2015-03-03