自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

下載文件并使用Javascript將其壓縮在瀏覽器中

開發(fā) 前端
與其生成zip文件并從您的服務(wù)器進(jìn)行傳輸,不如下載數(shù)據(jù)并將其壓縮在瀏覽器中呢?

與其生成zip文件并從您的服務(wù)器進(jìn)行傳輸,不如下載數(shù)據(jù)并將其壓縮在瀏覽器中呢?

下載文件并使用Javascript將其壓縮在瀏覽器中

我最近從事一個副項目,該項目可根據(jù)用戶的請求生成報告。對于每個請求,我們的后端將生成一個報告,將其上傳到Amazon S3存儲,然后將其URL返回給客戶端。由于生成報告需要一些時間,因此將存儲輸出文件,并且服務(wù)器將通過請求參數(shù)來緩存其URL。如果用戶訂購相同的商品,則后端將返回現(xiàn)有文件的URL。

幾天前,我有一個新要求,我需要下載一個包含數(shù)百個報告的zip文件,而不是單個文件。我想到的第一個解決方案是:

  • 在服務(wù)器上準(zhǔn)備壓縮文件
  • 上傳到Amazon S3存儲
  • 給客戶端提供下載URL

但是此解決方案有一些缺點:

  • 生成zip文件的邏輯非常復(fù)雜。我需要考慮為每個請求生成所有文件,或者在重用現(xiàn)有文件和生成新文件之間進(jìn)行組合。兩種方法似乎都很復(fù)雜。他們將花費一些時間來處理,并且稍后需要大量的編碼,測試和維護(hù)。
  • 它無法利用我已經(jīng)構(gòu)建的功能。盡管zip文件是不同的報告集,但很可能大多數(shù)單個報告都是由較早的請求生成的。因此,雖然zip文件本身不太可能可重用,但單個文件卻可以重用。使用上述方法,我需要一直重做整個過程,這并不是很有效。
  • 生成一個zip文件需要很長時間。由于我的后端是一個單線程進(jìn)程,因此此操作可能會阻止其他請求一段時間,并且在此期間可能會超時。
  • 在客戶端跟蹤流程非常困難,我喜歡在網(wǎng)站上放置進(jìn)度欄。如果一切都在后端處理,我需要找到其他方法向前端報告狀態(tài)。這并不容易。
  • 我想節(jié)省基礎(chǔ)設(shè)施的成本。如果我們可以將一些計算轉(zhuǎn)移到前端并降低基礎(chǔ)架構(gòu)的成本,那就太好了。我的客戶不介意他們再等幾秒鐘,還是在筆記本電腦上花費額外的MB RAM。

我想出的最終解決方案是:將所有文件下載到瀏覽器中,然后將其壓縮。在這篇文章中,我將介紹如何做。

免責(zé)聲明:在這篇文章中,我假設(shè)你已經(jīng)具有有關(guān)Javascript和Promise的基本知識。如果你沒有,我建議你先了解他們,然后再回到這里:)

下載單個文件

在應(yīng)用新解決方案之前,我的系統(tǒng)允許下載一個報告文件。有很多方法可以做到這一點,后端可以直接通過HTTP請求響應(yīng)原始文件的內(nèi)容,也可以將文件上傳到另一個存儲設(shè)備并返回文件URL。我選擇第二種方法,因為我想緩存所有生成的文件。

一旦有了文件URL,在客戶端上的工作就非常簡單:在新選項卡中打開此URL。瀏覽器將完成剩下的工作以下載文件。

  1. const downloadViaBrowser = url => { 
  2.     window.open(url, ‘_blank’); 

下載多個文件并存儲在內(nèi)存中

當(dāng)下載和壓縮多個文件時,我們不能再使用上面的簡單方法。

  • 如果一個JS腳本試圖同時打開許多鏈接,瀏覽器會懷疑它是否是一個安全威脅,并警告用戶阻止這些行為。雖然用戶可以確認(rèn)繼續(xù),但這不是一個好的體驗
  • 你無法控制下載的文件,瀏覽器管理文件內(nèi)容和位置

解決此問題的另一種方法是使用 fetch 來下載文件并將數(shù)據(jù)作為Blob存儲在內(nèi)存中。然后,我們可以將其寫入文件或?qū)⑦@些Blob數(shù)據(jù)合并為zip文件。

  1. const download = url => { 
  2.   return fetch(url).then(resp => resp.blob()); 
  3. }; 

