如何使用JavaScript压缩文件

Isu*_*uru 15 javascript zip zipfile

有没有办法使用JavaScript压缩文件?例如,在Yahoo邮件中,当您选择从电子邮件下载所有附件时,它会被压缩并下载到单个zip文件中.JavaScript能够做到这一点吗?如果是,请提供编码示例.

我发现这个名为jszip的库来执行任务,但它已知并且未解决的问题.

谢谢.

Yuc*_*uci 10

JSZip已经更新了多年。现在您可以在其GitHub存储库中找到它

它可以与FileSaver.js一起使用

您可以使用npm安装它们:

npm install jszip --save
npm install file-saver --save
Run Code Online (Sandbox Code Playgroud)

然后导入并使用它们:

import JSZip from 'jszip';
import FileSaver from 'file-saver';

let zip = new JSZip();
zip.file("idlist.txt", `PMID:29651880\r\nPMID:29303721`);
zip.generateAsync({type: "blob"}).then(function(content) {
  FileSaver.saveAs(content, "download.zip");
});
Run Code Online (Sandbox Code Playgroud)

然后,一旦解压缩,您将下载一个名为download.zip的zip文件,并且可以在名为idlist.txt的文件中找到该文件,该文件有两行:

PMID:29651880
PMID:29303721
Run Code Online (Sandbox Code Playgroud)

供您参考,我使用以下浏览器进行了测试,并全部通过:

  • Firefox 59.0.2(Windows 10)
  • Chrome 65.0.3325.181(Windows 10)
  • Microsoft Edge 41.16299.371.0(Windows 10)
  • Internet Explorer 11.0.60(Windows 10)
  • Opera 52(Mac OSX 10.13)
  • Safari 11(Mac OSX 10.13)


小智 9

我开发了一种使用 JavaScript 生成 zip 文件的新解决方案。该解决方案属于公共领域。

Zip 类。

Zip {

    constructor(name) {
        this.name = name;
        this.zip = new Array();
        this.file = new Array();
    }
    
    dec2bin=(dec,size)=>dec.toString(2).padStart(size,'0');
    str2dec=str=>Array.from(new TextEncoder().encode(str));
    str2hex=str=>[...new TextEncoder().encode(str)].map(x=>x.toString(16).padStart(2,'0'));
    hex2buf=hex=>new Uint8Array(hex.split(' ').map(x=>parseInt(x,16)));
    bin2hex=bin=>(parseInt(bin.slice(8),2).toString(16).padStart(2,'0')+' '+parseInt(bin.slice(0,8),2).toString(16).padStart(2,'0'));
    
    reverse=hex=>{
        let hexArray=new Array();
        for(let i=0;i<hex.length;i=i+2)hexArray[i]=hex[i]+''+hex[i+1];
        return hexArray.filter((a)=>a).reverse().join(' '); 
    }
    
    crc32=r=>{
        for(var a,o=[],c=0;c<256;c++){
            a=c;
            for(var f=0;f<8;f++)a=1&a?3988292384^a>>>1:a>>>1;
            o[c]=a;
        }
        for(var n=-1,t=0;t<r.length;t++)n=n>>>8^o[255&(n^r[t])];
        return this.reverse(((-1^n)>>>0).toString(16).padStart(8,'0'));
    }
    
    fecth2zip(filesArray,folder=''){
        filesArray.forEach(fileUrl=>{
            let resp;               
            fetch(fileUrl).then(response=>{
                resp=response;
                return response.arrayBuffer();
            }).then(blob=>{
                new Response(blob).arrayBuffer().then(buffer=>{
                    console.log(`File: ${fileUrl} load`);
                    let uint=[...new Uint8Array(buffer)];
                    uint.modTime=resp.headers.get('Last-Modified');
                    uint.fileUrl=`${this.name}/${folder}${fileUrl}`;                            
                    this.zip[fileUrl]=uint;
                });
            });             
        });
    }
    
    str2zip(name,str,folder){
        let uint=[...new Uint8Array(this.str2dec(str))];
        uint.name=name;
        uint.modTime=new Date();
        uint.fileUrl=`${this.name}/${folder}${name}`;
        this.zip[uint.fileUrl]=uint;
    }
    
    files2zip(files,folder){
        for(let i=0;i<files.length;i++){
            files[i].arrayBuffer().then(data=>{
                let uint=[...new Uint8Array(data)];
                uint.name=files[i].name;
                uint.modTime=files[i].lastModifiedDate;
                uint.fileUrl=`${this.name}/${folder}${files[i].name}`;
                this.zip[uint.fileUrl]=uint;                            
            });
        }
    }
    
    makeZip(){
        let count=0;
        let fileHeader='';
        let centralDirectoryFileHeader='';
        let directoryInit=0;
        let offSetLocalHeader='00 00 00 00';
        let zip=this.zip;
        for(const name in zip){
            let modTime=()=>{
                lastMod=new Date(zip[name].modTime);
                hour=this.dec2bin(lastMod.getHours(),5);
                minutes=this.dec2bin(lastMod.getMinutes(),6);
                seconds=this.dec2bin(Math.round(lastMod.getSeconds()/2),5);
                year=this.dec2bin(lastMod.getFullYear()-1980,7);
                month=this.dec2bin(lastMod.getMonth()+1,4);
                day=this.dec2bin(lastMod.getDate(),5);                      
                return this.bin2hex(`${hour}${minutes}${seconds}`)+' '+this.bin2hex(`${year}${month}${day}`);
            }                   
            let crc=this.crc32(zip[name]);
            let size=this.reverse(parseInt(zip[name].length).toString(16).padStart(8,'0'));
            let nameFile=this.str2hex(zip[name].fileUrl).join(' ');
            let nameSize=this.reverse(zip[name].fileUrl.length.toString(16).padStart(4,'0'));
            let fileHeader=`50 4B 03 04 14 00 00 00 00 00 ${modTime} ${crc} ${size} ${size} ${nameSize} 00 00 ${nameFile}`;
            let fileHeaderBuffer=this.hex2buf(fileHeader);
            directoryInit=directoryInit+fileHeaderBuffer.length+zip[name].length;
            centralDirectoryFileHeader=`${centralDirectoryFileHeader}50 4B 01 02 14 00 14 00 00 00 00 00 ${modTime} ${crc} ${size} ${size} ${nameSize} 00 00 00 00 00 00 01 00 20 00 00 00 ${offSetLocalHeader} ${nameFile} `;
            offSetLocalHeader=this.reverse(directoryInit.toString(16).padStart(8,'0'));
            this.file.push(fileHeaderBuffer,new Uint8Array(zip[name]));
            count++;
        }
        centralDirectoryFileHeader=centralDirectoryFileHeader.trim();
        let entries=this.reverse(count.toString(16).padStart(4,'0'));
        let dirSize=this.reverse(centralDirectoryFileHeader.split(' ').length.toString(16).padStart(8,'0'));
        let dirInit=this.reverse(directoryInit.toString(16).padStart(8,'0'));
        let centralDirectory=`50 4b 05 06 00 00 00 00 ${entries} ${entries} ${dirSize} ${dirInit} 00 00`;
        this.file.push(this.hex2buf(centralDirectoryFileHeader),this.hex2buf(centralDirectory));                
        let a = document.createElement('a');
        a.href = URL.createObjectURL(new Blob([...this.file],{type:'application/octet-stream'}));
        a.download = `${this.name}.zip`;
        a.click();              
    }
}
Run Code Online (Sandbox Code Playgroud)

然后,创建一个新对象 Zip。

  z=new Zip('myZipFileName');
Run Code Online (Sandbox Code Playgroud)

你你可以:

  • 使用fecth2zip(filesArray,folder) 将目录中的文件加载到zip 对象。
  filesArray=[
    'file01.ext',
    'file02.ext',
    'file...'
  ];
  z.fecth2zip(filesArray,'public/');
Run Code Online (Sandbox Code Playgroud)
  • 使用 str2zip(nameFile,content,directory) 从字符串创建一个新文件。
  z.str2zip('test.txt','content','public/teste/');
Run Code Online (Sandbox Code Playgroud)
  • 或者上传到zip。

    将 onchange 事件放入输入文件并将文件发送到函数 files2zip(this.files)。

  <input type="file" onchange="z.files2zip(this.files)" value='files' multiple>
