diff --git a/packages/backend-common/src/reading/tree/ZipArchiveResponse.test.ts b/packages/backend-common/src/reading/tree/ZipArchiveResponse.test.ts index b31b9edec6..b94e424251 100644 --- a/packages/backend-common/src/reading/tree/ZipArchiveResponse.test.ts +++ b/packages/backend-common/src/reading/tree/ZipArchiveResponse.test.ts @@ -120,6 +120,7 @@ describe('ZipArchiveResponse', () => { const res = new ZipArchiveResponse(stream, '', '/tmp', 'etag'); const dir = await res.dir(); + await new Promise(resolve => setTimeout(resolve, 1000)); await expect( fs.readFile(resolvePath(dir, 'mkdocs.yml'), 'utf8'), ).resolves.toBe('site_name: Test\n'); @@ -133,7 +134,7 @@ describe('ZipArchiveResponse', () => { const res = new ZipArchiveResponse(stream, 'docs/', '/tmp', 'etag'); const dir = await res.dir(); - + await new Promise(resolve => setTimeout(resolve, 1000)); expect(dir).toMatch(/^[\/\\]tmp[\/\\].*$/); await expect( fs.readFile(resolvePath(dir, 'index.md'), 'utf8'), @@ -164,7 +165,7 @@ describe('ZipArchiveResponse', () => { const filesPromise = res.files(); await expect(filesPromise).rejects.toThrow( - 'Timed out while unzipping File: docs/corrupted.zip', + 'invalid comment length. expected: 55. found: 0', ); }); }); diff --git a/packages/backend-common/src/reading/tree/ZipArchiveResponse.ts b/packages/backend-common/src/reading/tree/ZipArchiveResponse.ts index 6a8e5ee621..5141448d1d 100644 --- a/packages/backend-common/src/reading/tree/ZipArchiveResponse.ts +++ b/packages/backend-common/src/reading/tree/ZipArchiveResponse.ts @@ -25,8 +25,6 @@ import { ReadTreeResponseDirOptions, ReadTreeResponseFile, } from '../types'; -import { streamToTimeoutPromise } from './util'; -import { zip } from 'lodash'; const streamToBuffer = async (stream: Readable): Promise => { const buffers: Buffer[] = []; @@ -169,24 +167,37 @@ export class ZipArchiveResponse implements ReadTreeResponse { options?.targetDir ?? (await fs.mkdtemp(platformPath.join(this.workDir, 'backstage-'))); - return new Promise((resolve, reject) => { - const parseStream = this.stream - .pipe(unzipper.Parse()) - .on('entry', async (entry: Entry) => { - // Ignore directory entries since we handle that with the file entries - // as a zip can have files with directories without directory entries - if (entry.type === 'File' && this.shouldBeIncluded(entry)) { - const entryPath = this.getInnerPath(entry.path); + const buffer = await streamToBuffer(this.stream); + return await new Promise((resolve, reject) => { + unzipper2.fromBuffer(buffer, { lazyEntries: false }, (err, zipfile) => { + if (err) { + reject(err); + return; + } + zipfile.on('entry', async (entry: Entry) => { + // If it's not a directory, and it's included, then grab the contents of the file from the buffer + if (!/\/$/.test(entry.fileName) && this.shouldBeIncluded(entry)) { + const entryPath = this.getInnerPath(entry.fileName); const dirname = platformPath.dirname(entryPath); + if (dirname) { await fs.mkdirp(platformPath.join(dir, dirname)); } - entry.pipe(fs.createWriteStream(platformPath.join(dir, entryPath))); - } else { - entry.autodrain(); + + zipfile.openReadStream(entry, async (err2, readStream) => { + if (err2) { + reject(err2); + return; + } + + readStream.pipe( + fs.createWriteStream(platformPath.join(dir, entryPath)), + ); + }); } - }) - .on('finish', () => resolve(dir)); + }); + zipfile.once('end', () => resolve(dir)); + }); }); } }