javascript - 我的网页需要 JSZip 和 gzip,JSZip 具有所有成分,但以我无法破解的方式隐藏它们

标签 javascript gzip jszip pako

JavaScript 对 gzip 的支持出奇的弱。所有浏览器都实现它以支持 Content-encoding: gzip header ,但浏览器的 gzip/gunzip 功能没有标准访问。因此必须使用仅 javascript 的方法。周围有一些旧的 gzip-js 库,但它们似乎不支持流,并且已经停止维护 6 年了。

然后是 pako,维护得更积极,但是如果使用他们自己的发行版,也看不到启用流,因此您需要将整个二进制数组和 gzip 输出保存在内存中。我可能是错的,但这就是我所收集的。

JSZip 是一个设计良好的工具,支持“Workers”流。 JSZip 使用 pako。 ZIP 条目是 DEFLATEd 的,并且有一个 CRC32 校验和,就像 gzip 一样,当然只是组织方式略有不同。仅从考虑 JSZip 源代码来看,将 pako 的 gzip 压缩选项公开到 JSZip 的流支持中似乎很容易。如果我同时使用 JSZip 并且还需要 gzip,为什么我要加载 pako 两次?

我希望我能够侵入 JSZip 的内部并到达底层 Workers,并使用基于 pako 的“Flate”(即 in-flate/de-flate)实现以及 pako 识别的 gzip 选项。使用 Chrome javascript 控制台探索它,但我无法通过。可分发的可加载 jszip.js 或 jszip-min.js 隐藏了所有内部结构,无法访问脚本。我无法打开那个盒子。

所以我一直在查看 git hub 源代码,看看是否可以构建自己的 jszip.js 或 jszip-min.js 可加载模块,在其中我可以导出更多内部资源以在我的页面中使用,但是有我已经从事这行 20 年了,UNIX make 文件,ant,一切,当谈到这些打包 javascript 模块的技巧时,我感觉自己像一个完全的新手,我看到了 Bower 和“gruntfiles”,它们似乎都与 node.js 有关,我不需要(仅客户端浏览器)并且从未使用过,所以我不知道从哪里开始。

最佳答案

正如 Evert 所说,我应该首先检查文档 https://stuk.github.io/jszip/documentation/contributing.html 中的构建说明。 .

从中可以清楚地看出,第一个需要 git 并进行本地克隆。然后需要设置grunt命令行,这需要npm,它是nodejs自带的。一旦 grunt 运行,还有其他依赖项需要 npm install-ed。这是通常的小事情,无法正常工作,但需要足够多的谷歌搜索和蛮力重试才能完成。

现在jszip/lib/index.js包含了最终导出的资源。它就是 JSZip 对象。因此,为了使用内部内容,我可以将它们添加到 JSZip 对象中,例如,它已经包含:

JSZip.external = require("./external");
module.exports = JSZip;

因此我们可以轻松添加我们想要使用的其他资源:

JSZip.flate = require("./flate");
JSZip.DataWorker = require('./stream/DataWorker');
JSZip.DataLengthProbe = require('./stream/DataLengthProbe');
JSZip.Crc32Probe = require('./stream/Crc32Probe');
JSZip.StreamHelper = require('./stream/StreamHelper');
JSZip.pako = require("pako");

现在,我可以在 Chrome 调试器中创建概念验证:

(new JSZip.StreamHelper(
   (new JSZip.DataWorker(Promise.resolve("Hello World! Hello World! Hello World! Hello World! Hello World! Hello World!")))
      .pipe(new JSZip.DataLengthProbe("uncompressedSize"))
      .pipe(new JSZip.Crc32Probe())
      .pipe(JSZip.flate.compressWorker({}))
      .pipe(new JSZip.DataLengthProbe("compressedSize"))
      .on("end", function(event) { console.log("onEnd: ", this.streamInfo) }), 
   "uint8array", "")
).accumulate(function(data) { console.log("acc: ", data); })
 .then(function(data) { console.log("then: ", data); })

这有效。我一直在为自己制作一个带有 gzip header 和预告片的 GZipFileStream,正确创建所有内容。我将 jszip/lib/generate/GZipFileWorker.js 放入如下:

'use strict';

var external = require('../external');
var utils = require('../utils');
var flate = require('../flate');
var GenericWorker = require('../stream/GenericWorker');
var DataWorker = require('../stream/DataWorker');
var StreamHelper = require('../stream/StreamHelper');
var DataLengthProbe = require('../stream/DataLengthProbe');
var Crc32Probe = require('../stream/Crc32Probe');

function GZipFileWorker() {
    GenericWorker.call(this, "GZipFileWorker");
    this.virgin = true;
}
utils.inherits(GZipFileWorker, GenericWorker);

