在JS中重建解压缩的zip文件的文件/文件夹结构

时间:2018-05-30 09:00:04

标签: javascript compression arraybuffer filelist pako

我正在尝试使用JavaScript在浏览器中重建解压缩的zip文件的文件/文件夹结构。理想情况下,我希望将所有文件都放在FileList中 (好像它们刚刚通过网页上传)或其他可迭代对象。例如,包含

的压缩文件夹
folder/file1
folder/file2
someotherfile

应该重建为一个FileList / iterable对象,其中每个项目对应于包中的一个文件(据我所知,没有办法在JS中保留文件夹结构)。

我已经非常成功地阅读了tar.gz文件并使用pako使用此问题底部的代码对其进行解压缩。但是,pako的结果是一个大型ArrayBuffer(下面代码中的inflator.result),在尝试重建原始文件和文件夹时,我无法做到这一点。我碰到了以下问题:

  1. 如何知道一个文件的结束位置以及另一个文件从ArrayBuffer开始?
  2. 如何确定当前文件的原始文件类型?
  3. 一旦我知道这一点,我应该能够将ArrayBuffer数据转换为带有

    的文件
    File(segment, {type: filetype})
    

    搜索网络也没有提供任何有用的信息。有没有人知道如何解决这个问题呢?

    以下是我用来解压缩zipfile的代码。

    import pako from 'pako';
    import isFunction from 'lodash/isFunction'
    
    class FileStreamer {
      constructor(file, chunkSize = 64 * 1024) {
        this.file = file;
        this.offset = 0;
        this.chunkSize = chunkSize; // bytes
        this.rewind();
      }
      rewind() {
        this.offset = 0;
      }
      isEndOfFile() {
        return this.offset >= this.getFileSize();
      }
      readBlock() {
        const fileReader = new FileReader();
        const blob = this.file.slice(this.offset, this.offset + this.chunkSize);
    
        return new Promise((resolve, reject) => {
          fileReader.onloadend = (event) => {
            const target = (event.target);
            if (target.error) {
              return reject(target.error);
            }
    
            this.offset += target.result.byteLength;
    
            resolve({
              data: target.result,
              progress: Math.min(this.offset / this.file.size, 1)
            });
          };
    
          fileReader.readAsArrayBuffer(blob);
        });
      }
      getFileSize() {
        return this.file.size;
      }
    }
    
    export async function decompress(zipfile, onProgress) {
      const fs = new FileStreamer(zipfile);
      const inflator = new pako.Inflate();
      let block;
    
      while (!fs.isEndOfFile()) {
        block = await fs.readBlock();
        inflator.push(block.data, fs.isEndOfFile());
        if (inflator.err) {
          throw inflator.err
        }
        if (isFunction(onProgress)) onProgress(block.progress)
      }
    
      return inflator.result;
    }
    

2 个答案:

答案 0 :(得分:1)

.tar.gz文件是一个tar文件('Tape ARchive' - 因为最初捆绑文件用于存储在磁带上是它的主要目的),然后被压缩。您可以获得tar.bz等变体,用于基于bzip的压缩。

请注意,这与最初由PKZIP创建的.zip文件格式不同,后者在单个步骤/规范中处理捆绑(tar)和压缩(gz)。

无论如何,鉴于此,您需要的另一个工具是解释tar数据并将其转换为对您有用的内容。我搜索了“tar file reader js”并找到了js-untar:https://github.com/InvokIT/js-untar

这似乎采用了ArrayBuffer并将其转换为一系列File对象。项目页面中的示例代码:

import untar from "js-untar";

// Load the source ArrayBuffer from a XMLHttpRequest (or any other way you may need).
var sourceBuffer = [...];

untar(sourceBuffer)
.progress(function(extractedFile) {
    ... // Do something with a single extracted file.
})
.then(function(extractedFiles) {
    ... // Do something with all extracted files.
});

// or

untar(sourceBuffer).then(
    function(extractedFiles) { // onSuccess
        ... // Do something with all extracted files.
    },
    function(err) { // onError
        ... // Handle the error.
    },
    function(extractedFile) { // onProgress
        ... // Do something with a single extracted file.
    }
);

这似乎就是你所需要的。

(请注意我不能保证这个模块的适用性或可靠性,因为我从未使用它,但是这应该给你一个起点和背景来继续)。

答案 1 :(得分:0)

在ChrisM的回答和他对js-untar的引用的帮助下,我设法做了以下工作,这完美地完成了工作:

@example.com

解压缩功能的结果现在是一个包含File对象的数组。甚至可以检索有关存档文件中原始路径的信息。