Run Code Online (Sandbox Code Playgroud)

将所有对象放入 Zip 对象文件后,只需下载它即可。

  <input type="button" onclick="z.makeZip()" value='Zip'>
Run Code Online (Sandbox Code Playgroud)

该类也可以在这里找到: https: //github.com/pwasystem/zip/


Tou*_*ffy 7

如果你不关心 IE,client-zip比 JSZip 更快、更小,并且旨在解决这个问题(是的,这是一个无耻但与我的库完全相关的插件)。

你会做这样的事情(例如,其中files可能是一个fetch响应数组,尽管有许多支持的输入):

import { downloadZip } from "client-zip/index.js"
import FileSaver from "file-saver"

const content = await downloadZip(files).blob()
FileSaver.saveAs(content, "download.zip");
Run Code Online (Sandbox Code Playgroud)

基本上与 Yuci 的答案中的用法相同,但已更新至 2020 年。

  • 这不一定是坏事。事实上,许多浏览器会自动解压缩下载的存档,因此压缩只会增加 CPU 负载(用于压缩,然后再次解压缩),而根本不会节省带宽或存储空间。这里压缩的主要原因似乎是为了实现多个附件的一键下载。 (7认同)
  • 但 client-zip 不压缩数据 (4认同)
  • 并没有说这是一件坏事。但你提到“正是这个问题”,听起来它应该做与 JSZip 相同的事情。Client-zip 似乎很棒,可以实现它所建议的功能。 (4认同)
  • 好的。需要明确的是:我并不声称 client-zip 解决了与 JSZip 相同的一组问题,而只是解决了这个问题中的问题。 (2认同)

San*_*raj 6

通过使用 JSZIP,我们可以在 JavaScript 中生成和下载 zip 文件。为此,您必须按照以下步骤操作

  1. http://github.com/Stuk/jszip/zipball/master下载 jszip zip 文件
  2. 解压 zip 并在 dist 文件夹中找到 jszip.js 文件
  3. 在您的 html 文件中导入 jszip.js 文件,如下所示

    <script type="text/javascript" src="jszip.js"></script>
    
    Run Code Online (Sandbox Code Playgroud)
  4. 在您的代码中添加以下函数并调用它

    onClickDownload: function () {
        var zip = new JSZip();
        for (var i = 0; i < 5; i++) {
            var txt = 'hello';
            zip.file("file" + i + ".txt", txt);
        }
        zip.generateAsync({
            type: "base64"
        }).then(function(content) {
            window.location.href = "data:application/zip;base64," + content;
        });       
    }
    
    Run Code Online (Sandbox Code Playgroud)
  5. 你可以从我的 git 存储库下载示例代码(GIT 链接

  • 以 Base64 生成输出是一个非常糟糕的主意,使用这样的数据 URI 更是一个更糟糕的主意。浪费内存和CPU。请改用 blob URL。 (2认同)

Mar*_*rkM 5

这是一个较旧的问题,但我遇到它寻找创建 zip 存档的解决方案。

对于我的用例,我每分钟都会从一个非常大的记录器源在 node.js 中创建数千个 zip 存档,每个存档中最多包含 200 个文件。

我曾尝试过 JSZip,但由于性能问题和内存泄漏,结果非常差,不值得花时间去追踪。由于我的用例相当极端,因此这是一个相当大的压力测试。

我遇到了另一个值得一提的纯 JavaScript zip 库,供其他人查看。

我最终使用了fflate

https://github.com/101arrowz/fflate

这个库的性能非常好。我只使用 9 级压缩的 zip 存档功能,但 fflate 是一个功能齐全的库,可在服务器端运行并提供完整的浏览器支持(2011+)。我在这里不包含任何示例,因为 fflate 文档非常完整。我强烈推荐它作为替代方案。


Ale*_*pin 1

借助新的 HTML5 文件 API 和类型化数组,您几乎可以在 JavaScript 中做任何您想做的事情。然而,浏览器支持不会很好。我猜这就是你所说的“未解决的问题”的意思。我暂时建议在服务器上执行此操作。例如,在 PHP 中,您可以使用此扩展。