GZipFileWorker.prototype.processChunk = function(chunk) {
    if(this.virgin) {
        this.virgin = false;
        var headerBuffer = new ArrayBuffer(10);
        var headerView = new DataView(headerBuffer);
        headerView.setUint16(0, 0x8b1f, true); // GZip magic
        headerView.setUint8(2, 0x08); // compression algorithm DEFLATE
        headerView.setUint8(3, 0x00); // flags
        // bit 0   FTEXT
        // bit 1   FHCRC
        // bit 2   FEXTRA
        // bit 3   FNAME
        // bit 4   FCOMMENT
        headerView.setUint32(4, (new Date()).getTime()/1000>>>0, true);
        headerView.setUint8(8, 0x00); // no extension headers
        headerView.setUint8(9, 0x03); // OS type UNIX
        this.push({data: new Uint8Array(headerBuffer)});
    }
    this.push(chunk);
};

GZipFileWorker.prototype.flush = function() {
    var trailerBuffer = new ArrayBuffer(8);
    var trailerView = new DataView(trailerBuffer);
    trailerView.setUint32(0, this.streamInfo["crc32"]>>>0, true);
    trailerView.setUint32(4, this.streamInfo["originalSize"]>>>0 & 0xffffffff, true);
    this.push({data: new Uint8Array(trailerBuffer)});
};

exports.gzip = function(data, inputFormat, outputFormat, compressionOptions, onUpdate) {
    var mimeType = data.contentType || data.mimeType || "";
    if(! (data instanceof GenericWorker)) {
        inputFormat = (inputFormat || "").toLowerCase();
        data = new DataWorker(
            utils.prepareContent(data.name || "gzip source",
                                 data,
                                 inputFormat !== "string",
                                 inputFormat === "binarystring",
                                 inputFormat === "base64"));
    }
    return new StreamHelper(
        data
            .pipe(new DataLengthProbe("originalSize"))
            .pipe(new Crc32Probe())
            .pipe(flate.compressWorker( compressionOptions || {} ))
            .pipe(new GZipFileWorker()),
        outputFormat.toLowerCase(), mimeType).accumulate(onUpdate);
};

在 jszip/lib/index.js 中我只需要这个:

var gzip = require("./generate/GZipFileWorker");
JSZip.gzip = gzip.gzip;

这是这样工作的:

JSZip.gzip("Hello World! Hello World! Hello World! Hello World! Hello World! Hello World!", "string", "base64", {level: 3}).then(function(result) { console.log(result); })

我可以将结果粘贴到 UNIX 管道中,如下所示:

$ echo -n "H4sIAOyR/VsAA/NIzcnJVwjPL8pJUVTwoJADAPCORolNAAAA" |base64 -d |zcat

它正确返回

Hello World! Hello World! Hello World! Hello World! Hello World! Hello World!

它也可以与文件一起使用:

JSZip.gzip(file, "", "Blob").then(function(blob) { 
     xhr.setRequestProperty("Content-encoding", "gzip");
     xhr.send(blob); 
  })

我可以将 blob 发送到我的网络服务器。我已经检查过大文件确实是分块处理的。

我唯一不喜欢的是,最终的 blob 仍然组装为一个大 Blob,因此我假设它在内存中保存所有压缩数据。如果 Blow 是 Worker 管道的端点,那就更好了,这样当 xhr.send 从 Blob 中逐 block 抓取数据时,它只会消耗 Worker 管道中的 block 。然而,考虑到它只保存压缩内容,而且大文件很可能(至少对我来说)是多媒体文件,无论如何都不需要进行 gzip 压缩,所以影响会减轻很多。

我没有编写gunzip函数,因为坦率地说,我不需要一个函数,而且我不想创建一个无法正确解析gzip header 中的扩展 header 的函数。一旦我将压缩内容上传到服务器(在我的例子中是 S3),当我再次获取它时,我假设浏览器会为我解压缩。不过我还没有检查过。如果这成为一个问题,我会回来更多地编辑这个答案。

这是我在 github 上的分支:https://github.com/gschadow/jszip ,已输入拉取请求。

关于javascript - 我的网页需要 JSZip 和 gzip,JSZip 具有所有成分,但以我无法破解的方式隐藏它们,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53486254/

相关文章:

javascript - 如何在用户点击jquery时自动安装Android应用程序

linux - 如何gzip文件并保留原始扩展名?

javascript - 使用 JSZip 读取 zip 的内容

javascript - 将 JS Promise 包装到函数中

IE 中的 JavaScript 错误

javascript - 在 Firebase 中制作用于更新数据的 Web 表单

python - 在 python 中读取/压缩一个大文件的更简洁的方法

python - gzip 引发 OverflowError : Size does not fit in an unsigned int

angularjs - 链接多个 Promise,包括 Promise.all

javascript - 是否可以将 jquery 调用绑定(bind)到两个 CSS 选择器?