解压文件

91

8
“没有成功”需要更具体一些,向我们展示一些代码,一些错误……我们在这里是来帮助你的,而不是去猜测。 - OcuS
基本上我只调用了inflate函数 - data = zip_inflate(src); 但是我认为这个函数是针对单个文件的。如果一个zip文件包含多个目录结构中的文件,那么"data"的内容会是什么呢?我不知道如何使用这个库。 - user69260
7个回答

63

我用Javascript编写了一个解压缩程序,它可以工作。

它依赖于Andy G.P. Na的二进制文件阅读器notmasteryet的一些RFC1951膨胀逻辑。我添加了ZipFile类。

工作示例:
http://cheeso.members.winisp.net/Unzip-Example.htm(链接已失效)

源代码:
http://cheeso.members.winisp.net/srcview.aspx?dir=js-unzip(链接已失效)

NB:链接已失效;我会尽快找到新的主机。

源代码中包括一个ZipFile.htm演示页面和3个不同的脚本,一个用于ZipFile类,一个用于膨胀类,一个用于二进制文件阅读器类。演示还依赖于jQuery和jQuery UI。如果您只下载js-zip.zip文件,则所有必要的源代码都在那里。


以下是Javascript应用程序代码的样子:

// In my demo, this gets attached to a click event.
// it instantiates a ZipFile, and provides a callback that is
// invoked when the zip is read.  This can take a few seconds on a
// large zip file, so it's asynchronous. 
var readFile = function(){
    $("#status").html("<br/>");
    var url= $("#urlToLoad").val();
    var doneReading = function(zip){
        extractEntries(zip);
    };

    var zipFile = new ZipFile(url, doneReading);
};


// this function extracts the entries from an instantiated zip
function extractEntries(zip){
    $('#report').accordion('destroy');

    // clear
    $("#report").html('');

    var extractCb = function(id) {
        // this callback is invoked with the entry name, and entry text
        // in my demo, the text is just injected into an accordion panel.
        return (function(entryName, entryText){
            var content = entryText.replace(new RegExp( "\\n", "g" ), "<br/>");
            $("#"+id).html(content);
            $("#status").append("extract cb, entry(" + entryName + ")  id(" + id + ")<br/>");
            $('#report').accordion('destroy');
            $('#report').accordion({collapsible:true, active:false});
        });
    }

    // for each entry in the zip, extract it. 
    for (var i=0; i<zip.entries.length;  i++) {
        var entry = zip.entries[i];

        var entryInfo = "<h4><a>" + entry.name + "</a></h4>\n<div>";

        // contrive an id for the entry, make it unique
        var randomId = "id-"+ Math.floor((Math.random() * 1000000000));

        entryInfo += "<span class='inputDiv'><h4>Content:</h4><span id='" + randomId +
            "'></span></span></div>\n";

        // insert the info for one entry as the last child within the report div
        $("#report").append(entryInfo);

        // extract asynchronously
        entry.extract(extractCb(randomId));
    }
}

演示分为几个步骤:点击后触发readFile函数,实例化ZipFile对象,读取zip文件。读取完成时有一个异步回调(对于大小合理的zip文件通常在一秒内完成),在此演示中回调保存在doneReading本地变量中,该变量仅调用extractEntries,它会盲目地解压提供的zip文件的所有内容。在真正的应用程序中,您可能会选择提取其中一些条目(允许用户进行选择,或者以编程方式选择一个或多个条目等)。 extractEntries函数遍历所有条目,并在每个条目上调用extract(),并传递一个回调。解压缩条目需要时间,对于zip文件中的每个条目可能需要1秒或更长时间,这意味着异步是适当的。提取回调只需将提取的内容添加到页面上的jQuery手风琴中。如果内容是二进制的,则以此格式化(未显示)。

它能够工作,但我认为其功能有些受限。

首先:它非常慢。从PKWare解压140k的AppNote.txt文件需要大约4秒钟。在.NET程序中可以在不到0.5秒的时间内完成相同的解压缩。 编辑:现在,在IE9和Chrome中,Javascript ZipFile的解包速度比以前快得多。它仍然比编译程序慢,但对于正常的浏览器使用来说已经足够快了。

其次:它不支持流式处理。它基本上将整个zip文件的内容全部读入内存。在“真正”的编程环境中,你可以只读取zip文件的元数据(例如,每个条目64字节)然后根据需要读取和解压缩其他数据。就我所知,在javascript中无法进行此类IO操作,因此唯一的选择是将整个zip文件读入内存并在其中进行随机访问。这意味着对于较大的zip文件,它将对系统内存提出不合理的要求。对于较小的zip文件来说,这不是太大的问题。

此外:它无法处理“通用情况”zip文件-有许多zip选项我没有在解压程序中实现,比如ZIP加密、WinZip加密、zip64等等。(编辑-它现在可以处理UTF-8编码的文件名)。ZipFile类处理了基础知识。其中一些东西不难实现。我在Javascript中有一个AES加密类,可以集成以支持加密。支持Zip64对于大多数Javascript用户来说可能是无用的,因为它旨在支持>4GB的zip文件-不需要在浏览器中提取这些文件。

我也没有测试解压二进制内容的情况。现在它也可以解压二进制文件了。


