本文簡介
點贊 + 關注 + 收藏 = 學會了
本文介紹一個能讓前端優(yōu)雅下載大文件的工具:StreamSaver.js
- ?? StreamSaver.js GitHub地址
- ?? 官方案例
StreamSaver.js
可用于實現(xiàn)在Web瀏覽器中直接將大文件流式傳輸?shù)接脩粼O備的功能。
傳統(tǒng)的下載方式可能導致大文件的加載時間較長或造成內存占用過大的問題,使用 <a>
標簽打開新頁面下載文件,遇到 .txt
或者 .mp4
之類的文件可能就直接在頁面展示了,不會觸發(fā)下載功能。而 StreamSaver.js
則通過流式下載的方式解決了這些問題。
StreamSaver.js
將大文件拆分成小塊,并在下載過程中逐塊傳輸?shù)接脖P,從而降低內存占用和提高下載速度。
環(huán)境準備
要學習 StreamSaver.js
首先要準備一份或者多份可下載的文件。
你可以使用網(wǎng)絡上的文件資源,但這需要你自己去找。
你也可以在自己的電腦運行個服務,把文件資源丟進去即可。
如果你用腳手架創(chuàng)建項目,比如vue或者react之類的項目,也可以把文件放在靜態(tài)資源目錄里。
比如用 vite
創(chuàng)建一個 Vue
項目,然后在 public
目錄下創(chuàng)建一個 test.txt
文件。項目運行起來,在瀏覽器訪問 http://localhost:端口號/public/test.txt
就能查看到這個文件內容。
安裝 StreamSaver.js
可以使用 CDN
或者 npm
安裝 StreamSaver.js
。
本文使用 CDN
的方式講解。
CDN
打開 StreamSaver.js的倉庫。
把 StreamSaver.js
文件下載到你項目里引入即可。
<script src="../StreamSaver.js"></script>
npm
??StreamSaver npm地址
使用以下命令下載 StreamSaver
到項目里
npm i streamsaver
然后在要使用的地方引入即可。
import streamSaver from "streamsaver"
起步
起步階段,我們先試試如何下載一個 .txt
文件。
如果我們要下載一些瀏覽器讀不懂的文件,我們可以使用 <a>
標簽在新窗口打開鏈接,也可以使用 windows.open('url')
的方式打開新窗口進行下載。
但如果這個文件瀏覽器是讀得懂的,比如 .txt
文件,那瀏覽器就不會執(zhí)行下載,而是會直接在頁面中把文件內容展示出來。
此時就可以使用 StreamSaver.js
來解決這個問題。
使用 StreamSaver.js
下載文件的大概流程是這樣的(為了方便理解,我用一些不專業(yè)的術語進行描述):
- 創(chuàng)建一個文件,該文件支持寫入操作。
streamSaver.createWriteStream('文件名.后綴')
。 - 使用
fetch
方法訪問文件的url,將內容一點點的放到StreamSaver
創(chuàng)建的文件里。 - 監(jiān)聽文件內容是否讀取完整,讀取完就執(zhí)行“保存并關閉文件”的操作。
根據(jù)上面的指引編寫代碼:
<!-- 下載按鈕 -->
<button id="download">下載</button>
<!-- 引入StreamSaver.js -->
<script src="../StreamSaver.js"></script>
<script>
// 監(jiān)聽按鈕點擊事件,點擊就下載文件
download.onclick = () => {
// 【步驟1】創(chuàng)建一個文件,該文件支持寫入操作
const fileStream = streamSaver.createWriteStream('test.txt') // 這里傳入的是下載后的文件名,這個名字可以自定義
// 【步驟2】使用 fetch 方法訪問文件的url,將內容一點點的放到 StreamSaver 創(chuàng)建的文件里
fetch('http://localhost:9988/public/test.txt')
.then(res => {
const readableStream = res.body
if (window.WritableStream && readableStream.pipeTo) {
return readableStream.pipeTo(fileStream)
.then(() => console.log('完成寫入'))
}
// 【步驟3】監(jiān)聽文件內容是否讀取完整,讀取完就執(zhí)行“保存并關閉文件”的操作。
window.writer = fileStream.getWriter()
const reader = res.body.getReader()
const pump = () => reader.read()
.then(res => res.done
? writer.close()
: writer.write(res.value).then(pump)
)
pump()
})
}
</script>
大概就是這樣子了。
??提示
如果遇到提示跨域的問題,可以配置 mitm
指向 mitm.html
。
mitm.html
在 StreamSaver.js倉庫
里可以獲取到。
可以把 mitm.html
放到你服務器再配置。
streamSaver.mitm = 'https://你的服務器地址/mitm.html'
打包下載 zip
如果想將多個文件打包成zip下載到本地,可以將 StreamSaver.js
和 zip-stream.js
結合在一起使用。
zip-stream.js
在 StreamSaver.js
的倉庫里有。
zip-stream.js
在 /examples
目錄里。
使用 npm
安裝 streamsaver
也能在 /examples
目錄下找到 zip-stream.js
,直接引入即可。
import 'node_modules/streamsaver/examples/zip-stream.js'
如果使用 CDN
的方式就直接用 <script src='zip-stream.js'></script>
引入即可。
打包zip下載的步驟:
- 創(chuàng)建下載后的文件名和文件格式。
- 使用
zip-stream
創(chuàng)建一個ZIP
實例,用來不斷接收要下載的文件。 - 所有文件下載完成就執(zhí)行
close()
方法將所有文件真正打包成一個zip
。
<button id="download">下載</button>
<script src="../StreamSaver.js"></script>
<script src="zip-stream.js"></script>
<script>
// 要下載的文件地址列表
let urls = [
{
fileName: 'test.txt',
url: 'http://localhost:9988/public/test.txt',
},
{
fileName: 'test.csv',
url: 'http://localhost:9988/public/test.csv',
}
]
download.onclick = () => {
// 【步驟1】
const fileStream = streamSaver.createWriteStream('test.zip')
// 【步驟2】
const readableZipStream = new ZIP({
async pull(ctrl) {
for (let i = 0; i < urls.length; i++) {
const res = await fetch(urls[i].url)
const stream = () => res.body
const name = urls[i].fileName
ctrl.enqueue({ name, stream }) // 不斷接收要下載的文件
}
// 【步驟3】
ctrl.close()
}
})
if (window.WritableStream && readableZipStream.pipeTo) {
return readableZipStream.pipeTo(fileStream).then(
() => console.log('下載完了')
)
}
}
</script>
點擊下載按鈕后的效果:
這個例子準備了 .csv
和 .txt
文件。下載時會合并成 .zip
,解壓后能看到里面的所有文件都是正常能打開的 。
合成文件再下載
在這個例子中,我要將2個 .csv
文件合并成1個再下載。
我準備了兩個 .csv
文件(test1.csv 和 test2.csv),它們的內容分別長這個樣子。
我要將它們合并成這樣子:
在合并文件之前我們首先要清楚這個文件的內容是如何組成的。
在 Excel 中打開 .csv
的每個單元格的內容轉換成文本形式的話是用逗號分隔。
如果要合并多個 .csv
文件,只需監(jiān)聽到每個 .csv
下載完成,然后再拼接一個 \n
換行,再下載下一個 .csv
文件即可。
整理一下就是以下幾個步驟:
- 拿到一組下載地址,把它們轉存到一個迭代器里。
- 遞歸執(zhí)行迭代器,如果迭代器里還有內容,就使用
fetch
請求數(shù)據(jù)。 - 如果迭代器沒內容了,使用
writer.close()
關閉文件寫入。
該功能寫成真正的代碼如下所示:
<button id="download" οnclick="down()">下載</button>
<script src="../StreamSaver.js"></script>
<script>
// 編碼轉換方法
let encode = TextEncoder.prototype.encode.bind(new TextEncoder)
// 準備好要下載的鏈接
const urls = [
'http://localhost:9988/public/test1.csv',
'http://localhost:9988/public/test2.csv'
]
// 迭代器數(shù)據(jù)
let urlsIter = null
// 寫入方法放到全局中保存
let writer = null
// 下載按鈕點擊事件
function down() {
// 創(chuàng)建一個下載管道,并將下載后的文件命名為 newTest.csv
const fileStream = streamSaver.createWriteStream('newTest.csv')
// 創(chuàng)建寫入方法
writer = fileStream.getWriter()
// 將要下載的鏈接轉換成迭代器
urlsIter = urls[Symbol.iterator]()
// 開始執(zhí)行循環(huán)下載
forDown()
}
// 循環(huán)下載的方法
async function forDown() {
// 獲取迭代器最新一條數(shù)據(jù)
let urlIter = urlsIter.next()
// 如果迭代器沒數(shù)據(jù),執(zhí)行寫入完成操作,并停止遞歸
if (urlIter.done) {
writer.close()
return
}
// 迭代器有內容時執(zhí)行請求操作
await fetch(urlIter.value)
.then(res => {
// 通過請求文件url獲取到的數(shù)據(jù)
const readableStream = res.body
if (window.WritableStream && readableStream.pipeTo) {
const reader = readableStream.getReader()
// 講獲取到的每一包寫入文件里
const pump = () => {
return reader.read()
.then(readRes => {
if (readRes.done) { // 當前文件讀取完成后執(zhí)行
// 文件讀取完成后換行
writer.write(encode('\n'))
// 執(zhí)行請求下一個文件
forDown()
} else { // 文件讀取過程執(zhí)行
// 一包包寫入
writer.write(readRes.value)
.then(pump)
}
})
}
pump()
}
})
}
</script>
這個案例稍微復雜一丟丟,建議跟著手敲一遍。
推薦閱讀
除了本文所講內容外,StreamSaver.js
的倉庫還有好幾個實用的例子,有需要的工友可以去看看。
??《前端中文漢字轉拼音》
??《提升日期處理效率:day.js 實戰(zhàn)經(jīng)驗分享》
??《眨個眼就學會了Pixi.js》
??《OpenLayers.js 入門教程:打造互動地圖的入門指南》
??《物理世界的互動之旅:Matter.js入門指南》
??《p5.js 光速入門》文章來源:http://www.zghlxwxcb.cn/news/detail-720284.html
點贊 + 關注 + 收藏 = 學會了 代碼倉庫文章來源地址http://www.zghlxwxcb.cn/news/detail-720284.html
到了這里,關于StreamSaver.js入門教程:優(yōu)雅解決前端下載文件的難題的文章就介紹完了。如果您還想了解更多內容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網(wǎng)!