這個函數(shù)返回一個被解析為blob的promise。我們可以結(jié)合 Promise.all() 來下載多個文件。Promise.all() 將一次性完成所有的promise,如果所有的子promise都被解析,或者其中一個承諾出現(xiàn)錯誤,則進(jìn)行解析。

  1. const downloadMany = urls => { 
  2.   return Promise.all(urls.map(url => download(url)) 

按X文件組下載

但是,如果我們需要一次下載大量文件怎么辦?假設(shè)有1000個文件?使用 Promise.all() 可能不再是一個好主意,你的代碼將一次發(fā)送一千個請求。 這種方法有很多問題:

  • 操作系統(tǒng)和瀏覽器支持的并發(fā)連接數(shù)是有限的。因此,瀏覽器一次只能處理幾個請求。其他請求放入隊列,并且超時計數(shù)。結(jié)果是,你的大多數(shù)請求在發(fā)送之前都會超時。
  • 一次發(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。

  1. import Promise from 'bluebird'; 
  2. const downloadByGroup = (urls, files_per_group=5) => { 
  3.   return Promise.map( 
  4.     urls,  
  5.     async url => { 
  6.       return await download(url); 
  7.     }, 
  8.     {concurrency: files_per_group} 
  9.   ); 

通過上面的實現(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)建一個壓縮文件。

  1. import JsZip from 'jszip'; 
  2. import FileSaver from 'file-saver'; 
  3. const exportZip = blobs => { 
  4.   const zip = JsZip(); 
  5.   blobs.forEach((blob, i) => { 
  6.     zip.file(`file-${i}.csv`, blob); 
  7.   }); 
  8.   zip.generateAsync({type: 'blob'}).then(zipFile => { 
  9.     const currentDate = new Date().getTime(); 
  10.     const fileName = `combined-${currentDate}.zip`; 
  11.     return FileSaver.saveAs(zipFile, fileName); 
  12.   }); 

最終代碼

讓我們在這里完成我為此完成的所有代碼。

  1. import Promise from 'bluebird'; 
  2. import JsZip from 'jszip'; 
  3. import FileSaver from 'file-saver'; 
  4. const download = url => { 
  5.   return fetch(url).then(resp => resp.blob()); 
  6. }; 
  7. const downloadByGroup = (urls, files_per_group=5) => { 
  8.   return Promise.map( 
  9.     urls,  
  10.     async url => { 
  11.       return await download(url); 
  12.     }, 
  13.     {concurrency: files_per_group} 
  14.   ); 
  15. const exportZip = blobs => { 
  16.   const zip = JsZip(); 
  17.   blobs.forEach((blob, i) => { 
  18.     zip.file(`file-${i}.csv`, blob); 
  19.   }); 
  20.   zip.generateAsync({type: 'blob'}).then(zipFile => { 
  21.     const currentDate = new Date().getTime(); 
  22.     const fileName = `combined-${currentDate}.zip`; 
  23.     return FileSaver.saveAs(zipFile, fileName); 
  24.   }); 
  25. const downloadAndZip = urls => { 
  26.   return downloadByGroup(urls, 5).then(exportZip); 

總結(jié)

利用客戶端的功能有時對于減少后端的工作量和復(fù)雜性非常有用。

不要一次發(fā)送大量的請求。你會在前端和后端都遇到麻煩。相反,將作品分成小塊。

介紹一些第三方庫bluebird,jszip和file-saver。他們?yōu)槲夜ぷ鞯煤芎茫部赡軐δ袔椭?/p>

 

責(zé)任編輯:趙寧寧 來源: 今日頭條
相關(guān)推薦

2021-11-15 23:37:39

Windows 11Windows微軟

2017-01-05 09:07:25

JavaScript瀏覽器驅(qū)動

2022-07-07 07:22:01

瀏覽器JavaScript工具

2016-10-09 08:38:01

JavaScript瀏覽器事件

2021-04-15 16:11:51

微軟瀏覽器Windows 10

2010-09-15 09:12:03

JavaScript瀏覽器兼容

2022-07-18 08:48:06

HtmxHTML

2020-03-12 11:29:51

JavaScript瀏覽器語言

2020-05-28 08:33:28

JavaScript開發(fā)技術(shù)

2012-03-19 17:25:22

2012-03-20 11:41:18

海豚瀏覽器

2012-03-20 11:31:58

移動瀏覽器

2011-10-11 10:29:14

linux

2016-12-22 16:18:33

微軟Edge瀏覽器谷歌

2012-03-20 11:07:08

2013-08-22 10:09:00

2011-06-10 16:44:17

Qt 瀏覽器

2011-04-12 16:51:29

Javascript兼容性

2010-08-11 13:35:10

JavaScriptCSS

2010-09-15 09:43:24

Javascript瀏覽器兼容
點贊
收藏

51CTO技術(shù)棧公眾號