编辑 - 我更新了JS解压缩库和演示。它现在可以处理二进制文件,除了文本。我使其更加强健和通用 - 现在可以指定读取文本文件时要使用的编码方式。此外,演示也得到了扩展 - 它展示了在浏览器中解压XLSX文件等内容。

因此,尽管我认为它的实用性和兴趣有限,但它确实有效。我想它也可以在Node.js中运行。


1
我有旧版本的一个演示在线,但是我来这里寻找更新。@Cheeso 如果你有时间,会对更新链接感兴趣。 - geocodezip
现在也有一个npm包unzip-stream,它与Node.js流API很好地配合使用。 - Christian Gawron
2
刚刚收到一条标记,称链接已失效。你能再次检查一下,并将代码放在你的SO答案中吗?你有30,000个字符。如果不够,请发布第二个答案。这些链接不断失效没有任何好处。 - Cody Gray
为什么代码不能放在Github上,而不是死链呢?这样似乎更容易些。 - Peter Moore
大多数链接都是失效的链接。 - Ajit Hogade

30

我有一些JSON文件,其中包含一个以zip格式编码的base64编码的JSON字符串。我需要获取内部的JSON对象。Java的InflatorInputStream可以在服务器上解压缩它,因此它实际上是zip格式。但是,当我使用BlobReader将从atob()解码的base64数据传递给zip.js时,我会收到“读取zip文件时出错”的错误消息。从视觉上看,atob()的输出是二进制的,因此BlobReader似乎是正确的,但无论如何尝试TextReader,都会出现“未识别的文件格式”错误。有什么想法吗? - enigment
使用pako的一行代码解决了我的问题: pako.inflate(binaryData, { to: 'string' }) - enigment

22

我发现jszip非常有用。目前我只用它来读取文件,但是它还拥有创建/编辑文件的功能。

代码方面看起来大概是这样的:

var new_zip = new JSZip();
new_zip.load(file);
new_zip.files["doc.xml"].asText() // this give you the text in the file

我注意到的一件事是,似乎文件必须采用二进制流格式(使用FileReader()的.readAsArrayBuffer方法读取),否则会出现错误,说明可能是损坏的zip文件。

编辑:2.x升级到3.0.0指南中的注释:

load()方法和构造函数(new JSZip(data))已被loadAsync()替换。

谢谢user2677034。


1
此方法已在 JSZip 3.0 中删除,请查阅升级指南。 - user2677034

7
如果您需要支持其他格式或仅需要良好的性能,可以使用此WebAssembly库
它是基于Promise的,使用WebWorkers进行线程处理,API实际上是简单的ES模块。
如何使用:
使用npm i libarchive.js进行安装,并将其用作ES模块。
该库由两部分组成:ES模块和WebWorker捆绑包,ES模块部分是您与库交流的接口,像任何其他模块一样使用它。 WebWorker捆绑包位于libarchive.js / dist文件夹中,因此您需要确保它在您的公共文件夹中可用,因为如果您正在使用打包工具(它已经全部打包好了),它不会被捆绑,并指定正确的路径到Archive.init()方法。
import {Archive} from 'libarchive.js/main.js';

Archive.init({
    workerUrl: 'libarchive.js/dist/worker-bundle.js'
});

document.getElementById('file').addEventListener('change', async (e) => {
    const file = e.currentTarget.files[0];

    const archive = await Archive.open(file);
    let obj = await archive.extractFiles();
    
    console.log(obj);
});

// outputs
{
    ".gitignore": {File},
    "addon": {
        "addon.py": {File},
        "addon.xml": {File}
    },
    "README.md": {File}
}

3

2
如果有人从远程服务器上托管的zip文件中读取图像或其他二进制文件,您可以使用以下代码片段使用jszip库下载并创建zip对象。请注意保留html标签。
// this function just get the public url of zip file.
let url = await getStorageUrl(path) 
console.log('public url is', url)
//get the zip file to client
axios.get(url, { responseType: 'arraybuffer' }).then((res) => {
  console.log('zip download status ', res.status)
//load contents into jszip and create an object
  jszip.loadAsync(new Blob([res.data], { type: 'application/zip' })).then((zip) => {
    const zipObj = zip
    $.each(zip.files, function (index, zipEntry) {
    console.log('filename', zipEntry.name)
    })
  })

现在使用zipObj,您可以访问文件并为其创建src URL。
var fname = 'myImage.jpg'
zipObj.file(fname).async('blob').then((blob) => {
var blobUrl = URL.createObjectURL(blob)

2

2023年的答案

自从Chrome在2020年和Safari在2023年开始,您可以使用标准的压缩流API。 https://developer.mozilla.org/en-US/docs/Web/API/Compression_Streams_API

例如,以下是如何解压使用Java的"DeflaterOutputStream"生成的数据:

let DecompressionStream = globalThis["DecompressionStream"];

export async function zipInflate(zip: Uint8Array): Promise<Uint8Array> {
    let ds = new DecompressionStream("deflate");
    let blob = new Blob([zip]);
    const decompressedStream = blob.stream().pipeThrough(ds);
    let blob2 = await new Response(decompressedStream).blob();
    let ab = await blob2.arrayBuffer();
    let arB = new Uint8Array(ab);
    return arB;
}

以上源代码是TypeScript。如果你需要JavaScript,请在冒号后面删除类型说明符。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接