@loaders.gl/zip 4.2.0-alpha.4 → 4.2.0-alpha.5
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/dist/dist.dev.js +920 -542
- package/dist/dist.min.js +25 -0
- package/dist/filesystems/IndexedArchive.js +24 -12
- package/dist/filesystems/zip-filesystem.d.ts +2 -2
- package/dist/filesystems/zip-filesystem.d.ts.map +1 -1
- package/dist/filesystems/zip-filesystem.js +121 -88
- package/dist/hash-file-utility.d.ts +1 -1
- package/dist/hash-file-utility.d.ts.map +1 -1
- package/dist/hash-file-utility.js +85 -42
- package/dist/index.cjs +49 -125
- package/dist/index.cjs.map +7 -0
- package/dist/index.d.ts +12 -12
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -1
- package/dist/lib/tar/header.d.ts +1 -1
- package/dist/lib/tar/header.d.ts.map +1 -1
- package/dist/lib/tar/header.js +69 -33
- package/dist/lib/tar/tar.d.ts +1 -1
- package/dist/lib/tar/tar.d.ts.map +1 -1
- package/dist/lib/tar/tar.js +121 -106
- package/dist/lib/tar/types.js +3 -1
- package/dist/lib/tar/utils.js +45 -18
- package/dist/parse-zip/cd-file-header.d.ts +1 -1
- package/dist/parse-zip/cd-file-header.d.ts.map +1 -1
- package/dist/parse-zip/cd-file-header.js +239 -177
- package/dist/parse-zip/end-of-central-directory.js +247 -158
- package/dist/parse-zip/local-file-header.d.ts +1 -1
- package/dist/parse-zip/local-file-header.d.ts.map +1 -1
- package/dist/parse-zip/local-file-header.js +143 -102
- package/dist/parse-zip/search-from-the-end.js +27 -13
- package/dist/parse-zip/zip-composition.js +142 -92
- package/dist/parse-zip/zip64-info-generation.js +64 -41
- package/dist/tar-builder.d.ts +1 -1
- package/dist/tar-builder.d.ts.map +1 -1
- package/dist/tar-builder.js +30 -29
- package/dist/zip-loader.js +51 -40
- package/dist/zip-writer.js +39 -39
- package/package.json +11 -7
- package/dist/filesystems/IndexedArchive.js.map +0 -1
- package/dist/filesystems/zip-filesystem.js.map +0 -1
- package/dist/hash-file-utility.js.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/lib/tar/header.js.map +0 -1
- package/dist/lib/tar/tar.js.map +0 -1
- package/dist/lib/tar/types.js.map +0 -1
- package/dist/lib/tar/utils.js.map +0 -1
- package/dist/parse-zip/cd-file-header.js.map +0 -1
- package/dist/parse-zip/end-of-central-directory.js.map +0 -1
- package/dist/parse-zip/local-file-header.js.map +0 -1
- package/dist/parse-zip/search-from-the-end.js.map +0 -1
- package/dist/parse-zip/zip-composition.js.map +0 -1
- package/dist/parse-zip/zip64-info-generation.js.map +0 -1
- package/dist/tar-builder.js.map +0 -1
- package/dist/zip-loader.js.map +0 -1
- package/dist/zip-writer.js.map +0 -1
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
// loaders.gl
|
|
2
|
+
// SPDX-License-Identifier: MIT
|
|
3
|
+
// Copyright (c) vis.gl contributors
|
|
1
4
|
import { isBrowser } from '@loaders.gl/loader-utils';
|
|
2
5
|
import { isFileProvider } from '@loaders.gl/loader-utils';
|
|
3
6
|
import { FileHandleFile } from '@loaders.gl/loader-utils';
|
|
@@ -5,101 +8,131 @@ import { makeZipCDHeaderIterator } from "../parse-zip/cd-file-header.js";
|
|
|
5
8
|
import { parseZipLocalFileHeader } from "../parse-zip/local-file-header.js";
|
|
6
9
|
import { DeflateCompression } from '@loaders.gl/compression';
|
|
7
10
|
import { IndexedArchive } from "./IndexedArchive.js";
|
|
11
|
+
/** Handling different compression types in zip */
|
|
8
12
|
export const ZIP_COMPRESSION_HANDLERS = {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
13
|
+
/** No compression */
|
|
14
|
+
0: async (compressedFile) => compressedFile,
|
|
15
|
+
/** Deflation */
|
|
16
|
+
8: async (compressedFile) => {
|
|
17
|
+
const compression = new DeflateCompression({ raw: true });
|
|
18
|
+
const decompressedData = await compression.decompress(compressedFile);
|
|
19
|
+
return decompressedData;
|
|
20
|
+
}
|
|
17
21
|
};
|
|
22
|
+
/**
|
|
23
|
+
* FileSystem adapter for a ZIP file
|
|
24
|
+
* Holds FileProvider object that provides random access to archived files
|
|
25
|
+
*/
|
|
18
26
|
export class ZipFileSystem {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
this.
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
throw new Error('No data detected in the zip archive');
|
|
27
|
+
/**
|
|
28
|
+
* Constructor
|
|
29
|
+
* @param file - instance of FileProvider or file path string
|
|
30
|
+
*/
|
|
31
|
+
constructor(file) {
|
|
32
|
+
/** FileProvider instance promise */
|
|
33
|
+
this.fileProvider = null;
|
|
34
|
+
this.archive = null;
|
|
35
|
+
// Try to open file in NodeJS
|
|
36
|
+
if (typeof file === 'string') {
|
|
37
|
+
this.fileName = file;
|
|
38
|
+
if (!isBrowser) {
|
|
39
|
+
this.fileProvider = new FileHandleFile(file);
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
throw new Error('Cannot open file for random access in a WEB browser');
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
else if (file instanceof IndexedArchive) {
|
|
46
|
+
this.fileProvider = file.fileProvider;
|
|
47
|
+
this.archive = file;
|
|
48
|
+
this.fileName = file.fileName;
|
|
49
|
+
}
|
|
50
|
+
else if (isFileProvider(file)) {
|
|
51
|
+
this.fileProvider = file;
|
|
52
|
+
}
|
|
46
53
|
}
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
54
|
+
/** Clean up resources */
|
|
55
|
+
async destroy() {
|
|
56
|
+
if (this.fileProvider) {
|
|
57
|
+
await this.fileProvider.destroy();
|
|
58
|
+
}
|
|
51
59
|
}
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
if (!this.fileProvider) {
|
|
67
|
-
throw new Error('No data detected in the zip archive');
|
|
68
|
-
}
|
|
69
|
-
const cdFileHeader = await this.getCDFileHeader(filename);
|
|
70
|
-
const localFileHeader = await parseZipLocalFileHeader(cdFileHeader.localHeaderOffset, this.fileProvider);
|
|
71
|
-
if (!localFileHeader) {
|
|
72
|
-
throw new Error('Local file header has not been found in the zip archive`');
|
|
73
|
-
}
|
|
74
|
-
const compressionHandler = ZIP_COMPRESSION_HANDLERS[localFileHeader.compressionMethod.toString()];
|
|
75
|
-
if (!compressionHandler) {
|
|
76
|
-
throw Error('Only Deflation compression is supported');
|
|
77
|
-
}
|
|
78
|
-
const compressedFile = await this.fileProvider.slice(localFileHeader.fileDataOffset, localFileHeader.fileDataOffset + localFileHeader.compressedSize);
|
|
79
|
-
uncompressedFile = await compressionHandler(compressedFile);
|
|
60
|
+
/**
|
|
61
|
+
* Get file names list from zip archive
|
|
62
|
+
* @returns array of file names
|
|
63
|
+
*/
|
|
64
|
+
async readdir() {
|
|
65
|
+
if (!this.fileProvider) {
|
|
66
|
+
throw new Error('No data detected in the zip archive');
|
|
67
|
+
}
|
|
68
|
+
const fileNames = [];
|
|
69
|
+
const zipCDIterator = makeZipCDHeaderIterator(this.fileProvider);
|
|
70
|
+
for await (const cdHeader of zipCDIterator) {
|
|
71
|
+
fileNames.push(cdHeader.fileName);
|
|
72
|
+
}
|
|
73
|
+
return fileNames;
|
|
80
74
|
}
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
throw new Error('No data detected in the zip archive');
|
|
75
|
+
/**
|
|
76
|
+
* Get file metadata
|
|
77
|
+
* @param filename - name of a file
|
|
78
|
+
* @returns central directory data
|
|
79
|
+
*/
|
|
80
|
+
async stat(filename) {
|
|
81
|
+
const cdFileHeader = await this.getCDFileHeader(filename);
|
|
82
|
+
return { ...cdFileHeader, size: Number(cdFileHeader.uncompressedSize) };
|
|
90
83
|
}
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
84
|
+
/**
|
|
85
|
+
* Implementation of fetch against this file system
|
|
86
|
+
* @param filename - name of a file
|
|
87
|
+
* @returns - Response with file data
|
|
88
|
+
*/
|
|
89
|
+
async fetch(filename) {
|
|
90
|
+
let uncompressedFile;
|
|
91
|
+
if (this.archive) {
|
|
92
|
+
uncompressedFile = await this.archive.getFile(filename, 'http');
|
|
93
|
+
}
|
|
94
|
+
else {
|
|
95
|
+
if (!this.fileProvider) {
|
|
96
|
+
throw new Error('No data detected in the zip archive');
|
|
97
|
+
}
|
|
98
|
+
const cdFileHeader = await this.getCDFileHeader(filename);
|
|
99
|
+
const localFileHeader = await parseZipLocalFileHeader(cdFileHeader.localHeaderOffset, this.fileProvider);
|
|
100
|
+
if (!localFileHeader) {
|
|
101
|
+
throw new Error('Local file header has not been found in the zip archive`');
|
|
102
|
+
}
|
|
103
|
+
const compressionHandler = ZIP_COMPRESSION_HANDLERS[localFileHeader.compressionMethod.toString()];
|
|
104
|
+
if (!compressionHandler) {
|
|
105
|
+
throw Error('Only Deflation compression is supported');
|
|
106
|
+
}
|
|
107
|
+
const compressedFile = await this.fileProvider.slice(localFileHeader.fileDataOffset, localFileHeader.fileDataOffset + localFileHeader.compressedSize);
|
|
108
|
+
uncompressedFile = await compressionHandler(compressedFile);
|
|
109
|
+
}
|
|
110
|
+
const response = new Response(uncompressedFile);
|
|
111
|
+
Object.defineProperty(response, 'url', {
|
|
112
|
+
value: filename ? `${this.fileName || ''}/${filename}` : this.fileName || ''
|
|
113
|
+
});
|
|
114
|
+
return response;
|
|
98
115
|
}
|
|
99
|
-
|
|
100
|
-
|
|
116
|
+
/**
|
|
117
|
+
* Get central directory file header
|
|
118
|
+
* @param filename - name of a file
|
|
119
|
+
* @returns central directory file header
|
|
120
|
+
*/
|
|
121
|
+
async getCDFileHeader(filename) {
|
|
122
|
+
if (!this.fileProvider) {
|
|
123
|
+
throw new Error('No data detected in the zip archive');
|
|
124
|
+
}
|
|
125
|
+
const zipCDIterator = makeZipCDHeaderIterator(this.fileProvider);
|
|
126
|
+
let result = null;
|
|
127
|
+
for await (const cdHeader of zipCDIterator) {
|
|
128
|
+
if (cdHeader.fileName === filename) {
|
|
129
|
+
result = cdHeader;
|
|
130
|
+
break;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
if (!result) {
|
|
134
|
+
throw new Error('File has not been found in the zip archive');
|
|
135
|
+
}
|
|
136
|
+
return result;
|
|
101
137
|
}
|
|
102
|
-
return result;
|
|
103
|
-
}
|
|
104
138
|
}
|
|
105
|
-
//# sourceMappingURL=zip-filesystem.js.map
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { FileProvider } from '@loaders.gl/loader-utils';
|
|
2
|
-
import { ZipCDFileHeader } from
|
|
2
|
+
import { ZipCDFileHeader } from "./parse-zip/cd-file-header.js";
|
|
3
3
|
/**
|
|
4
4
|
* Reads hash file from buffer and returns it in ready-to-use form
|
|
5
5
|
* @param arrayBuffer - buffer containing hash file
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hash-file-utility.d.ts","sourceRoot":"","sources":["../src/hash-file-utility.ts"],"names":[],"mappings":"AAKA,OAAO,EACL,YAAY,EAGb,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAAC,eAAe,EAA0B,
|
|
1
|
+
{"version":3,"file":"hash-file-utility.d.ts","sourceRoot":"","sources":["../src/hash-file-utility.ts"],"names":[],"mappings":"AAKA,OAAO,EACL,YAAY,EAGb,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAAC,eAAe,EAA0B,sCAAmC;AAEpF;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,WAAW,EAAE,WAAW,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAY/E;AASD;;;;GAIG;AACH,wBAAsB,2BAA2B,CAC/C,YAAY,EAAE,YAAY,GACzB,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAGjC;AAED;;;;GAIG;AACH,wBAAsB,YAAY,CAChC,aAAa,EAAE,aAAa,CAAC,eAAe,CAAC,GAC5C,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAcjC;AAED,mCAAmC;AACnC,KAAK,YAAY,GAAG;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,iBAAiB,EAAE,MAAM,CAAC;CAC3B,CAAC;AAEF;;;;GAIG;AACH,wBAAsB,eAAe,CACnC,aAAa,EAAE,aAAa,CAAC,YAAY,CAAC,GAAG,QAAQ,CAAC,YAAY,CAAC,GAClE,OAAO,CAAC,WAAW,CAAC,CAwBtB"}
|
|
@@ -1,62 +1,105 @@
|
|
|
1
|
+
// loaders.gl
|
|
2
|
+
// SPDX-License-Identifier: MIT
|
|
3
|
+
// Copyright (c) vis.gl contributors
|
|
1
4
|
import { MD5Hash } from '@loaders.gl/crypto';
|
|
2
5
|
import { concatenateArrayBuffers, concatenateArrayBuffersFromArray } from '@loaders.gl/loader-utils';
|
|
3
6
|
import { makeZipCDHeaderIterator } from "./parse-zip/cd-file-header.js";
|
|
7
|
+
/**
|
|
8
|
+
* Reads hash file from buffer and returns it in ready-to-use form
|
|
9
|
+
* @param arrayBuffer - buffer containing hash file
|
|
10
|
+
* @returns Map containing hash and offset
|
|
11
|
+
*/
|
|
4
12
|
export function parseHashTable(arrayBuffer) {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
+
const dataView = new DataView(arrayBuffer);
|
|
14
|
+
const hashMap = {};
|
|
15
|
+
for (let i = 0; i < arrayBuffer.byteLength; i = i + 24) {
|
|
16
|
+
const offset = dataView.getBigUint64(i + 16, true);
|
|
17
|
+
const hash = bufferToHex(arrayBuffer, i, 16);
|
|
18
|
+
hashMap[hash] = offset;
|
|
19
|
+
}
|
|
20
|
+
return hashMap;
|
|
13
21
|
}
|
|
14
22
|
function bufferToHex(buffer, start, length) {
|
|
15
|
-
|
|
23
|
+
// buffer is an ArrayBuffer
|
|
24
|
+
return [...new Uint8Array(buffer, start, length)]
|
|
25
|
+
.map((x) => x.toString(16).padStart(2, '0'))
|
|
26
|
+
.join('');
|
|
16
27
|
}
|
|
28
|
+
/**
|
|
29
|
+
* generates hash info from zip files "central directory"
|
|
30
|
+
* @param fileProvider - provider of the archive
|
|
31
|
+
* @returns ready to use hash info
|
|
32
|
+
*/
|
|
17
33
|
export async function makeHashTableFromZipHeaders(fileProvider) {
|
|
18
|
-
|
|
19
|
-
|
|
34
|
+
const zipCDIterator = makeZipCDHeaderIterator(fileProvider);
|
|
35
|
+
return getHashTable(zipCDIterator);
|
|
20
36
|
}
|
|
37
|
+
/**
|
|
38
|
+
* creates hash table from file offset iterator
|
|
39
|
+
* @param zipCDIterator iterator to use
|
|
40
|
+
* @returns hash table
|
|
41
|
+
*/
|
|
21
42
|
export async function getHashTable(zipCDIterator) {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
43
|
+
const md5Hash = new MD5Hash();
|
|
44
|
+
const textEncoder = new TextEncoder();
|
|
45
|
+
const hashTable = {};
|
|
46
|
+
for await (const cdHeader of zipCDIterator) {
|
|
47
|
+
const filename = cdHeader.fileName.split('\\').join('/').toLocaleLowerCase();
|
|
48
|
+
const arrayBuffer = textEncoder.encode(filename).buffer;
|
|
49
|
+
const md5 = await md5Hash.hash(arrayBuffer, 'hex');
|
|
50
|
+
hashTable[md5] = cdHeader.localHeaderOffset;
|
|
51
|
+
}
|
|
52
|
+
return hashTable;
|
|
32
53
|
}
|
|
54
|
+
/**
|
|
55
|
+
* creates hash file that later can be added to the SLPK archive
|
|
56
|
+
* @param zipCDIterator iterator to use
|
|
57
|
+
* @returns ArrayBuffer containing hash file
|
|
58
|
+
*/
|
|
33
59
|
export async function composeHashFile(zipCDIterator) {
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
60
|
+
const md5Hash = new MD5Hash();
|
|
61
|
+
const textEncoder = new TextEncoder();
|
|
62
|
+
const hashArray = [];
|
|
63
|
+
for await (const cdHeader of zipCDIterator) {
|
|
64
|
+
let filename = cdHeader.fileName.split('\\').join('/');
|
|
65
|
+
// I3S edge case. All files should be lower case by spec. However, ArcGIS
|
|
66
|
+
// and official i3s_converter https://github.com/Esri/i3s-spec/blob/master/i3s_converter/i3s_converter_ReadMe.md
|
|
67
|
+
// expect `3dSceneLayer.json.gz` in camel case
|
|
68
|
+
if (filename !== '3dSceneLayer.json.gz') {
|
|
69
|
+
filename = filename.toLocaleLowerCase();
|
|
70
|
+
}
|
|
71
|
+
const arrayBuffer = textEncoder.encode(filename).buffer;
|
|
72
|
+
const md5 = await md5Hash.hash(arrayBuffer, 'hex');
|
|
73
|
+
hashArray.push(concatenateArrayBuffers(hexStringToBuffer(md5), bigintToBuffer(cdHeader.localHeaderOffset)));
|
|
41
74
|
}
|
|
42
|
-
const
|
|
43
|
-
|
|
44
|
-
hashArray.push(concatenateArrayBuffers(hexStringToBuffer(md5), bigintToBuffer(cdHeader.localHeaderOffset)));
|
|
45
|
-
}
|
|
46
|
-
const bufferArray = hashArray.sort(compareHashes);
|
|
47
|
-
return concatenateArrayBuffersFromArray(bufferArray);
|
|
75
|
+
const bufferArray = hashArray.sort(compareHashes);
|
|
76
|
+
return concatenateArrayBuffersFromArray(bufferArray);
|
|
48
77
|
}
|
|
78
|
+
/**
|
|
79
|
+
* Function to compare md5 hashes according to https://github.com/Esri/i3s-spec/blob/master/docs/2.0/slpk_hashtable.pcsl.md
|
|
80
|
+
* @param arrA first hash to compare
|
|
81
|
+
* @param arrB second hash to compare
|
|
82
|
+
* @returns 0 if equal, negative number if a<b, pozitive if a>b
|
|
83
|
+
*/
|
|
49
84
|
function compareHashes(arrA, arrB) {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
85
|
+
const a = new BigUint64Array(arrA);
|
|
86
|
+
const b = new BigUint64Array(arrB);
|
|
87
|
+
return Number(a[0] === b[0] ? a[1] - b[1] : a[0] - b[0]);
|
|
53
88
|
}
|
|
89
|
+
/**
|
|
90
|
+
* converts hex string to buffer
|
|
91
|
+
* @param str hex string to convert
|
|
92
|
+
* @returns conversion result
|
|
93
|
+
*/
|
|
54
94
|
function hexStringToBuffer(str) {
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
return new Uint8Array(byteArray !== null && byteArray !== void 0 ? byteArray : new Array(16)).buffer;
|
|
95
|
+
const byteArray = str.match(/../g)?.map((h) => parseInt(h, 16));
|
|
96
|
+
return new Uint8Array(byteArray ?? new Array(16)).buffer;
|
|
58
97
|
}
|
|
98
|
+
/**
|
|
99
|
+
* converts bigint to buffer
|
|
100
|
+
* @param n bigint to convert
|
|
101
|
+
* @returns convertion result
|
|
102
|
+
*/
|
|
59
103
|
function bigintToBuffer(n) {
|
|
60
|
-
|
|
104
|
+
return new BigUint64Array([n]).buffer;
|
|
61
105
|
}
|
|
62
|
-
//# sourceMappingURL=hash-file-utility.js.map
|