簡體   English   中英

如何使用 JavaScript 壓縮文件?

[英]How to Zip files using JavaScript?

有沒有辦法使用 JavaScript 壓縮文件? 例如,在 Yahoo 郵件中,當您選擇從電子郵件中下載所有附件時,它會被壓縮並下載到單個 zip 文件中。 JavaScript 有能力做到這一點嗎? 如果是這樣,請提供一個編碼示例。

我找到了這個名為jszip的庫來完成這項任務,但它存在已知和未解決的問題。

我該如何解決這個問題?

JSZip 多年來一直在更新。 現在你可以在它的 GitHub repo上找到它

它可以與FileSaver.js一起使用

您可以使用 npm 安裝它們:

npm install jszip --save
npm install file-saver --save

然后導入並使用它們:

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

const zip = new JSZip();
zip.file('idlist.txt', 'PMID:29651880\r\nPMID:29303721');
zip.generateAsync({ type: 'blob' }).then(function (content) {
    FileSaver.saveAs(content, 'download.zip');
});

然后,您將下載一個名為 download.zip 的 zip 文件,解壓后,您可以在一個名為 idlist.txt 的文件中找到它,該文件有兩行:

PMID:29651880
PMID:29303721

供大家參考,我用以下瀏覽器測試過,都通過了:

  • 火狐 59.0.2 (Windows 10)
  • 鉻 65.0.3325.181 (Windows 10)
  • 微軟邊緣 41.16299.371.0 (Windows 10)
  • Internet Explorer 11.0.60 (Windows 10)
  • 歌劇 52 (Mac OSX 10.13)
  • Safari 11 (Mac OSX 10.13)

如果你不關心 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");

與 Yuci 的答案中的用法基本相同,但為 2020 年更新。

通過使用 JSZIP,我們可以在 JavaScript 中生成和下載 zip 文件。 為此,您必須按照以下步驟操作

  1. http://github.com/Stuk/jszip/zipball/master下載 jszip 壓縮文件
  2. 解壓 zip 並在 dist 文件夾中找到 jszip.js 文件
  3. 在您的 html 文件中導入 jszip.js 文件,如下所示

    <script type="text/javascript" src="jszip.js"></script>
  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; }); }
  5. 您可以在此處從我的 git 存儲庫下載示例代碼( GIT 鏈接

我建議直接使用Node 的內置庫Zlib ,其中包括圖像; 使用“緩沖區”以 base 64 編碼。 而不是使用npm包。 原因是:

  • Zlib 是一個 Node 原生庫- 近 10 年來一直保持最新狀態 - 所以那里有長期支持的證明
  • Node 允許您使用緩沖區- 即您可以將文本字符串/圖像轉換為原始二進制數據並使用 Zlib 以這種方式壓縮它
  • 輕松壓縮和解壓縮大文件- 利用節點流以 MB 或 GB 為單位壓縮文件

您使用 jszip 的事實讓我猜測您使用的是 npm 和 node; 假設您已正確設置環境,即全局安裝節點。

示例:將input.txt壓縮成input.txt.gz

const zlib = require('zlib');
const fs = require('fs');
const gzip = zlib.createGzip();
const input = fs.createReadStream('input.txt');
const output = fs.createWriteStream('input.txt.gz');

input.pipe(gzip).pipe(output);

第 1 步:因此,您require來自 node 的每個本機模塊 - require 是 ES5 的一部分。 前面提到的 Zlib 和fs模塊,即文件系統模塊。

const zlib = require('zlib');
const fs = require('fs');

第 2 步: fs模塊,這允許您創建一個readstream ,專門調用來讀取數據塊。 這將返回一個readstream對象; 可讀流

const input = fs.createReadStream(FILE PATH HERE);

__注意:這個 readstream 對象會再次被管道化; readsteam 對象上的這種管道鏈接可以無休止地發生,使管道非常靈活。

ReadStream.pipe(DoesSomething).pipe(SomethingElse).pipe(ConvertToWriteStream)

第 3 步:將經過管道和壓縮的 readstream 對象轉換為 writestream 對象。

const output = fs.createWriteStream('input.txt.gz');

input.pipe(gzip).pipe(output); // returned filename input.txt.gz, within local directory

因此,此庫允許您輕松輸入文件路徑並決定壓縮文件的位置。 如果需要,您也可以選擇反向操作。

我開發了一種使用 JavaScript 生成 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();              
    }
}

然后,創建一個新對象 Zip。

  z=new Zip('myZipFileName');

你可以:

  • 使用 fecth2zip(filesArray,folder) 將目錄中的文件加載到 zip 對象。
  filesArray=[
    'file01.ext',
    'file02.ext',
    'file...'
  ];
  z.fecth2zip(filesArray,'public/');
  • 使用 str2zip(nameFile,content,directory) 從字符串創建一個新文件。
  z.str2zip('test.txt','content','public/teste/');
  • 或者上傳到zip。

    將 onchange 事件放入輸入文件並將文件發送到函數 files2zip(this.files)。

  <input type="file" onchange="z.files2zip(this.files)" value='files' multiple>

將所有對象放入 Zip 對象文件后,只需下載它。

  <input type="button" onclick="z.makeZip()" value='Zip'>

該課程也可以在這里找到: https ://github.com/pwasystem/zip/

使用新的 HTML5 文件 API 和類型化數組,您幾乎可以在 JavaScript 中做任何您想做的事情。 但是,瀏覽器支持不會很好。 我猜這就是您所說的“未解決的問題”。 我建議暫時在服務器上進行。 例如,在 PHP 中,您可以使用此擴展。

這是一個較老的問題,但我在尋找創建 zip 存檔的解決方案時遇到了它。

對於我的用例,我每分鍾從一個非常大的記錄器源在 node.js 中創建數千個 zip 存檔,每個存檔中最多包含 200 個文件。

由於性能問題和內存泄漏,我嘗試了 JSZip,但結果非常糟糕,不值得花時間去追蹤。 由於我的用例相當極端,所以這是壓力測試。

我遇到了另一個值得一提的純 javascript zip 庫,供其他人查看。

我最終使用了fflate

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

這個庫的性能非常好。 我只使用具有 9 級壓縮的 zip 存檔功能,但 fflate 是一個功能齊全的庫,可在服務器端工作並提供完整的瀏覽器支持(2011+)。 我在這里不包括任何示例,因為 fflate 文檔非常完整。 我強烈推薦它作為替代品。

有沒有一種方法可以使用JavaScript壓縮文件? 例如,就像在Yahoo郵件中一樣,當您選擇從電子郵件中下載所有附件時,該附件將被壓縮並下載到一個zip文件中。 JavaScript能夠做到這一點嗎? 如果是這樣,請提供一個編碼示例。

我發現這個名為jszip的庫可以執行此任務,但它存在已知問題和未解決的問題。

我該如何解決這個問題?

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM