下载文件并使用Javascript将其压缩在浏览器中

与其生成zip文件并从您的服务器进行传输,不如下载数据并将其压缩在浏览器中呢?

成都创新互联是一家集网站建设,濉溪企业网站建设,濉溪品牌网站建设,网站定制,濉溪网站建设报价,网络营销,网络优化,濉溪网站推广为一体的创新建站企业,帮助传统企业提升企业形象加强企业竞争力。可充分满足这一群体相比中小企业更为丰富、高端、多元的互联网需求。同时我们时刻保持专业、时尚、前沿,时刻以成就客户成长自我,坚持不断学习、思考、沉淀、净化自己,让我们为更多的企业打造出实用型网站。

我最近从事一个副项目,该项目可根据用户的请求生成报告。对于每个请求,我们的后端将生成一个报告,将其上传到Amazon S3存储,然后将其URL返回给客户端。由于生成报告需要一些时间,因此将存储输出文件,并且服务器将通过请求参数来缓存其URL。如果用户订购相同的商品,则后端将返回现有文件的URL。

几天前,我有一个新要求,我需要下载一个包含数百个报告的zip文件,而不是单个文件。我想到的第一个解决方案是:

  • 在服务器上准备压缩文件
  • 上传到Amazon S3存储
  • 给客户端提供下载URL

但是此解决方案有一些缺点:

  • 生成zip文件的逻辑非常复杂。我需要考虑为每个请求生成所有文件,或者在重用现有文件和生成新文件之间进行组合。两种方法似乎都很复杂。他们将花费一些时间来处理,并且稍后需要大量的编码,测试和维护。
  • 它无法利用我已经构建的功能。尽管zip文件是不同的报告集,但很可能大多数单个报告都是由较早的请求生成的。因此,虽然zip文件本身不太可能可重用,但单个文件却可以重用。使用上述方法,我需要一直重做整个过程,这并不是很有效。
  • 生成一个zip文件需要很长时间。由于我的后端是一个单线程进程,因此此操作可能会阻止其他请求一段时间,并且在此期间可能会超时。
  • 在客户端跟踪流程非常困难,我喜欢在网站上放置进度栏。如果一切都在后端处理,我需要找到其他方法向前端报告状态。这并不容易。
  • 我想节省基础设施的成本。如果我们可以将一些计算转移到前端并降低基础架构的成本,那就太好了。我的客户不介意他们再等几秒钟,还是在笔记本电脑上花费额外的MB RAM。

我想出的最终解决方案是:将所有文件下载到浏览器中,然后将其压缩。在这篇文章中,我将介绍如何做。

免责声明:在这篇文章中,我假设你已经具有有关Javascript和Promise的基本知识。如果你没有,我建议你先了解他们,然后再回到这里:)

下载单个文件

在应用新解决方案之前,我的系统允许下载一个报告文件。有很多方法可以做到这一点,后端可以直接通过HTTP请求响应原始文件的内容,也可以将文件上传到另一个存储设备并返回文件URL。我选择第二种方法,因为我想缓存所有生成的文件。

一旦有了文件URL,在客户端上的工作就非常简单:在新选项卡中打开此URL。浏览器将完成剩下的工作以下载文件。

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

下载多个文件并存储在内存中

当下载和压缩多个文件时,我们不能再使用上面的简单方法。

  • 如果一个JS脚本试图同时打开许多链接,浏览器会怀疑它是否是一个安全威胁,并警告用户阻止这些行为。虽然用户可以确认继续,但这不是一个好的体验
  • 你无法控制下载的文件,浏览器管理文件内容和位置

解决此问题的另一种方法是使用 fetch 来下载文件并将数据作为Blob存储在内存中。然后,我们可以将其写入文件或将这些Blob数据合并为zip文件。

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

这个函数返回一个被解析为blob的promise。我们可以结合 Promise.all() 来下载多个文件。Promise.all() 将一次性完成所有的promise,如果所有的子promise都被解析,或者其中一个承诺出现错误,则进行解析。

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

按X文件组下载

但是,如果我们需要一次下载大量文件怎么办?假设有1000个文件?使用 Promise.all() 可能不再是一个好主意,你的代码将一次发送一千个请求。 这种方法有很多问题:

  • 操作系统和浏览器支持的并发连接数是有限的。因此,浏览器一次只能处理几个请求。其他请求放入队列,并且超时计数。结果是,你的大多数请求在发送之前都会超时。
  • 一次发送大量请求也会使后端过载

我考虑过的解决方案是将文件分成多个组。假设我有1000个文件可供下载。而不是通过 Promise.all() 立即开始一次下载所有文件,我将每次下载5个文件。在完成这5个之后,我将开始另一个包,我总共会下载250个包。

要实现这个功能,我们可以做一个自定义逻辑。或者我建议一个更简单的方法,就是利用第三方库bluebirdjs。该库实现了许多有用的Promise函数。在这个用例中,我将使用 Promise.map()。注意这里的Promise现在是库提供的自定义Promise,而不是内置的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.   );
  10. }

通过上面的实现,该函数将接收一个URL数组并开始下载所有URL,每次都具有最大 files_per_group。该函数返回一个Promise,它将在下载所有URL时解析,并在其中任何一个失败时拒绝。

创建zip文件

现在我已经把所有的内容都下载到内存中了。正如我上面提到的,下载的内容被存储为Blob。下一步是使用这些Blob数据创建一个压缩文件。

 
 
 
  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.   });
  13. }

最终代码

让我们在这里完成我为此完成的所有代码。

 
 
 
  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. }
  16. const exportZip = blobs => {
  17.   const zip = JsZip();
  18.   blobs.forEach((blob, i) => {
  19.     zip.file(`file-${i}.csv`, blob);
  20.   });
  21.   zip.generateAsync({type: 'blob'}).then(zipFile => {
  22.     const currentDate = new Date().getTime();
  23.     const fileName = `combined-${currentDate}.zip`;
  24.     return FileSaver.saveAs(zipFile, fileName);
  25.   });
  26. }
  27. const downloadAndZip = urls => {
  28.   return downloadByGroup(urls, 5).then(exportZip);
  29. }

总结

利用客户端的功能有时对于减少后端的工作量和复杂性非常有用。

不要一次发送大量的请求。你会在前端和后端都遇到麻烦。相反,将作品分成小块。

介绍一些第三方库bluebird,jszip和file-saver。他们为我工作得很好,也可能对您有帮助。

分享标题:下载文件并使用Javascript将其压缩在浏览器中
本文地址:http://www.csdahua.cn/qtweb/news11/261311.html

网站建设、网络推广公司-快上网,是专注品牌与效果的网站制作,网络营销seo公司;服务项目有等

广告

声明:本网站发布的内容(图片、视频和文字)以用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。电话:028-86922220;邮箱:631063699@qq.com。内容未经允许不得转载,或转载时需注明来源: 快上网