Мне нужны JSZip и gzip для моей веб-страницы, и JSZip содержит все ингредиенты, но скрывает их так, что я не могу взломать

Поддержка gzip в JavaScript на удивление слабая. Все браузеры реализуют его для поддержки заголовка Content-encoding: gzip, но стандартного доступа к функции браузера gzip / gunzip нет. Так что нужно использовать только JavaScript-подход. Вокруг есть несколько старых библиотек gzip-js, но они не поддерживают потоковую передачу и не требуют 6 лет обслуживания.

Кроме того, существует пакет, более активно поддерживаемый, но он также не видит включенного потока при использовании их собственного дистрибутива, поэтому вам нужно хранить весь двоичный массив и вывод gzip в памяти. Я могу ошибаться, но это то, что я собираю.

JSZip является хорошо разработанным инструментом и имеет поддержку потоков "Рабочие". JSZip использует Пако. ZIP-записи DEFLATEd и имеют контрольную сумму CRC32, как и в gzip, только немного иначе организованную, конечно. Просто исходя из созерцания исходных текстов JSZip, похоже, что было бы легко выставить опцию сжатия pzo gzip в потоковую поддержку JSZip. И если я использую как JSZip, так и мне нужен gzip, зачем мне загружать pako дважды?

Я надеялся, что смогу просто проникнуть во внутреннюю часть JSZip к базовым Workers и использовать основанную на pako реализацию "Flate" (т.е. in-flate / de-flate) с опцией gzip, распознаваемой pako. Исследовал это с помощью консоли Chrome javascript, но я не могу дозвониться. Распространяемый загружаемый jszip.js или jszip-min.js скрывают все внутренние компоненты от доступа к сценариям. Я не могу открыть эту коробку.

Поэтому я посмотрел исходный код git hub, чтобы посмотреть, смогу ли я создать свой собственный загружаемый модуль jszip.js или jszip-min.js, куда я бы экспортировал больше внутренних ресурсов для использования на своей странице, но, находясь в этом в течение 20 лет UNIX создает файлы, ant, все, я чувствую себя полным новичком, когда дело доходит до этих трюков по упаковке модулей javascript, и я вижу bower и gruntfiles, которые, кажется, все связаны с node.js, который я надеваю не нужно (только клиентский браузер) и никогда не работал, поэтому я понятия не имею, с чего начать.

1 ответ

Решение

Как говорил Эверт, я должен был сначала проверить инструкции по сборке в документации https://stuk.github.io/jszip/documentation/contributing.html.

Из этого ясно, что сначала нужно сделать git и сделать локальный клон. Затем нужно настроить командную строку grunt, для которой требуется npm, поставляемый с nodejs. После запуска grunt появляются другие зависимости, которые необходимо установить npm. Это обычные мелочи и не работают, но достаточно Goggling и грубой силы, пытаясь сделать это.

Теперь 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); })

и это работает. Я делаю себе GZipFileStream с заголовком gzip и трейлером, создавая все правильно. Я поместил 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); 
  })

и я могу отправить большой двоичный объект на мой веб-сервер. Я проверил, что действительно большой файл обрабатывается кусками.

Единственное, что мне не нравится в этом, это то, что последний большой двоичный объект все еще собирается как один большой большой двоичный объект, поэтому я предполагаю, что он хранит все сжатые данные в памяти. Было бы лучше, если бы этот Blow был конечной точкой этого конвейера Worker, чтобы, когда xhr.send собирал данные из BLOB-объекта в виде фрагментов, только тогда он получал бы фрагменты из конвейера Worker. Тем не менее, влияние значительно уменьшается, учитывая, что он содержит только сжатый контент, и, вероятно, (по крайней мере для меня) большие файлы будут мультимедийными файлами, которые в любом случае не должны быть сжаты gzip.

Я не писал функцию gunzip, потому что, честно говоря, мне она не нужна, и я не хочу делать такую, которая не сможет правильно анализировать заголовки расширений в заголовках gzip. Как только я загрузил сжатый контент на сервер (в моем случае S3), когда я снова его получаю, я предполагаю, что браузер выполнит распаковку для меня. Я не проверял это все же. Если это станет проблемой, я вернусь и отредактирую этот ответ.

Вот мой форк на github: https://github.com/gschadow/jszip, запрос на получение уже введен.

Другие вопросы по тегам