1. 程式人生 > 程式設計 >前端 javascript 實現檔案下載的示例

前端 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 檔案下載的資料請關注我們其它相關文章!