From 11b17ce57db1af8d85cd5280cbd056c741ca955b Mon Sep 17 00:00:00 2001 From: BlackAsLight <44320105+BlackAsLight@users.noreply.github.com> Date: Thu, 22 Feb 2024 15:28:56 +1100 Subject: [PATCH 1/2] Added ZipCompressionStream & ZipDecompressionStream --- index.d.ts | 123 ++++++++++++++++++++++++++++++++++--- index.js | 8 ++- lib/core/zip-compress.js | 37 +++++++++++ lib/core/zip-decompress.js | 42 +++++++++++++ lib/zip-fs.js | 8 ++- 5 files changed, 205 insertions(+), 13 deletions(-) create mode 100644 lib/core/zip-compress.js create mode 100644 lib/core/zip-decompress.js diff --git a/index.d.ts b/index.d.ts index bfe65f50f..667d3d15e 100644 --- a/index.d.ts +++ b/index.d.ts @@ -242,7 +242,7 @@ declare class SyncCodec { } /** - * Represents an intance used to compress data. + * Represents an instance used to compress data. */ declare class ZipDeflate extends SyncCodec { /** @@ -599,7 +599,7 @@ export class SplitDataWriter implements Initializable, WritableWriter { /** * Creates the {@link SplitDataWriter} instance * - * @param writerGenerator A generator of Writer isntances. + * @param writerGenerator A generator of Writer instances. * @param maxSize The maximum size of the data written into {@link Writer} instances (default: 4GB). */ constructor( @@ -616,6 +616,40 @@ export class SplitDataWriter implements Initializable, WritableWriter { */ export class Uint8ArrayWriter extends Writer {} +/** + * Represents an instance used to create an unzipped stream. + * + * @example + * This example will take a zip file, decompress it and then recompress each file in it, saving it to disk. + * ``` + * for await (const entry of (await fetch(urlToZippedFile)).body.pipeThrough(new ZipDecompressionStream())) + * if (entry.readable) { + * console.log(entry.filename) + * entry.readable + * .pipeThrough(ZipCompressionStream().transform(entry.filename)) + * .pipeTo((await Deno.create(entry.filename + '.zip')).writable) + * } + * ``` + */ +export class ZipDecompressionStream { + /** + * Creates the stream. + * + * @param options The options. + */ + constructor (options?: ZipReaderConstructorOptions); + + /** + * The readonly property for the readable stream. + */ + get readable(): ReadableStream & { readable?: ReadableStream; }>; + + /** + * The readonly property for the writable stream. + */ + get writable(): WritableStream; +} + /** * Represents an instance used to read a zip file. * @@ -688,7 +722,7 @@ export class ZipReader { * Returns a generator used to iterate on all the entries in the zip file * * @param options The options. - * @returns An asynchrounous generator of {@link Entry} instances. + * @returns An asynchronous generator of {@link Entry} instances. */ getEntriesGenerator( options?: ZipReaderGetEntriesOptions, @@ -947,6 +981,81 @@ interface EntryGetDataOptions interface EntryGetDataCheckPasswordOptions extends EntryGetDataOptions, ZipReaderCheckPasswordOptions {} +/** + * Represents an instance used to create a zipped stream. + * + * @example + * This example creates a zipped file called numbers.txt.zip containing the numbers 0 - 1000 each on their own line. + * ``` + * const readable = ReadableStream.from((function* () { + * for (let i = 0; i < 1000; ++i) + * yield i + '\n' + * })()) + * + * readable + * .pipeThrough(new ZipCompressionStream().transform('numbers.txt')) + * .pipeTo((await Deno.create('numbers.txt.zip')).writable) + * ``` + * + * @example + * This example creates a zipped file called Archive.zip containing two files called numbers.txt and letters.txt + * ``` + * const readable1 = ReadableStream.from((function* () { + * for (let i = 0; i < 1000; ++i) + * yield i + '\n' + * })()) + * const readable2 = ReadableStream.from((function* () { + * const letters = 'abcdefghijklmnopqrstuvwxyz'.split('') + * while (letters.length) + * yield letters.shift() + '\n' + * })()) + * + * const zipper = new ZipCompressionStream() + * zipper.readable.pipeTo((await Deno.create('Archive.zip')).writable) + * readable1.pipeTo(zipper.writable('numbers.txt')) + * readable2.pipeTo(zipper.writable('letters.txt')) + * zipper.close() + * ``` + */ +export class ZipCompressionStream { + /** + * Creates the stream. + * + * @param options The options. + */ + constructor (options?: ZipWriterConstructorOptions); + + /** + * The readonly property for the readable stream. + */ + get readable(): ReadableStream; + + /** + * Returns an object containing a readable and writable property for the .pipeThrough method + * + * @param path The name of the stream when unzipped. + * @returns An object containing readable and writable properties + */ + transform(path: string): { readable: ReadableStream, writable: WritableStream; }; + + /** + * Returns a WritableStream for the .pipeTo method + * + * @param path The directory path of where the stream should exist in the zipped stream. + * @returns A WritableStream. + */ + writable(path: string): WritableStream; + + /** + * Writes the entries directory, writes the global comment, and returns the content of the zipped file. + * + * @param comment The global comment of the zip file. + * @param options The options. + * @returns The content of the zip file. + */ + close(comment?: Uint8Array, options?: ZipWriterCloseOptions): Promise; +} + /** * Represents an instance used to create a zip file. * @@ -1324,7 +1433,7 @@ declare class ZipEntry { /** * Set the name of the entry * - * @param name The new name of the netry. + * @param name The new name of the entry. */ rename(name: string): void; } @@ -1487,7 +1596,7 @@ export class ZipDirectoryEntry extends ZipEntry { options?: ZipWriterAddDataOptions, ): ZipFileEntry; /** - * Adds aentry entry with content provided as a `Blob` instance + * Adds a entry entry with content provided as a `Blob` instance * * @param name The relative filename of the entry. * @param blob The `Blob` instance. @@ -1500,7 +1609,7 @@ export class ZipDirectoryEntry extends ZipEntry { options?: ZipWriterAddDataOptions, ): ZipFileEntry; /** - * Adds aentry entry with content provided as a Data URI `string` encoded in Base64 + * Adds a entry entry with content provided as a Data URI `string` encoded in Base64 * * @param name The relative filename of the entry. * @param dataURI The Data URI `string` encoded in Base64. @@ -1539,7 +1648,7 @@ export class ZipDirectoryEntry extends ZipEntry { options?: HttpOptions & ZipWriterAddDataOptions, ): ZipFileEntry; /** - * Adds aentry entry with content provided via a `ReadableStream` instance + * Adds a entry entry with content provided via a `ReadableStream` instance * * @param name The relative filename of the entry. * @param readable The `ReadableStream` instance. diff --git a/index.js b/index.js index 43820c6cc..ac3e6826f 100644 --- a/index.js +++ b/index.js @@ -9,8 +9,8 @@ 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - 2. Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. The names of the authors may not be used to endorse or promote products @@ -42,6 +42,8 @@ export { initShimAsyncCodec, ZipReader, ZipWriter, + ZipDecompressionStream, + ZipCompressionStream, Reader, Writer, TextReader, @@ -83,4 +85,4 @@ export { ERR_SPLIT_ZIP_FILE, ERR_ITERATOR_COMPLETED_TOO_SOON } from "./lib/zip-fs.js"; -export { getMimeType, terminateWorkers }; \ No newline at end of file +export { getMimeType, terminateWorkers }; diff --git a/lib/core/zip-compress.js b/lib/core/zip-compress.js new file mode 100644 index 000000000..9e2c6f51e --- /dev/null +++ b/lib/core/zip-compress.js @@ -0,0 +1,37 @@ +import { ZipWriter } from "./zip-writer"; + +class ZipCompressionStream { + #readable + #zipWriter + constructor (options = {}) { + const { readable, writable } = new TransformStream(); + this.#readable = readable; + this.#zipWriter = new ZipWriter(writable, options); + } + + get readable() { + return this.#readable; + } + + transform(path) { + const { readable, writable } = new TransformStream({ + flush: () => { this.#zipWriter.close(); } + }); + this.#zipWriter.add(path, readable); + return { readable: this.#readable, writable }; + } + + writable(path) { + const { readable, writable } = new TransformStream(); + this.#zipWriter.add(path, readable); + return writable; + } + + close(comment = undefined, options = {}) { + return this.#zipWriter.close(comment, options); + } +} + +export { + ZipCompressionStream +}; diff --git a/lib/core/zip-decompress.js b/lib/core/zip-decompress.js new file mode 100644 index 000000000..285f98f2d --- /dev/null +++ b/lib/core/zip-decompress.js @@ -0,0 +1,42 @@ +import { ZipReader } from "./zip-reader"; + +class ZipDecompressionStream { + #readable + #writable + constructor (options = {}) { + const { readable, writable } = new TransformStream(); + const gen = new ZipReader(readable, options).getEntriesGenerator(); + this.#readable = new ReadableStream({ + async pull(controller) { + const { done, value } = await gen.next(); + if (done) + return controller.close(); + const chunk = { + ...value, + readable: (function () { + const { readable, writable } = new TransformStream(); + if (value.getData) { + value.getData(writable); + return readable; + } + })() + }; + delete chunk.getData; + controller.enqueue(chunk); + } + }) + this.#writable = writable; + } + + get readable() { + return this.#readable; + } + + get writable() { + return this.#writable; + } +} + +export { + ZipDecompressionStream +}; diff --git a/lib/zip-fs.js b/lib/zip-fs.js index d66a95aec..4b0c5a95c 100644 --- a/lib/zip-fs.js +++ b/lib/zip-fs.js @@ -7,8 +7,8 @@ 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - 2. Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. The names of the authors may not be used to endorse or promote products @@ -44,10 +44,12 @@ configureWebWorker(configure); export * from "./core/io.js"; export * from "./core/zip-reader.js"; export * from "./core/zip-writer.js"; +export * from "./core/zip-decompress.js"; +export * from "./core/zip-compress.js"; export * from "./core/zip-fs-core.js"; export { configure, getMimeType, initShimAsyncCodec, terminateWorkers -}; \ No newline at end of file +}; From 6e4b9ec6a1364d42e9304fa6f6dea474951ad3aa Mon Sep 17 00:00:00 2001 From: BlackAsLight <44320105+BlackAsLight@users.noreply.github.com> Date: Fri, 23 Feb 2024 17:01:56 +1100 Subject: [PATCH 2/2] Changed Class Names, Removed Private Fields, and Merged Files --- index.d.ts | 29 +++++++++++++++----------- index.js | 4 ++-- lib/core/zip-compress.js | 37 --------------------------------- lib/core/zip-decompress.js | 42 -------------------------------------- lib/core/zip-reader.js | 38 ++++++++++++++++++++++++++++++---- lib/core/zip-writer.js | 35 ++++++++++++++++++++++++++++--- lib/zip-fs.js | 2 -- 7 files changed, 85 insertions(+), 102 deletions(-) delete mode 100644 lib/core/zip-compress.js delete mode 100644 lib/core/zip-decompress.js diff --git a/index.d.ts b/index.d.ts index 667d3d15e..c2722e92e 100644 --- a/index.d.ts +++ b/index.d.ts @@ -622,16 +622,16 @@ export class Uint8ArrayWriter extends Writer {} * @example * This example will take a zip file, decompress it and then recompress each file in it, saving it to disk. * ``` - * for await (const entry of (await fetch(urlToZippedFile)).body.pipeThrough(new ZipDecompressionStream())) + * for await (const entry of (await fetch(urlToZippedFile)).body.pipeThrough(new ZipWriterStream())) * if (entry.readable) { * console.log(entry.filename) * entry.readable - * .pipeThrough(ZipCompressionStream().transform(entry.filename)) + * .pipeThrough(ZipReaderStream().transform(entry.filename)) * .pipeTo((await Deno.create(entry.filename + '.zip')).writable) * } * ``` */ -export class ZipDecompressionStream { +export class ZipReaderStream { /** * Creates the stream. * @@ -640,14 +640,14 @@ export class ZipDecompressionStream { constructor (options?: ZipReaderConstructorOptions); /** - * The readonly property for the readable stream. + * The readable stream. */ - get readable(): ReadableStream & { readable?: ReadableStream; }>; + readable: ReadableStream & { readable?: ReadableStream; }>; /** - * The readonly property for the writable stream. + * The writable stream. */ - get writable(): WritableStream; + writable: WritableStream; } /** @@ -993,7 +993,7 @@ interface EntryGetDataCheckPasswordOptions * })()) * * readable - * .pipeThrough(new ZipCompressionStream().transform('numbers.txt')) + * .pipeThrough(new ZipWriterStream().transform('numbers.txt')) * .pipeTo((await Deno.create('numbers.txt.zip')).writable) * ``` * @@ -1010,14 +1010,14 @@ interface EntryGetDataCheckPasswordOptions * yield letters.shift() + '\n' * })()) * - * const zipper = new ZipCompressionStream() + * const zipper = new ZipWriterStream() * zipper.readable.pipeTo((await Deno.create('Archive.zip')).writable) * readable1.pipeTo(zipper.writable('numbers.txt')) * readable2.pipeTo(zipper.writable('letters.txt')) * zipper.close() * ``` */ -export class ZipCompressionStream { +export class ZipWriterStream { /** * Creates the stream. * @@ -1026,9 +1026,14 @@ export class ZipCompressionStream { constructor (options?: ZipWriterConstructorOptions); /** - * The readonly property for the readable stream. + * The readable stream. */ - get readable(): ReadableStream; + readable: ReadableStream; + + /** + * The ZipWriter property. + */ + zipWriter: ZipWriter /** * Returns an object containing a readable and writable property for the .pipeThrough method diff --git a/index.js b/index.js index ac3e6826f..8784bd906 100644 --- a/index.js +++ b/index.js @@ -42,8 +42,8 @@ export { initShimAsyncCodec, ZipReader, ZipWriter, - ZipDecompressionStream, - ZipCompressionStream, + ZipReaderStream, + ZipWriterStream, Reader, Writer, TextReader, diff --git a/lib/core/zip-compress.js b/lib/core/zip-compress.js deleted file mode 100644 index 9e2c6f51e..000000000 --- a/lib/core/zip-compress.js +++ /dev/null @@ -1,37 +0,0 @@ -import { ZipWriter } from "./zip-writer"; - -class ZipCompressionStream { - #readable - #zipWriter - constructor (options = {}) { - const { readable, writable } = new TransformStream(); - this.#readable = readable; - this.#zipWriter = new ZipWriter(writable, options); - } - - get readable() { - return this.#readable; - } - - transform(path) { - const { readable, writable } = new TransformStream({ - flush: () => { this.#zipWriter.close(); } - }); - this.#zipWriter.add(path, readable); - return { readable: this.#readable, writable }; - } - - writable(path) { - const { readable, writable } = new TransformStream(); - this.#zipWriter.add(path, readable); - return writable; - } - - close(comment = undefined, options = {}) { - return this.#zipWriter.close(comment, options); - } -} - -export { - ZipCompressionStream -}; diff --git a/lib/core/zip-decompress.js b/lib/core/zip-decompress.js deleted file mode 100644 index 285f98f2d..000000000 --- a/lib/core/zip-decompress.js +++ /dev/null @@ -1,42 +0,0 @@ -import { ZipReader } from "./zip-reader"; - -class ZipDecompressionStream { - #readable - #writable - constructor (options = {}) { - const { readable, writable } = new TransformStream(); - const gen = new ZipReader(readable, options).getEntriesGenerator(); - this.#readable = new ReadableStream({ - async pull(controller) { - const { done, value } = await gen.next(); - if (done) - return controller.close(); - const chunk = { - ...value, - readable: (function () { - const { readable, writable } = new TransformStream(); - if (value.getData) { - value.getData(writable); - return readable; - } - })() - }; - delete chunk.getData; - controller.enqueue(chunk); - } - }) - this.#writable = writable; - } - - get readable() { - return this.#readable; - } - - get writable() { - return this.#writable; - } -} - -export { - ZipDecompressionStream -}; diff --git a/lib/core/zip-reader.js b/lib/core/zip-reader.js index 8d76bcbbc..2c3f534e0 100644 --- a/lib/core/zip-reader.js +++ b/lib/core/zip-reader.js @@ -7,8 +7,8 @@ 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - 2. Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. The names of the authors may not be used to endorse or promote products @@ -209,7 +209,7 @@ class ZipReader { } if (directoryDataOffset >= reader.size) { prependedDataLength = reader.size - directoryDataOffset - directoryDataLength - END_OF_CENTRAL_DIR_LENGTH; - directoryDataOffset = reader.size - directoryDataLength - END_OF_CENTRAL_DIR_LENGTH; + directoryDataOffset = reader.size - directoryDataLength - END_OF_CENTRAL_DIR_LENGTH; } if (expectedLastDiskNumber != lastDiskNumber) { throw new Error(ERR_SPLIT_ZIP_FILE); @@ -326,8 +326,38 @@ class ZipReader { } } +class ZipReaderStream { + readable; + writable; + constructor (options = {}) { + const { readable, writable } = new TransformStream(); + const gen = new ZipReader(readable, options).getEntriesGenerator(); + this.readable = new ReadableStream({ + async pull(controller) { + const { done, value } = await gen.next(); + if (done) + return controller.close(); + const chunk = { + ...value, + readable: (function () { + const { readable, writable } = new TransformStream(); + if (value.getData) { + value.getData(writable); + return readable; + } + })() + }; + delete chunk.getData; + controller.enqueue(chunk); + } + }); + this.writable = writable; + } +} + export { ZipReader, + ZipReaderStream, ERR_BAD_FORMAT, ERR_EOCDR_NOT_FOUND, ERR_EOCDR_ZIP64_NOT_FOUND, @@ -721,4 +751,4 @@ function setUint32(view, offset, value) { function getDataView(array) { return new DataView(array.buffer); -} \ No newline at end of file +} diff --git a/lib/core/zip-writer.js b/lib/core/zip-writer.js index 78778f8ae..573bd2ee5 100644 --- a/lib/core/zip-writer.js +++ b/lib/core/zip-writer.js @@ -7,8 +7,8 @@ 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - 2. Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. The names of the authors may not be used to endorse or promote products @@ -176,8 +176,37 @@ class ZipWriter { } } +class ZipWriterStream { + readable; + zipWriter; + constructor (options = {}) { + const { readable, writable } = new TransformStream(); + this.readable = readable; + this.zipWriter = new ZipWriter(writable, options); + } + + transform(path) { + const { readable, writable } = new TransformStream({ + flush: () => { this.zipWriter.close(); } + }); + this.zipWriter.add(path, readable); + return { readable: this.readable, writable }; + } + + writable(path) { + const { readable, writable } = new TransformStream(); + this.zipWriter.add(path, readable); + return writable; + } + + close(comment = undefined, options = {}) { + return this.zipWriter.close(comment, options); + } +} + export { ZipWriter, + ZipWriterStream, ERR_DUPLICATED_NAME, ERR_INVALID_COMMENT, ERR_INVALID_ENTRY_NAME, @@ -1148,4 +1177,4 @@ function getLength(...arrayLikes) { let result = 0; arrayLikes.forEach(arrayLike => arrayLike && (result += arrayLike.length)); return result; -} \ No newline at end of file +} diff --git a/lib/zip-fs.js b/lib/zip-fs.js index 4b0c5a95c..204b4049b 100644 --- a/lib/zip-fs.js +++ b/lib/zip-fs.js @@ -44,8 +44,6 @@ configureWebWorker(configure); export * from "./core/io.js"; export * from "./core/zip-reader.js"; export * from "./core/zip-writer.js"; -export * from "./core/zip-decompress.js"; -export * from "./core/zip-compress.js"; export * from "./core/zip-fs-core.js"; export { configure,