前端 javascript 實現檔案下載的示例
在 html5 中,a 標籤新增了 download 屬性,包含該屬性的連結被點選時,瀏覽器會以下載檔案方式下載 href 屬性上的連結。示例:
<a href="https://www.baidu.com" rel="external nofollow" download="baidu.html">下載</a>
1. 前端 js 下載實現與示例
通過 javascript 動態建立一個包含 download 屬性的 a 元素,再觸發點選事件,即可實現前端下載。
程式碼示例:
function download(href,title) { const a = document.createElement('a'); a.setAttribute('href',href); a.setAttribute('download',title); a.click(); }
說明:
- href 屬性設定要下載的檔案地址。這個地址支援多種方式的格式,因此可以實現豐富的下載方法。
- download 屬性設定了下載檔案的名稱。但 href 屬性為普通連結並且跨域時,該屬性值設定多數情況下會被瀏覽器忽略。
1.1 普通連線下載示例
// 下載圖片 download('https://lzw.me/images/gravatar.gif','lzwme-gravatar'); // 下載一個連線 download('https://lzw.me','lzwme-index.html');
1.2 href 為 data URIs 示例
data URI 是字首為 data:scheme 的 URL,允許內容建立者在文件中嵌入小檔案。資料URI由四個部分組成:字首(資料:),指示資料型別的MIME型別,如果非文字則為可選的base64令牌,資料本身:
data:[<mediatype>][;base64],<data>
連結的 href 屬性為 data URIs 時,也可以實現檔案內容的下載。示例:
download('data:,Hello%2C%20World!','data-uris.txt'); download('data:text/plain;base64,SGVsbG8sIFdvcmxkIQ%3D%3D','data-uris.txt');
1.3 canvas 下載示例
對於 canvas 可以通過 toDataURL 方法取得 data URIs 格式的內容。
1.4 二進位制內容下載
URL.createObjectURL 方法會根據傳入的引數建立一個指向該引數物件的 URL。新的物件 URL 指向執行的 File 物件或者是 Blob 物件。
URL.createObjectURL 的引數是 File 物件或者 Blob 物件,File 物件也就是通過 input[type=file] 選擇的檔案,Blob 物件是二進位制資料。
將URL.createObjectURL 返回值設為 href 屬性的值,即可實現二進位制內容下載。示例:
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 前端下載方法示例
綜合上述討論,這裡給出一個前端實現下載的 saveAs 方法的 TypeScript 示例:
/** * 通過建立 a dom 物件方式實現前端檔案下載 * @param href 要下載的內容連結。當定義了 toBlob 時,可以為純文字或二進位制資料(取決於 toBlob 格式 * @param fileName 下載後的檔名稱 * @param toBlob 如設定該引數,則通過 blob 方式將 href 轉換為要儲存的檔案內容,該引數將入參為 new Blob([href],toBlob) 的第二個引數 * @example * ```js * saveAs('abc','abc.txt',{}); * saveAs('data:,'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.檢測瀏覽器是否支援 download 屬性
download 屬性為 html5 新增內容,瀏覽器支援情況可參考:http://caniuse.com/#feat=download
<img src="https://lzw.me/wp-content/uploads/2017/04/a-download.png" alt="前端 javascript 實現檔案下載的示例" width="879" height="346" class="aligncenter size-full wp-image-2330" />
判斷瀏覽器是否支援該屬性,只需要檢測 a 標籤是否存在 download 屬性。示例:
const downloadAble = 'download' in document.createElement('a');
對於不支援的瀏覽器,只能另想他法或者予以降級處理了。
3.使用 serviceWorker 和 fetch API 代理實現
前端下載更多的需求是因為內容產生於前端。那麼可以在後端實現一個這樣的 API ,它在接收到前端發出的內容後返回下載格式的資料。這種實現就不存在瀏覽器相容問題。
利用 serviceWorker 和 fetch API 截攔瀏覽器請求,只需實現好約定邏輯,也可實現這種功能需求。示例:
在頁面中,通過 fetch API 構造請求:
fetch('lzwme.txt',{ isDownload: true,body: { data: new Blob('hi!') } })
在 serviceWorker 中,截攔附帶 isDownload 頭資訊的請求,構造下載迴應:
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) 方式下載伺服器檔案
以上主要討論的是純前端實現下載儲存檔案的方法。對於下載伺服器檔案,最簡的方式就是 window.open(url) 和 location.href=url 了,但是其的弊端也很明顯,當出錯時整個頁面都會掛掉,而且也無法獲得下載狀態與進度,下載時間稍長時體驗相當不好。
下面介紹一下使用 xhr 和 fetch API 實現檔案下載的方法。其主要思路為:將請求結果設為 Blob 型別,然後採用前端下載儲存 Blob 型別資料的方式實現下載。
4.1 使用 xhr 下載遠端伺服器檔案
程式碼示例:
/** 前端下載/儲存檔案 */ 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 下載遠端伺服器檔案
function fetchDownload(url,options = {}) { options = Object.assign({ credentials: 'include',method: 'get',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; }); }); } // 測試 fetchDownload('https://lzw.me/images/avatar/lzwme-80x80.png',{ // method: 'post',// headers: { // 'Content-Type': 'application/json' // },// body: JSON.stringify({ // pageSize: 100000,// startPage: 0 // }) })
以上就是前端 javascript 實現檔案下載的示例的詳細內容,更多關於JavaScript 檔案下載的資料請關注我們其它相關文章!