read-excel-file 9.1.1 → 9.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.gitlab-ci.yml +1 -1
- package/CHANGELOG.md +14 -0
- package/README.md +153 -50
- package/bundle/read-excel-file.min.js +1 -1
- package/bundle/read-excel-file.min.js.map +1 -1
- package/bundle/read-excel-file.min.old.js +2 -0
- package/bundle/read-excel-file.min.old.js.map +1 -0
- package/commonjs/export/convertValuesFromUint8ArraysToStrings.js +3 -0
- package/commonjs/export/convertValuesFromUint8ArraysToStrings.js.map +1 -1
- package/commonjs/export/parseSheet.js +12 -9
- package/commonjs/export/parseSheet.js.map +1 -1
- package/commonjs/export/readSheetBrowser.js +6 -2
- package/commonjs/export/readSheetBrowser.js.map +1 -1
- package/commonjs/export/readSheetNode.js +6 -2
- package/commonjs/export/readSheetNode.js.map +1 -1
- package/commonjs/export/readSheetUniversal.js +14 -2
- package/commonjs/export/readSheetUniversal.js.map +1 -1
- package/commonjs/export/readSheetWebWorker.js +12 -2
- package/commonjs/export/readSheetWebWorker.js.map +1 -1
- package/commonjs/export/readXlsxFileBrowser.js +6 -2
- package/commonjs/export/readXlsxFileBrowser.js.map +1 -1
- package/commonjs/export/readXlsxFileNode.js +6 -2
- package/commonjs/export/readXlsxFileNode.js.map +1 -1
- package/commonjs/export/readXlsxFileUniversal.js +14 -2
- package/commonjs/export/readXlsxFileUniversal.js.map +1 -1
- package/commonjs/export/readXlsxFileWebWorker.js +12 -2
- package/commonjs/export/readXlsxFileWebWorker.js.map +1 -1
- package/commonjs/export/unpackXlsxFileBrowser.js +29 -6
- package/commonjs/export/unpackXlsxFileBrowser.js.map +1 -1
- package/commonjs/export/unpackXlsxFileNode.js +6 -0
- package/commonjs/export/unpackXlsxFileNode.js.map +1 -1
- package/commonjs/export/unpackXlsxFileUniversal.js +6 -0
- package/commonjs/export/unpackXlsxFileUniversal.js.map +1 -1
- package/commonjs/export/unpackXlsxFileWebWorker.js +14 -0
- package/commonjs/export/unpackXlsxFileWebWorker.js.map +1 -0
- package/commonjs/parseSheetData/parseSheetData.js +6 -6
- package/commonjs/parseSheetData/parseSheetData.js.map +1 -1
- package/commonjs/utility/checkpoint.js +33 -0
- package/commonjs/utility/checkpoint.js.map +1 -0
- package/commonjs/utility/isObject.js +15 -0
- package/commonjs/utility/isObject.js.map +1 -0
- package/commonjs/utility/isPromise.js +18 -0
- package/commonjs/utility/isPromise.js.map +1 -0
- package/commonjs/xlsx/convertCellsToData2dArray.js +0 -3
- package/commonjs/xlsx/convertCellsToData2dArray.js.map +1 -1
- package/commonjs/xlsx/isDateFormatStyle.js +2 -2
- package/commonjs/xlsx/isDateFormatStyle.js.map +1 -1
- package/commonjs/xlsx/parseCell.js +89 -24
- package/commonjs/xlsx/parseCell.js.map +1 -1
- package/{modules/xlsx/parseCellCoordinates.js → commonjs/xlsx/parseCellAddress.js} +18 -7
- package/commonjs/xlsx/parseCellAddress.js.map +1 -0
- package/commonjs/xlsx/parseCellValue.js +7 -23
- package/commonjs/xlsx/parseCellValue.js.map +1 -1
- package/commonjs/xlsx/parseFilePaths.js +31 -32
- package/commonjs/xlsx/parseFilePaths.js.map +1 -1
- package/commonjs/xlsx/parseNumber.js +20 -0
- package/commonjs/xlsx/parseNumber.js.map +1 -0
- package/commonjs/xlsx/parseSharedString.js +64 -0
- package/commonjs/xlsx/parseSharedString.js.map +1 -0
- package/commonjs/xlsx/parseSharedStrings.js +45 -5
- package/commonjs/xlsx/parseSharedStrings.js.map +1 -1
- package/commonjs/xlsx/parseSheet.js +93 -9
- package/commonjs/xlsx/parseSheet.js.map +1 -1
- package/commonjs/xlsx/parseSheetData.js +68 -0
- package/commonjs/xlsx/parseSheetData.js.map +1 -0
- package/commonjs/xlsx/parseSpreadsheetContents.js +244 -43
- package/commonjs/xlsx/parseSpreadsheetContents.js.map +1 -1
- package/commonjs/xlsx/parseSpreadsheetInfo.js +67 -36
- package/commonjs/xlsx/parseSpreadsheetInfo.js.map +1 -1
- package/commonjs/xlsx/parseStyles.js +89 -36
- package/commonjs/xlsx/parseStyles.js.map +1 -1
- package/commonjs/xml/dom.js +2 -1
- package/commonjs/xml/dom.js.map +1 -1
- package/commonjs/xml/parseXmlStream.js +14 -0
- package/commonjs/xml/parseXmlStream.js.map +1 -0
- package/commonjs/xml/parseXmlStream.sax.js +109 -0
- package/commonjs/xml/parseXmlStream.sax.js.map +1 -0
- package/commonjs/xml/parseXmlStream.saxen.code.js +953 -0
- package/commonjs/xml/parseXmlStream.saxen.code.js.map +1 -0
- package/commonjs/xml/parseXmlStream.saxen.js +85 -0
- package/commonjs/xml/parseXmlStream.saxen.js.map +1 -0
- package/commonjs/xml/xlsx.js +75 -40
- package/commonjs/xml/xlsx.js.map +1 -1
- package/commonjs/zip/unzipFromArrayBuffer.js +5 -14
- package/commonjs/zip/unzipFromArrayBuffer.js.map +1 -1
- package/commonjs/zip/unzipFromStream.fflate.js +338 -0
- package/commonjs/zip/unzipFromStream.fflate.js.map +1 -0
- package/commonjs/zip/unzipFromStream.js +8 -200
- package/commonjs/zip/unzipFromStream.js.map +1 -1
- package/commonjs/zip/unzipFromStream.unzipper.js +138 -0
- package/commonjs/zip/unzipFromStream.unzipper.js.map +1 -0
- package/modules/export/convertValuesFromUint8ArraysToStrings.js +2 -0
- package/modules/export/convertValuesFromUint8ArraysToStrings.js.map +1 -1
- package/modules/export/parseSheet.js +12 -9
- package/modules/export/parseSheet.js.map +1 -1
- package/modules/export/readSheetBrowser.js +5 -2
- package/modules/export/readSheetBrowser.js.map +1 -1
- package/modules/export/readSheetNode.js +5 -2
- package/modules/export/readSheetNode.js.map +1 -1
- package/modules/export/readSheetUniversal.js +14 -2
- package/modules/export/readSheetUniversal.js.map +1 -1
- package/modules/export/readSheetWebWorker.js +12 -2
- package/modules/export/readSheetWebWorker.js.map +1 -1
- package/modules/export/readXlsxFileBrowser.js +5 -2
- package/modules/export/readXlsxFileBrowser.js.map +1 -1
- package/modules/export/readXlsxFileNode.js +5 -2
- package/modules/export/readXlsxFileNode.js.map +1 -1
- package/modules/export/readXlsxFileUniversal.js +14 -2
- package/modules/export/readXlsxFileUniversal.js.map +1 -1
- package/modules/export/readXlsxFileWebWorker.js +12 -2
- package/modules/export/readXlsxFileWebWorker.js.map +1 -1
- package/modules/export/unpackXlsxFileBrowser.js +26 -6
- package/modules/export/unpackXlsxFileBrowser.js.map +1 -1
- package/modules/export/unpackXlsxFileNode.js +3 -0
- package/modules/export/unpackXlsxFileNode.js.map +1 -1
- package/modules/export/unpackXlsxFileUniversal.js +3 -0
- package/modules/export/unpackXlsxFileUniversal.js.map +1 -1
- package/modules/export/unpackXlsxFileWebWorker.js +6 -0
- package/modules/export/unpackXlsxFileWebWorker.js.map +1 -0
- package/modules/parseSheetData/parseSheetData.js +4 -4
- package/modules/parseSheetData/parseSheetData.js.map +1 -1
- package/modules/utility/checkpoint.js +25 -0
- package/modules/utility/checkpoint.js.map +1 -0
- package/modules/utility/isObject.js +9 -0
- package/modules/utility/isObject.js.map +1 -0
- package/modules/utility/isPromise.js +12 -0
- package/modules/utility/isPromise.js.map +1 -0
- package/modules/xlsx/convertCellsToData2dArray.js +0 -3
- package/modules/xlsx/convertCellsToData2dArray.js.map +1 -1
- package/modules/xlsx/isDateFormatStyle.js +2 -2
- package/modules/xlsx/isDateFormatStyle.js.map +1 -1
- package/modules/xlsx/parseCell.js +85 -24
- package/modules/xlsx/parseCell.js.map +1 -1
- package/{commonjs/xlsx/parseCellCoordinates.js → modules/xlsx/parseCellAddress.js} +12 -13
- package/modules/xlsx/parseCellAddress.js.map +1 -0
- package/modules/xlsx/parseCellValue.js +7 -23
- package/modules/xlsx/parseCellValue.js.map +1 -1
- package/modules/xlsx/parseFilePaths.js +31 -33
- package/modules/xlsx/parseFilePaths.js.map +1 -1
- package/modules/xlsx/parseNumber.js +14 -0
- package/modules/xlsx/parseNumber.js.map +1 -0
- package/modules/xlsx/parseSharedString.js +55 -0
- package/modules/xlsx/parseSharedString.js.map +1 -0
- package/modules/xlsx/parseSharedStrings.js +46 -5
- package/modules/xlsx/parseSharedStrings.js.map +1 -1
- package/modules/xlsx/parseSheet.js +91 -9
- package/modules/xlsx/parseSheet.js.map +1 -1
- package/modules/xlsx/parseSheetData.js +55 -0
- package/modules/xlsx/parseSheetData.js.map +1 -0
- package/modules/xlsx/parseSpreadsheetContents.js +242 -43
- package/modules/xlsx/parseSpreadsheetContents.js.map +1 -1
- package/modules/xlsx/parseSpreadsheetInfo.js +67 -37
- package/modules/xlsx/parseSpreadsheetInfo.js.map +1 -1
- package/modules/xlsx/parseStyles.js +88 -36
- package/modules/xlsx/parseStyles.js.map +1 -1
- package/modules/xml/dom.js +2 -1
- package/modules/xml/dom.js.map +1 -1
- package/modules/xml/parseXmlStream.js +18 -0
- package/modules/xml/parseXmlStream.js.map +1 -0
- package/modules/xml/parseXmlStream.sax.js +103 -0
- package/modules/xml/parseXmlStream.sax.js.map +1 -0
- package/modules/xml/parseXmlStream.saxen.code.js +947 -0
- package/modules/xml/parseXmlStream.saxen.code.js.map +1 -0
- package/modules/xml/parseXmlStream.saxen.js +79 -0
- package/modules/xml/parseXmlStream.saxen.js.map +1 -0
- package/modules/xml/xlsx.js +72 -36
- package/modules/xml/xlsx.js.map +1 -1
- package/modules/zip/unzipFromArrayBuffer.js +5 -14
- package/modules/zip/unzipFromArrayBuffer.js.map +1 -1
- package/modules/zip/unzipFromStream.fflate.js +336 -0
- package/modules/zip/unzipFromStream.fflate.js.map +1 -0
- package/modules/zip/unzipFromStream.js +55 -187
- package/modules/zip/unzipFromStream.js.map +1 -1
- package/modules/zip/unzipFromStream.unzipper.js +133 -0
- package/modules/zip/unzipFromStream.unzipper.js.map +1 -0
- package/package.json +12 -32
- package/commonjs/xlsx/parseCellCoordinates.js.map +0 -1
- package/commonjs/xlsx/parseCells.js +0 -34
- package/commonjs/xlsx/parseCells.js.map +0 -1
- package/commonjs/xlsx/parseSheetDimensions.js +0 -43
- package/commonjs/xlsx/parseSheetDimensions.js.map +0 -1
- package/commonjs/xml/xml.js +0 -13
- package/commonjs/xml/xml.js.map +0 -1
- package/commonjs/xml/xmlBrowser.js +0 -17
- package/commonjs/xml/xmlBrowser.js.map +0 -1
- package/commonjs/xml/xpath/xlsx-xpath.js +0 -92
- package/commonjs/xml/xpath/xlsx-xpath.js.map +0 -1
- package/commonjs/xml/xpath/xpathBrowser.js +0 -33
- package/commonjs/xml/xpath/xpathBrowser.js.map +0 -1
- package/commonjs/xml/xpath/xpathNode.js +0 -16
- package/commonjs/xml/xpath/xpathNode.js.map +0 -1
- package/modules/xlsx/parseCellCoordinates.js.map +0 -1
- package/modules/xlsx/parseCells.js +0 -27
- package/modules/xlsx/parseCells.js.map +0 -1
- package/modules/xlsx/parseSheetDimensions.js +0 -37
- package/modules/xlsx/parseSheetDimensions.js.map +0 -1
- package/modules/xml/xml.js +0 -7
- package/modules/xml/xml.js.map +0 -1
- package/modules/xml/xmlBrowser.js +0 -11
- package/modules/xml/xmlBrowser.js.map +0 -1
- package/modules/xml/xpath/xlsx-xpath.js +0 -73
- package/modules/xml/xpath/xlsx-xpath.js.map +0 -1
- package/modules/xml/xpath/xpathBrowser.js +0 -27
- package/modules/xml/xpath/xpathBrowser.js.map +0 -1
- package/modules/xml/xpath/xpathNode.js +0 -9
- package/modules/xml/xpath/xpathNode.js.map +0 -1
|
@@ -0,0 +1,336 @@
|
|
|
1
|
+
function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
|
|
2
|
+
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
|
|
3
|
+
function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, _toPropertyKey(descriptor.key), descriptor); } }
|
|
4
|
+
function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; }
|
|
5
|
+
function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
|
|
6
|
+
function _toPropertyKey(arg) { var key = _toPrimitive(arg, "string"); return _typeof(key) === "symbol" ? key : String(key); }
|
|
7
|
+
function _toPrimitive(input, hint) { if (_typeof(input) !== "object" || input === null) return input; var prim = input[Symbol.toPrimitive]; if (prim !== undefined) { var res = prim.call(input, hint || "default"); if (_typeof(res) !== "object") return res; throw new TypeError("@@toPrimitive must return a primitive value."); } return (hint === "string" ? String : Number)(input); }
|
|
8
|
+
// Considerations:
|
|
9
|
+
//
|
|
10
|
+
// * `fflate` uses a pure-javascript implementation of `.zip` compression/decompression by default.
|
|
11
|
+
// This pure-javascript implementation is about 2x slower than Node.js's "native" `zlib` module.
|
|
12
|
+
// This issue is worked around by "marrying" `fflate` with Node.js's `zlib` "native" module through some tinkering.
|
|
13
|
+
// https://github.com/101arrowz/fflate/issues/284
|
|
14
|
+
//
|
|
15
|
+
// * Even though `fflate` implements a "streaming" mode of its own,
|
|
16
|
+
// it does not implement Node.js streams "contract", i.e. it just unzips the archive
|
|
17
|
+
// as fast as it can consume it from the input stream, without throttling the data throughput
|
|
18
|
+
// in cases when the input data flows faster than `fflate` can process it. This means that
|
|
19
|
+
// in the "worst case" scenario when `fflate`'s decompressor is unable to keep up with the
|
|
20
|
+
// data influx, it would "buffer" the entire `.zip` archive in RAM until it's processed.
|
|
21
|
+
// And while this is no big deal by any means, it's still not as elegant as adhering to
|
|
22
|
+
// Node.js streaming protocol.
|
|
23
|
+
|
|
24
|
+
// This code was originally submitted by Stian Jensen.
|
|
25
|
+
// https://github.com/catamphetamine/read-excel-file/pull/122
|
|
26
|
+
// https://github.com/catamphetamine/read-excel-file/pull/123
|
|
27
|
+
|
|
28
|
+
// A `*.zip` file consists of individual file entries with the "total" summary section
|
|
29
|
+
// placed at the end of the file rather than at the start of it, which was originally done
|
|
30
|
+
// to allow for easy append of data to a given `.zip` file.
|
|
31
|
+
// https://en.wikipedia.org/wiki/ZIP_(file_format)
|
|
32
|
+
//
|
|
33
|
+
// But this also means that reading a `*.zip` file from a stream can't really be done
|
|
34
|
+
// using the "officially recommended" way of first reading the "total" summary section
|
|
35
|
+
// and only then reading the individual file entries specified in that summary section.
|
|
36
|
+
//
|
|
37
|
+
// So in order to be able to read a `*.zip` file from a stream, some corners have to be cut.
|
|
38
|
+
// For example, the "total" summary section is completely ignored and instead the reader
|
|
39
|
+
// should adopt "data recovery" software approach — it should proactively "scan" the input stream
|
|
40
|
+
// for individual file entries and handle them one-by-one as they come.
|
|
41
|
+
//
|
|
42
|
+
// Such approach doesn't seem to contradict with the XLSX specification
|
|
43
|
+
// because an `*.xlsx` files is supposed to be a normal `.zip` archive
|
|
44
|
+
// without any "trickery" such as "deleted" files or "garbage" data
|
|
45
|
+
// hiding under the hood.
|
|
46
|
+
//
|
|
47
|
+
// So when handling `*.xlsx` file, we assume that each such file must start
|
|
48
|
+
// with an individual file entry followed by another individual file entry, etc.
|
|
49
|
+
//
|
|
50
|
+
// When the "summary" section is reached, we assume that the archive has ended.
|
|
51
|
+
//
|
|
52
|
+
// To read a `.zip` archive, the code uses `fflate`'s `Unzip` class.
|
|
53
|
+
// The actual decompression could be performed either by using
|
|
54
|
+
// `fflate`'s pure-javascript `UnzipInflate` implementation, which is
|
|
55
|
+
// about 2x slower than Node.js's "native" module `zlib`,
|
|
56
|
+
// or it could use the aforementioned `zlib` module as a drop-in replacement
|
|
57
|
+
// (with some tinkering).
|
|
58
|
+
//
|
|
59
|
+
// The `Unzip` class doesn't speak the Node.js stream interface, and `fflate`'s readme
|
|
60
|
+
// doesn't include a clear "reading a `.zip` file from a Node.js stream" section.
|
|
61
|
+
// https://github.com/101arrowz/fflate/issues/251
|
|
62
|
+
// Instead, the `Unzip` class has its own `push(chunk)` / `onfile` / `entry.ondata` protocol.
|
|
63
|
+
// This code reads the binary input stream and forwards each chunk of it to `unzip.push()`,
|
|
64
|
+
// and then collects the decompressed file entries.
|
|
65
|
+
//
|
|
66
|
+
// P.S. In the comments to `UnzipInflate` in `fflate` package, it says:
|
|
67
|
+
// "Streaming DEFLATE decompression for ZIP archives. Prefer AsyncZipInflate for better performance."
|
|
68
|
+
// But there seems to be no `AsyncZipInflate` class in the `fflate` package.
|
|
69
|
+
// https://github.com/101arrowz/fflate/issues/277
|
|
70
|
+
// So just the regular `UnzipInflate` is used here.
|
|
71
|
+
//
|
|
72
|
+
import { Unzip } from 'fflate';
|
|
73
|
+
// import { AsyncUnzipInflate, UnzipInflate } from 'fflate'
|
|
74
|
+
|
|
75
|
+
import { Buffer } from 'node:buffer';
|
|
76
|
+
import zlib from 'node:zlib';
|
|
77
|
+
|
|
78
|
+
// Native `zlib` is faster than `UnzipInflate`.
|
|
79
|
+
// * When decompressing a `1 MB` `.xlsx` file, the decompression time is `100 ms`
|
|
80
|
+
// when using `zlib` decompressor and `150 ms` when using `fflate` "sync" decompressor.
|
|
81
|
+
// * When decompressing a `10 MB` `.xlsx` file, the decompression time is `500 ms`
|
|
82
|
+
// when using `zlib` decompressor and `650 ms` when using `fflate` "sync" decompressor.
|
|
83
|
+
// * When decompressing a `50 MB` `.xlsx` file, the decompression time is `2800 ms`
|
|
84
|
+
// when using `zlib` decompressor and `3600 ms` when using `fflate` "sync" decompressor.
|
|
85
|
+
var USE_ZLIB_DECOMPRESSOR = true;
|
|
86
|
+
|
|
87
|
+
// `AsyncZipInflate` is faster than `UnzipInflate` for `.xlsx` files that're larger
|
|
88
|
+
// than a few megabytes.
|
|
89
|
+
// * When decompressing a `1 MB` `.xlsx` file, the decompression time is `150 ms`
|
|
90
|
+
// when using "sync" decompressor and `170 ms` when using "async" decompressor.
|
|
91
|
+
// * When decompressing a `10 MB` `.xlsx` file, the decompression time is about the same.
|
|
92
|
+
// * When decompressing a `50 MB` `.xlsx` file, the decompression time is `3600 ms`
|
|
93
|
+
// when using "sync" decompressor and `3200 ms` when using "async" decompressor.
|
|
94
|
+
var USE_ASYNC_FFLATE_DECOMPRESSOR = false;
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Reads `*.zip` file contents.
|
|
98
|
+
* @param {Stream} stream
|
|
99
|
+
* @return {Promise<Record<string,Buffer>>} Resolves to an object holding `*.zip` file entries. P.S. `Buffer` is a `Uint8Array`.
|
|
100
|
+
*/
|
|
101
|
+
export default function unzipFromStream(stream) {
|
|
102
|
+
var _ref = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {},
|
|
103
|
+
filter = _ref.filter;
|
|
104
|
+
// The `files` object stores the files and their contents.
|
|
105
|
+
var files = {};
|
|
106
|
+
var filesChunks = {};
|
|
107
|
+
var onFile = function onFile(filePath) {
|
|
108
|
+
// See if this file should be ignored.
|
|
109
|
+
// If it should, this entry won't be processed, i.e. `Unzip` will not try
|
|
110
|
+
// to decompress its data, and will just discard it.
|
|
111
|
+
if (filter && !filter({
|
|
112
|
+
path: filePath
|
|
113
|
+
})) {
|
|
114
|
+
return false;
|
|
115
|
+
}
|
|
116
|
+
filesChunks[filePath] = [];
|
|
117
|
+
};
|
|
118
|
+
var onFileData = function onFileData(filePath, chunk) {
|
|
119
|
+
filesChunks[filePath].push(chunk);
|
|
120
|
+
};
|
|
121
|
+
var onFileDataEnd = function onFileDataEnd(filePath) {
|
|
122
|
+
files[filePath] = Buffer.concat(filesChunks[filePath]);
|
|
123
|
+
delete filesChunks[filePath];
|
|
124
|
+
};
|
|
125
|
+
return unzipFromStream_(stream, onFile, onFileData, onFileDataEnd).then(function () {
|
|
126
|
+
return files;
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
var PROMISE_RESOLVE_VALUE = undefined;
|
|
130
|
+
function unzipFromStream_(stream, onFile, onFileData, onFileDataEnd) {
|
|
131
|
+
return new Promise(function (resolve, reject) {
|
|
132
|
+
var errored = false;
|
|
133
|
+
var onError = function onError(error) {
|
|
134
|
+
if (!errored) {
|
|
135
|
+
errored = true;
|
|
136
|
+
reject(error);
|
|
137
|
+
}
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
// The native `zlib` decoder finishes inflating an entry asynchronously, so
|
|
141
|
+
// the archive's `end` event can fire while entries are still decompressing.
|
|
142
|
+
// It counts "decompression still in-progress" entries the decompression for which
|
|
143
|
+
// has started but not yet finished, and the main promise only resolves once
|
|
144
|
+
// the input stream has ended *and* every entry is done decompressing.
|
|
145
|
+
var stillDecompressingEntriesCount = 0;
|
|
146
|
+
var noMoreEntries = false;
|
|
147
|
+
var resolveIfDone = function resolveIfDone() {
|
|
148
|
+
if (!errored && noMoreEntries && stillDecompressingEntriesCount === 0) {
|
|
149
|
+
resolve(PROMISE_RESOLVE_VALUE);
|
|
150
|
+
}
|
|
151
|
+
};
|
|
152
|
+
var _createZipFileValidat = createZipFileValidator(function (isValid) {
|
|
153
|
+
if (!isValid) {
|
|
154
|
+
onError(new Error('Invalid `.zip` archive'));
|
|
155
|
+
}
|
|
156
|
+
}),
|
|
157
|
+
validateChunk = _createZipFileValidat.validateChunk;
|
|
158
|
+
|
|
159
|
+
// `Unzip` discovers each individual file entry in the input data stream
|
|
160
|
+
// and then calls the callback function for each such entry.
|
|
161
|
+
var unzip = new Unzip(function (entry) {
|
|
162
|
+
// If there already was an error while reading this `.zip` file,
|
|
163
|
+
// ignore any follow-up entries.
|
|
164
|
+
if (errored) {
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// Skip directory entries (their names end with a slash).
|
|
169
|
+
// Only files are of any interest.
|
|
170
|
+
if (entry.name.endsWith('/')) {
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
if (onFile(entry.name) === false) {
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
stillDecompressingEntriesCount++;
|
|
177
|
+
|
|
178
|
+
// `entry.ondata` is called with each decompressed chunk of the entry,
|
|
179
|
+
// and then a final time with `isLast === true` once the entry is complete.
|
|
180
|
+
entry.ondata = function (error, chunk, isLast) {
|
|
181
|
+
if (error) {
|
|
182
|
+
return onError(error);
|
|
183
|
+
}
|
|
184
|
+
onFileData(entry.name, chunk);
|
|
185
|
+
if (isLast) {
|
|
186
|
+
stillDecompressingEntriesCount--;
|
|
187
|
+
onFileDataEnd(entry.name);
|
|
188
|
+
resolveIfDone();
|
|
189
|
+
}
|
|
190
|
+
};
|
|
191
|
+
|
|
192
|
+
// Start decompressing this entry.
|
|
193
|
+
entry.start();
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
// Register the decompressor for the data that was compressed using
|
|
197
|
+
// `DEFLATE` compression algorithm (compression method `8`),
|
|
198
|
+
// which is what `.xlsx` files use.
|
|
199
|
+
unzip.register(USE_ZLIB_DECOMPRESSOR ? NativeZlibInflate : USE_ASYNC_FFLATE_DECOMPRESSOR ? AsyncUnzipInflate : UnzipInflate);
|
|
200
|
+
stream
|
|
201
|
+
// Catch errors emitted from the input stream (for example, a file read error).
|
|
202
|
+
.on('error', onError)
|
|
203
|
+
// When another chunk of data is read from the input stream.
|
|
204
|
+
.on('data', function (chunk) {
|
|
205
|
+
// If there already was an error while reading this `.zip` file,
|
|
206
|
+
// ignore any follow-up data chunks.
|
|
207
|
+
if (errored) {
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
// Validate the `.zip` archive as its data comes through.
|
|
211
|
+
validateChunk(chunk);
|
|
212
|
+
// If the `.zip` archive is found to be invalid, stop any further
|
|
213
|
+
// processing of it.
|
|
214
|
+
if (errored) {
|
|
215
|
+
return;
|
|
216
|
+
}
|
|
217
|
+
// Push the next data chunk to `fflate`'s `Unzip` class instance.
|
|
218
|
+
//
|
|
219
|
+
// The `.push()` function of `fflate`'s own `ZipInflate` decompressor is synchronous,
|
|
220
|
+
// meaning that by the time it returns, any complete files entries encountered so far
|
|
221
|
+
// have already been decompressed and populated in the `files` object.
|
|
222
|
+
//
|
|
223
|
+
// The `.push()` function of `NativeZlibInflate` decompressor is asynchronous,
|
|
224
|
+
// so it requires hacking around with the counter of "still being decompressed" entries
|
|
225
|
+
// in order to detect the actual finish of the archive's decompression process.
|
|
226
|
+
//
|
|
227
|
+
try {
|
|
228
|
+
unzip.push(chunk, false);
|
|
229
|
+
} catch (error) {
|
|
230
|
+
onError(error);
|
|
231
|
+
}
|
|
232
|
+
})
|
|
233
|
+
// When there's no more data in the input stream to consume,
|
|
234
|
+
// finish reading the `.zip` archive.
|
|
235
|
+
.on('end', function () {
|
|
236
|
+
// If there were any errors when reading the `.zip` archive,
|
|
237
|
+
// don't `resolve()` with anything.
|
|
238
|
+
if (errored) {
|
|
239
|
+
return;
|
|
240
|
+
}
|
|
241
|
+
try {
|
|
242
|
+
// Signal the end of the archive to `fflate`'s `Unzip` class instance.
|
|
243
|
+
// It will flush any remaining state in it.
|
|
244
|
+
unzip.push(new Uint8Array(0), true);
|
|
245
|
+
// The input stream has ended.
|
|
246
|
+
noMoreEntries = true;
|
|
247
|
+
// The entries may still be decompressing asynchronously.
|
|
248
|
+
// In that case, resolve once they all finish decompresssing.
|
|
249
|
+
// Or, resolve if all entries have already finished decompressing by now.
|
|
250
|
+
resolveIfDone();
|
|
251
|
+
} catch (error) {
|
|
252
|
+
onError(error);
|
|
253
|
+
}
|
|
254
|
+
});
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
// Every section in a `.zip` archive is marked with 4 bytes, the first two of which
|
|
259
|
+
// are `0x50` and `0x4B`, which reads "PK", referencing the initials of the inventor Phil Katz.
|
|
260
|
+
//
|
|
261
|
+
// It looks like `fflate`'s `Unzip` doesn't ever complain about whatever data is thrown at it.
|
|
262
|
+
// Due to how `.zip` file format is defined, "garbage" data could be placed at various
|
|
263
|
+
// places in it and it'd still be a valid `.zip` archive. It's likely that for this reason
|
|
264
|
+
// `fflate` doesn't ever complain and simply emits no entries when fed any kind of invalid data.
|
|
265
|
+
//
|
|
266
|
+
// In order to introduce some basic validation, here we specifically demand
|
|
267
|
+
// that a `.zip` archive must at least start with an individual file entry
|
|
268
|
+
// because an `.xlsx` file creator softwared really shouldn't attempt doing
|
|
269
|
+
// anything "funny" when writing a file, hence this adherence requirement.
|
|
270
|
+
//
|
|
271
|
+
function createZipFileValidator(onValidationResult) {
|
|
272
|
+
var firstBytesCount = 2;
|
|
273
|
+
var firstBytes = [];
|
|
274
|
+
var firstBytesCheckResult;
|
|
275
|
+
return {
|
|
276
|
+
validateChunk: function validateChunk(chunk) {
|
|
277
|
+
if (firstBytes.length < 2) {
|
|
278
|
+
var i = 0;
|
|
279
|
+
while (i < chunk.length && i < firstBytesCount) {
|
|
280
|
+
firstBytes.push(chunk[i]);
|
|
281
|
+
i++;
|
|
282
|
+
}
|
|
283
|
+
if (firstBytes.length === 2) {
|
|
284
|
+
var isValid = firstBytes[0] === 0x50 && firstBytes[1] === 0x4B;
|
|
285
|
+
onValidationResult(isValid);
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
};
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
// An implemenation of a `DEFLATE` decompressor for `fflate`'s `Unzip` class
|
|
293
|
+
// that uses Node.js's "native" module `zlib`.
|
|
294
|
+
//
|
|
295
|
+
// It implements `fflate`'s decoder interface: `Unzip` constructs one decoder
|
|
296
|
+
// per each entry, sets `ondata(error, chunk, isLast)` callback on it, and feeds it
|
|
297
|
+
// the entry's compressed bytes by calling `push(chunk, isLast)` method.
|
|
298
|
+
//
|
|
299
|
+
// Unlike `fflate`'s synchronous `UnzipInflate` decoder, `zlib` decompresses asynchronously,
|
|
300
|
+
// so an entry only finishes some "ticks" after its last chunk of its data is `push()`ed
|
|
301
|
+
// by `fflate`'s `Unzip` class. To work around this issue, pending entries counter is used
|
|
302
|
+
// to track when the archive really finishes unpacking.
|
|
303
|
+
//
|
|
304
|
+
var NativeZlibInflate = /*#__PURE__*/function () {
|
|
305
|
+
function NativeZlibInflate() {
|
|
306
|
+
var _this = this;
|
|
307
|
+
_classCallCheck(this, NativeZlibInflate);
|
|
308
|
+
this.inflate = zlib.createInflateRaw();
|
|
309
|
+
this.inflate.on('data', function (chunk) {
|
|
310
|
+
return _this.ondata(null, chunk, false);
|
|
311
|
+
});
|
|
312
|
+
this.inflate.on('end', function () {
|
|
313
|
+
return _this.ondata(null, new Uint8Array(0), true);
|
|
314
|
+
});
|
|
315
|
+
this.inflate.on('error', function (error) {
|
|
316
|
+
return _this.ondata(error, null, false);
|
|
317
|
+
});
|
|
318
|
+
}
|
|
319
|
+
_createClass(NativeZlibInflate, [{
|
|
320
|
+
key: "push",
|
|
321
|
+
value: function push(chunk, isLast) {
|
|
322
|
+
this.inflate.write(Buffer.from(chunk));
|
|
323
|
+
if (isLast) {
|
|
324
|
+
this.inflate.end();
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
}, {
|
|
328
|
+
key: "terminate",
|
|
329
|
+
value: function terminate() {
|
|
330
|
+
this.inflate.destroy();
|
|
331
|
+
}
|
|
332
|
+
}]);
|
|
333
|
+
return NativeZlibInflate;
|
|
334
|
+
}();
|
|
335
|
+
_defineProperty(NativeZlibInflate, "compression", 8);
|
|
336
|
+
//# sourceMappingURL=unzipFromStream.fflate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"unzipFromStream.fflate.js","names":["Unzip","Buffer","zlib","USE_ZLIB_DECOMPRESSOR","USE_ASYNC_FFLATE_DECOMPRESSOR","unzipFromStream","stream","_ref","arguments","length","undefined","filter","files","filesChunks","onFile","filePath","path","onFileData","chunk","push","onFileDataEnd","concat","unzipFromStream_","then","PROMISE_RESOLVE_VALUE","Promise","resolve","reject","errored","onError","error","stillDecompressingEntriesCount","noMoreEntries","resolveIfDone","_createZipFileValidat","createZipFileValidator","isValid","Error","validateChunk","unzip","entry","name","endsWith","ondata","isLast","start","register","NativeZlibInflate","AsyncUnzipInflate","UnzipInflate","on","Uint8Array","onValidationResult","firstBytesCount","firstBytes","firstBytesCheckResult","i","_this","_classCallCheck","inflate","createInflateRaw","_createClass","key","value","write","from","end","terminate","destroy","_defineProperty"],"sources":["../../source/zip/unzipFromStream.fflate.js"],"sourcesContent":["// Considerations:\r\n//\r\n// * `fflate` uses a pure-javascript implementation of `.zip` compression/decompression by default.\r\n// This pure-javascript implementation is about 2x slower than Node.js's \"native\" `zlib` module.\r\n// This issue is worked around by \"marrying\" `fflate` with Node.js's `zlib` \"native\" module through some tinkering.\r\n// https://github.com/101arrowz/fflate/issues/284\r\n//\r\n// * Even though `fflate` implements a \"streaming\" mode of its own,\r\n// it does not implement Node.js streams \"contract\", i.e. it just unzips the archive\r\n// as fast as it can consume it from the input stream, without throttling the data throughput\r\n// in cases when the input data flows faster than `fflate` can process it. This means that\r\n// in the \"worst case\" scenario when `fflate`'s decompressor is unable to keep up with the\r\n// data influx, it would \"buffer\" the entire `.zip` archive in RAM until it's processed.\r\n// And while this is no big deal by any means, it's still not as elegant as adhering to\r\n// Node.js streaming protocol.\r\n\r\n// This code was originally submitted by Stian Jensen.\r\n// https://github.com/catamphetamine/read-excel-file/pull/122\r\n// https://github.com/catamphetamine/read-excel-file/pull/123\r\n\r\n// A `*.zip` file consists of individual file entries with the \"total\" summary section\r\n// placed at the end of the file rather than at the start of it, which was originally done\r\n// to allow for easy append of data to a given `.zip` file.\r\n// https://en.wikipedia.org/wiki/ZIP_(file_format)\r\n//\r\n// But this also means that reading a `*.zip` file from a stream can't really be done\r\n// using the \"officially recommended\" way of first reading the \"total\" summary section\r\n// and only then reading the individual file entries specified in that summary section.\r\n//\r\n// So in order to be able to read a `*.zip` file from a stream, some corners have to be cut.\r\n// For example, the \"total\" summary section is completely ignored and instead the reader\r\n// should adopt \"data recovery\" software approach — it should proactively \"scan\" the input stream\r\n// for individual file entries and handle them one-by-one as they come.\r\n//\r\n// Such approach doesn't seem to contradict with the XLSX specification\r\n// because an `*.xlsx` files is supposed to be a normal `.zip` archive\r\n// without any \"trickery\" such as \"deleted\" files or \"garbage\" data\r\n// hiding under the hood.\r\n//\r\n// So when handling `*.xlsx` file, we assume that each such file must start\r\n// with an individual file entry followed by another individual file entry, etc.\r\n//\r\n// When the \"summary\" section is reached, we assume that the archive has ended.\r\n//\r\n// To read a `.zip` archive, the code uses `fflate`'s `Unzip` class.\r\n// The actual decompression could be performed either by using\r\n// `fflate`'s pure-javascript `UnzipInflate` implementation, which is\r\n// about 2x slower than Node.js's \"native\" module `zlib`,\r\n// or it could use the aforementioned `zlib` module as a drop-in replacement\r\n// (with some tinkering).\r\n//\r\n// The `Unzip` class doesn't speak the Node.js stream interface, and `fflate`'s readme\r\n// doesn't include a clear \"reading a `.zip` file from a Node.js stream\" section.\r\n// https://github.com/101arrowz/fflate/issues/251\r\n// Instead, the `Unzip` class has its own `push(chunk)` / `onfile` / `entry.ondata` protocol.\r\n// This code reads the binary input stream and forwards each chunk of it to `unzip.push()`,\r\n// and then collects the decompressed file entries.\r\n//\r\n// P.S. In the comments to `UnzipInflate` in `fflate` package, it says:\r\n// \"Streaming DEFLATE decompression for ZIP archives. Prefer AsyncZipInflate for better performance.\"\r\n// But there seems to be no `AsyncZipInflate` class in the `fflate` package.\r\n// https://github.com/101arrowz/fflate/issues/277\r\n// So just the regular `UnzipInflate` is used here.\r\n//\r\nimport { Unzip } from 'fflate'\r\n// import { AsyncUnzipInflate, UnzipInflate } from 'fflate'\r\n\r\nimport { Buffer } from 'node:buffer'\r\nimport zlib from 'node:zlib'\r\n\r\n// Native `zlib` is faster than `UnzipInflate`.\r\n// * When decompressing a `1 MB` `.xlsx` file, the decompression time is `100 ms`\r\n// when using `zlib` decompressor and `150 ms` when using `fflate` \"sync\" decompressor.\r\n// * When decompressing a `10 MB` `.xlsx` file, the decompression time is `500 ms`\r\n// when using `zlib` decompressor and `650 ms` when using `fflate` \"sync\" decompressor.\r\n// * When decompressing a `50 MB` `.xlsx` file, the decompression time is `2800 ms`\r\n// when using `zlib` decompressor and `3600 ms` when using `fflate` \"sync\" decompressor.\r\nconst USE_ZLIB_DECOMPRESSOR = true\r\n\r\n// `AsyncZipInflate` is faster than `UnzipInflate` for `.xlsx` files that're larger\r\n// than a few megabytes.\r\n// * When decompressing a `1 MB` `.xlsx` file, the decompression time is `150 ms`\r\n// when using \"sync\" decompressor and `170 ms` when using \"async\" decompressor.\r\n// * When decompressing a `10 MB` `.xlsx` file, the decompression time is about the same.\r\n// * When decompressing a `50 MB` `.xlsx` file, the decompression time is `3600 ms`\r\n// when using \"sync\" decompressor and `3200 ms` when using \"async\" decompressor.\r\nconst USE_ASYNC_FFLATE_DECOMPRESSOR = false\r\n\r\n/**\r\n * Reads `*.zip` file contents.\r\n * @param {Stream} stream\r\n * @return {Promise<Record<string,Buffer>>} Resolves to an object holding `*.zip` file entries. P.S. `Buffer` is a `Uint8Array`.\r\n */\r\nexport default function unzipFromStream(stream, { filter } = {}) {\r\n\t// The `files` object stores the files and their contents.\r\n\tconst files = {}\r\n\tconst filesChunks = {}\r\n\r\n\tconst onFile = (filePath) => {\r\n\t\t// See if this file should be ignored.\r\n\t\t// If it should, this entry won't be processed, i.e. `Unzip` will not try\r\n\t\t// to decompress its data, and will just discard it.\r\n\t\tif (filter && !filter({ path: filePath })) {\r\n\t\t\treturn false\r\n\t\t}\r\n\t\tfilesChunks[filePath] = []\r\n\t}\r\n\r\n\tconst onFileData = (filePath, chunk) => {\r\n\t\tfilesChunks[filePath].push(chunk)\r\n\t}\r\n\r\n\tconst onFileDataEnd = (filePath) => {\r\n\t\tfiles[filePath] = Buffer.concat(filesChunks[filePath])\r\n\t\tdelete filesChunks[filePath]\r\n\t}\r\n\r\n\treturn unzipFromStream_(stream, onFile, onFileData, onFileDataEnd).then(() => {\r\n\t\treturn files\r\n\t})\r\n}\r\n\r\nconst PROMISE_RESOLVE_VALUE = undefined\r\n\r\nfunction unzipFromStream_(stream, onFile, onFileData, onFileDataEnd) {\r\n\treturn new Promise((resolve, reject) => {\r\n\t\tlet errored = false\r\n\r\n\t\tconst onError = (error) => {\r\n\t\t\tif (!errored) {\r\n\t\t\t\terrored = true\r\n\t\t\t\treject(error)\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\t// The native `zlib` decoder finishes inflating an entry asynchronously, so\r\n\t\t// the archive's `end` event can fire while entries are still decompressing.\r\n\t\t// It counts \"decompression still in-progress\" entries the decompression for which\r\n\t\t// has started but not yet finished, and the main promise only resolves once\r\n\t\t// the input stream has ended *and* every entry is done decompressing.\r\n\t\tlet stillDecompressingEntriesCount = 0\r\n\t\tlet noMoreEntries = false\r\n\t\tconst resolveIfDone = () => {\r\n\t\t\tif (!errored && noMoreEntries && stillDecompressingEntriesCount === 0) {\r\n\t\t\t\tresolve(PROMISE_RESOLVE_VALUE)\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tconst { validateChunk } = createZipFileValidator((isValid) => {\r\n\t\t\tif (!isValid) {\r\n\t\t\t\tonError(new Error('Invalid `.zip` archive'))\r\n\t\t\t}\r\n\t\t})\r\n\r\n\t\t// `Unzip` discovers each individual file entry in the input data stream\r\n\t\t// and then calls the callback function for each such entry.\r\n\t\tconst unzip = new Unzip((entry) => {\r\n\t\t\t// If there already was an error while reading this `.zip` file,\r\n\t\t\t// ignore any follow-up entries.\r\n\t\t\tif (errored) {\r\n\t\t\t\treturn\r\n\t\t\t}\r\n\r\n\t\t\t// Skip directory entries (their names end with a slash).\r\n\t\t\t// Only files are of any interest.\r\n\t\t\tif (entry.name.endsWith('/')) {\r\n\t\t\t\treturn\r\n\t\t\t}\r\n\r\n\t\t\tif (onFile(entry.name) === false) {\r\n\t\t\t\treturn\r\n\t\t\t}\r\n\r\n\t\t\tstillDecompressingEntriesCount++\r\n\r\n\t\t\t// `entry.ondata` is called with each decompressed chunk of the entry,\r\n\t\t\t// and then a final time with `isLast === true` once the entry is complete.\r\n\t\t\tentry.ondata = (error, chunk, isLast) => {\r\n\t\t\t\tif (error) {\r\n\t\t\t\t\treturn onError(error)\r\n\t\t\t\t}\r\n\t\t\t\tonFileData(entry.name, chunk)\r\n\t\t\t\tif (isLast) {\r\n\t\t\t\t\tstillDecompressingEntriesCount--\r\n\t\t\t\t\tonFileDataEnd(entry.name)\r\n\t\t\t\t\tresolveIfDone()\r\n\t\t\t\t}\r\n\t\t\t}\r\n\r\n\t\t\t// Start decompressing this entry.\r\n\t\t\tentry.start()\r\n\t\t})\r\n\r\n\t\t// Register the decompressor for the data that was compressed using\r\n\t\t// `DEFLATE` compression algorithm (compression method `8`),\r\n\t\t// which is what `.xlsx` files use.\r\n\t\tunzip.register(USE_ZLIB_DECOMPRESSOR ? NativeZlibInflate : (USE_ASYNC_FFLATE_DECOMPRESSOR ? AsyncUnzipInflate : UnzipInflate))\r\n\r\n\t\tstream\r\n\t\t\t// Catch errors emitted from the input stream (for example, a file read error).\r\n\t\t\t.on('error', onError)\r\n\t\t\t// When another chunk of data is read from the input stream.\r\n\t\t\t.on('data', (chunk) => {\r\n\t\t\t\t// If there already was an error while reading this `.zip` file,\r\n\t\t\t\t// ignore any follow-up data chunks.\r\n\t\t\t\tif (errored) {\r\n\t\t\t\t\treturn\r\n\t\t\t\t}\r\n\t\t\t\t// Validate the `.zip` archive as its data comes through.\r\n\t\t\t\tvalidateChunk(chunk)\r\n\t\t\t\t// If the `.zip` archive is found to be invalid, stop any further\r\n\t\t\t\t// processing of it.\r\n\t\t\t\tif (errored) {\r\n\t\t\t\t\treturn\r\n\t\t\t\t}\r\n\t\t\t\t// Push the next data chunk to `fflate`'s `Unzip` class instance.\r\n\t\t\t\t//\r\n\t\t\t\t// The `.push()` function of `fflate`'s own `ZipInflate` decompressor is synchronous,\r\n\t\t\t\t// meaning that by the time it returns, any complete files entries encountered so far\r\n\t\t\t\t// have already been decompressed and populated in the `files` object.\r\n\t\t\t\t//\r\n\t\t\t\t// The `.push()` function of `NativeZlibInflate` decompressor is asynchronous,\r\n\t\t\t\t// so it requires hacking around with the counter of \"still being decompressed\" entries\r\n\t\t\t\t// in order to detect the actual finish of the archive's decompression process.\r\n\t\t\t\t//\r\n\t\t\t\ttry {\r\n\t\t\t\t\tunzip.push(chunk, false)\r\n\t\t\t\t} catch (error) {\r\n\t\t\t\t\tonError(error)\r\n\t\t\t\t}\r\n\t\t\t})\r\n\t\t\t// When there's no more data in the input stream to consume,\r\n\t\t\t// finish reading the `.zip` archive.\r\n\t\t\t.on('end', () => {\r\n\t\t\t\t// If there were any errors when reading the `.zip` archive,\r\n\t\t\t\t// don't `resolve()` with anything.\r\n\t\t\t\tif (errored) {\r\n\t\t\t\t\treturn\r\n\t\t\t\t}\r\n\t\t\t\ttry {\r\n\t\t\t\t\t// Signal the end of the archive to `fflate`'s `Unzip` class instance.\r\n\t\t\t\t\t// It will flush any remaining state in it.\r\n\t\t\t\t\tunzip.push(new Uint8Array(0), true)\r\n\t\t\t\t\t// The input stream has ended.\r\n\t\t\t\t\tnoMoreEntries = true\r\n\t\t\t\t\t// The entries may still be decompressing asynchronously.\r\n\t\t\t\t\t// In that case, resolve once they all finish decompresssing.\r\n\t\t\t\t\t// Or, resolve if all entries have already finished decompressing by now.\r\n\t\t\t\t\tresolveIfDone()\r\n\t\t\t\t} catch (error) {\r\n\t\t\t\t\tonError(error)\r\n\t\t\t\t}\r\n\t\t\t})\r\n\t})\r\n}\r\n\r\n// Every section in a `.zip` archive is marked with 4 bytes, the first two of which\r\n// are `0x50` and `0x4B`, which reads \"PK\", referencing the initials of the inventor Phil Katz.\r\n//\r\n// It looks like `fflate`'s `Unzip` doesn't ever complain about whatever data is thrown at it.\r\n// Due to how `.zip` file format is defined, \"garbage\" data could be placed at various\r\n// places in it and it'd still be a valid `.zip` archive. It's likely that for this reason\r\n// `fflate` doesn't ever complain and simply emits no entries when fed any kind of invalid data.\r\n//\r\n// In order to introduce some basic validation, here we specifically demand\r\n// that a `.zip` archive must at least start with an individual file entry\r\n// because an `.xlsx` file creator softwared really shouldn't attempt doing\r\n// anything \"funny\" when writing a file, hence this adherence requirement.\r\n//\r\nfunction createZipFileValidator(onValidationResult) {\r\n\tconst firstBytesCount = 2\r\n\tconst firstBytes = []\r\n\tlet firstBytesCheckResult\r\n\treturn {\r\n\t\tvalidateChunk(chunk) {\r\n\t\t\tif (firstBytes.length < 2) {\r\n\t\t\t\tlet i = 0\r\n\t\t\t\twhile (i < chunk.length && i < firstBytesCount) {\r\n\t\t\t\t\tfirstBytes.push(chunk[i])\r\n\t\t\t\t\ti++\r\n\t\t\t\t}\r\n\t\t\t\tif (firstBytes.length === 2) {\r\n\t\t\t\t\tconst isValid = firstBytes[0] === 0x50 && firstBytes[1] === 0x4B\r\n\t\t\t\t\tonValidationResult(isValid)\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n}\r\n\r\n// An implemenation of a `DEFLATE` decompressor for `fflate`'s `Unzip` class\r\n// that uses Node.js's \"native\" module `zlib`.\r\n//\r\n// It implements `fflate`'s decoder interface: `Unzip` constructs one decoder\r\n// per each entry, sets `ondata(error, chunk, isLast)` callback on it, and feeds it\r\n// the entry's compressed bytes by calling `push(chunk, isLast)` method.\r\n//\r\n// Unlike `fflate`'s synchronous `UnzipInflate` decoder, `zlib` decompresses asynchronously,\r\n// so an entry only finishes some \"ticks\" after its last chunk of its data is `push()`ed\r\n// by `fflate`'s `Unzip` class. To work around this issue, pending entries counter is used\r\n// to track when the archive really finishes unpacking.\r\n//\r\nclass NativeZlibInflate {\r\n\tstatic compression = 8\r\n\r\n\tconstructor() {\r\n\t\tthis.inflate = zlib.createInflateRaw()\r\n\t\tthis.inflate.on('data', (chunk) => this.ondata(null, chunk, false))\r\n\t\tthis.inflate.on('end', () => this.ondata(null, new Uint8Array(0), true))\r\n\t\tthis.inflate.on('error', (error) => this.ondata(error, null, false))\r\n\t}\r\n\r\n\tpush(chunk, isLast) {\r\n\t\tthis.inflate.write(Buffer.from(chunk))\r\n\t\tif (isLast) {\r\n\t\t\tthis.inflate.end()\r\n\t\t}\r\n\t}\r\n\r\n\tterminate() {\r\n\t\tthis.inflate.destroy()\r\n\t}\r\n}\r\n"],"mappings":"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"}
|