掃二維碼與項目經(jīng)理溝通
我們在微信上24小時期待你的聲音
解答本文疑問/技術(shù)咨詢/運營咨詢/技術(shù)建議/互聯(lián)網(wǎng)交流
與其生成zip文件并從您的服務(wù)器進行傳輸,不如下載數(shù)據(jù)并將其壓縮在瀏覽器中呢?

成都創(chuàng)新互聯(lián)是一家集網(wǎng)站建設(shè),濉溪企業(yè)網(wǎng)站建設(shè),濉溪品牌網(wǎng)站建設(shè),網(wǎng)站定制,濉溪網(wǎng)站建設(shè)報價,網(wǎng)絡(luò)營銷,網(wǎng)絡(luò)優(yōu)化,濉溪網(wǎng)站推廣為一體的創(chuàng)新建站企業(yè),幫助傳統(tǒng)企業(yè)提升企業(yè)形象加強企業(yè)競爭力??沙浞譂M足這一群體相比中小企業(yè)更為豐富、高端、多元的互聯(lián)網(wǎng)需求。同時我們時刻保持專業(yè)、時尚、前沿,時刻以成就客戶成長自我,堅持不斷學(xué)習(xí)、思考、沉淀、凈化自己,讓我們?yōu)楦嗟钠髽I(yè)打造出實用型網(wǎng)站。
我最近從事一個副項目,該項目可根據(jù)用戶的請求生成報告。對于每個請求,我們的后端將生成一個報告,將其上傳到Amazon S3存儲,然后將其URL返回給客戶端。由于生成報告需要一些時間,因此將存儲輸出文件,并且服務(wù)器將通過請求參數(shù)來緩存其URL。如果用戶訂購相同的商品,則后端將返回現(xiàn)有文件的URL。
幾天前,我有一個新要求,我需要下載一個包含數(shù)百個報告的zip文件,而不是單個文件。我想到的第一個解決方案是:
但是此解決方案有一些缺點:
我想出的最終解決方案是:將所有文件下載到瀏覽器中,然后將其壓縮。在這篇文章中,我將介紹如何做。
免責聲明:在這篇文章中,我假設(shè)你已經(jīng)具有有關(guān)Javascript和Promise的基本知識。如果你沒有,我建議你先了解他們,然后再回到這里:)
下載單個文件
在應(yīng)用新解決方案之前,我的系統(tǒng)允許下載一個報告文件。有很多方法可以做到這一點,后端可以直接通過HTTP請求響應(yīng)原始文件的內(nèi)容,也可以將文件上傳到另一個存儲設(shè)備并返回文件URL。我選擇第二種方法,因為我想緩存所有生成的文件。
一旦有了文件URL,在客戶端上的工作就非常簡單:在新選項卡中打開此URL。瀏覽器將完成剩下的工作以下載文件。
- const downloadViaBrowser = url => {
- window.open(url, ‘_blank’);
- }
下載多個文件并存儲在內(nèi)存中
當下載和壓縮多個文件時,我們不能再使用上面的簡單方法。
解決此問題的另一種方法是使用 fetch 來下載文件并將數(shù)據(jù)作為Blob存儲在內(nèi)存中。然后,我們可以將其寫入文件或?qū)⑦@些Blob數(shù)據(jù)合并為zip文件。
- const download = url => {
- return fetch(url).then(resp => resp.blob());
- };
這個函數(shù)返回一個被解析為blob的promise。我們可以結(jié)合 Promise.all() 來下載多個文件。Promise.all() 將一次性完成所有的promise,如果所有的子promise都被解析,或者其中一個承諾出現(xiàn)錯誤,則進行解析。
- const downloadMany = urls => {
- return Promise.all(urls.map(url => download(url))
- }
按X文件組下載
但是,如果我們需要一次下載大量文件怎么辦?假設(shè)有1000個文件?使用 Promise.all() 可能不再是一個好主意,你的代碼將一次發(fā)送一千個請求。 這種方法有很多問題:
我考慮過的解決方案是將文件分成多個組。假設(shè)我有1000個文件可供下載。而不是通過 Promise.all() 立即開始一次下載所有文件,我將每次下載5個文件。在完成這5個之后,我將開始另一個包,我總共會下載250個包。
要實現(xiàn)這個功能,我們可以做一個自定義邏輯?;蛘呶医ㄗh一個更簡單的方法,就是利用第三方庫bluebirdjs。該庫實現(xiàn)了許多有用的Promise函數(shù)。在這個用例中,我將使用 Promise.map()。注意這里的Promise現(xiàn)在是庫提供的自定義Promise,而不是內(nèi)置的Promise。
- import Promise from 'bluebird';
- const downloadByGroup = (urls, files_per_group=5) => {
- return Promise.map(
- urls,
- async url => {
- return await download(url);
- },
- {concurrency: files_per_group}
- );
- }
通過上面的實現(xiàn),該函數(shù)將接收一個URL數(shù)組并開始下載所有URL,每次都具有最大 files_per_group。該函數(shù)返回一個Promise,它將在下載所有URL時解析,并在其中任何一個失敗時拒絕。
創(chuàng)建zip文件
現(xiàn)在我已經(jīng)把所有的內(nèi)容都下載到內(nèi)存中了。正如我上面提到的,下載的內(nèi)容被存儲為Blob。下一步是使用這些Blob數(shù)據(jù)創(chuàng)建一個壓縮文件。
- import JsZip from 'jszip';
- import FileSaver from 'file-saver';
- const exportZip = blobs => {
- const zip = JsZip();
- blobs.forEach((blob, i) => {
- zip.file(`file-${i}.csv`, blob);
- });
- zip.generateAsync({type: 'blob'}).then(zipFile => {
- const currentDate = new Date().getTime();
- const fileName = `combined-${currentDate}.zip`;
- return FileSaver.saveAs(zipFile, fileName);
- });
- }
最終代碼
讓我們在這里完成我為此完成的所有代碼。
- import Promise from 'bluebird';
- import JsZip from 'jszip';
- import FileSaver from 'file-saver';
- const download = url => {
- return fetch(url).then(resp => resp.blob());
- };
- const downloadByGroup = (urls, files_per_group=5) => {
- return Promise.map(
- urls,
- async url => {
- return await download(url);
- },
- {concurrency: files_per_group}
- );
- }
- const exportZip = blobs => {
- const zip = JsZip();
- blobs.forEach((blob, i) => {
- zip.file(`file-${i}.csv`, blob);
- });
- zip.generateAsync({type: 'blob'}).then(zipFile => {
- const currentDate = new Date().getTime();
- const fileName = `combined-${currentDate}.zip`;
- return FileSaver.saveAs(zipFile, fileName);
- });
- }
- const downloadAndZip = urls => {
- return downloadByGroup(urls, 5).then(exportZip);
- }
總結(jié)
利用客戶端的功能有時對于減少后端的工作量和復(fù)雜性非常有用。
不要一次發(fā)送大量的請求。你會在前端和后端都遇到麻煩。相反,將作品分成小塊。
介紹一些第三方庫bluebird,jszip和file-saver。他們?yōu)槲夜ぷ鞯煤芎茫部赡軐δ袔椭?/p>
網(wǎng)站名稱:下載文件并使用Javascript將其壓縮在瀏覽器中
文章路徑:http://uogjgqi.cn/article/dhesjcj.html

我們在微信上24小時期待你的聲音
解答本文疑問/技術(shù)咨詢/運營咨詢/技術(shù)建議/互聯(lián)網(wǎng)交流