[bugfix] created a get files' buffer from zip helper

This commit is contained in:
silentrald
2024-11-10 11:23:13 +08:00
parent dd08f418d5
commit d0831f2e44
4 changed files with 102 additions and 40 deletions
+16 -3
View File
@@ -1,11 +1,12 @@
import path from "path";
import { mkdir, pathExistsSync, rm } from "fs-extra";
import { extractZip } from "main/helpers/zip.helpers";
import { mkdir, pathExistsSync, readFile, rm } from "fs-extra";
import { extractZip, getFilesFromZip } from "main/helpers/zip.helpers";
const TEST_FOLDER = path.resolve(__dirname, "../../..", "assets", "tests");
const STANDARD_ZIP = path.join(TEST_FOLDER, "standard.zip");
const WINDOWS_LEGACY_MAP_ZIP = path.join(TEST_FOLDER, "windows_legacy.zip");
const SPECIAL_ZIP = path.join(TEST_FOLDER, "special.zip");
const MANIFEST_ZIP = path.join(TEST_FOLDER, "manifest.zip");
const DESTINATION_FOLDER = path.join(TEST_FOLDER, "out");
describe("Zip Server Service Test", () => {
@@ -82,7 +83,19 @@ describe("Zip Server Service Test", () => {
]
expect(beforeExtracted).toEqual(expected);
expect(afterExtracted).toEqual(expected);
})
});
it("Extract manifest.json from zip file", async () => {
const zipBuffer = await readFile(MANIFEST_ZIP);
const buffers = await getFilesFromZip(zipBuffer, ["manifest.json"]);
const manifest = JSON.parse(buffers["manifest.json"].toString());
expect(manifest).toBeTruthy();
// Check some of the fields if they are correct
expect(manifest.appId).toBe("some-app-id");
expect(manifest.canonicalName).toBe("some-canonical-name");
expect(manifest.isCore).toBe(true);
});
afterAll(async () => {
if (pathExistsSync(DESTINATION_FOLDER)) {
+80 -21
View File
@@ -28,19 +28,28 @@ export interface ZipProcessOptions {
getBuffer?: boolean;
};
const YAUZL_OPTIONS: yauzl.Options = {
lazyEntries: true,
decodeStrings: true,
};
function openZip(data: string | Buffer, callback: (error: Error, zip: yauzl.ZipFile) => void) {
if (typeof data === "string") {
yauzl.open(data, YAUZL_OPTIONS, callback);
} else {
yauzl.fromBuffer(data, YAUZL_OPTIONS, callback);
}
}
export async function extractZip(
zipPath: string,
data: string | Buffer,
destination: string,
options?: Readonly<ZipExtractOptions>
): Promise<string[]> {
await ensureFolderExist(destination);
return new Promise((resolve, reject) => {
yauzl.open(zipPath, {
lazyEntries: true,
decodeStrings: true,
},
(openError, zip) => {
openZip(data, (openError: Error, zip: yauzl.ZipFile) => {
if (openError) return reject(openError);
handleExtractZip(zip, destination, options, resolve, reject);
});
@@ -99,7 +108,8 @@ function handleExtractZip(
if (options?.terminate?.(zipEntry)) {
readStream.destroy();
return zip.close();
zip.close();
return resolve(files);
}
if (options?.condition) {
@@ -130,6 +140,64 @@ function handleExtractZip(
});
}
export function getFilesFromZip(
data: string | Buffer,
files: string[]
): Promise<Record<string, Buffer>> {
return new Promise((resolve, reject) => {
openZip(data, (openError: Error, zip: yauzl.ZipFile) => {
if (openError) return reject(openError);
handleGetFilesFromZip(zip, files, resolve, reject);
});
});
}
function handleGetFilesFromZip(
zip: yauzl.ZipFile,
files: string[],
resolve: (buffers: Record<string, Buffer>) => void,
reject: (error: Error) => void
) {
const buffers: Record<string, Buffer> = {};
let count = 0;
zip.readEntry();
zip.on("entry", (entry: yauzl.Entry) => {
// Entry is a directory / folder
if (entry.fileName.endsWith("/") || !files.includes(entry.fileName)) {
return zip.readEntry();
}
// Entry is in the files list
zip.openReadStream(entry, (readError, readStream) => {
if (readError) return reject(readError);
const chunks: Buffer[] = [];
readStream.on("data", data => {
chunks.push(data);
});
readStream.on("end", () => {
buffers[entry.fileName] = Buffer.concat(chunks);
++count;
if (count === files.length) {
zip.close();
return resolve(buffers);
}
zip.readEntry();
});
});
});
zip.once("end", () => {
zip.close();
resolve(buffers);
});
}
// @params loop - this should not throw an error
export function processZip<T>(
data: string | Buffer,
@@ -138,32 +206,23 @@ export function processZip<T>(
initValue: T
): Promise<T> {
return new Promise((resolve, reject) => {
const callback = (openError: Error, zip: yauzl.ZipFile) => {
openZip(data, (openError: Error, zip: yauzl.ZipFile) => {
if (openError) return reject(openError);
handleProcessZip<T>(
zip, options, loop, initValue,
resolve, reject
);
}
if (typeof data === "string") {
yauzl.open(data, {
lazyEntries: true,
decodeStrings: true,
}, callback);
} else {
yauzl.fromBuffer(data, callback);
}
});
});
}
async function handleProcessZip<T>(
function handleProcessZip<T>(
zip: yauzl.ZipFile,
options: Readonly<ZipProcessOptions>,
loop: (accumulator: T, entry: ZipEntry) => T,
accumulator: T,
resolve: (result: T) => void, reject: (error: Error) => void
): Promise<void> {
): void {
const zipEntry: ZipEntry = {
name: "",
directory: false,
@@ -191,7 +250,7 @@ async function handleProcessZip<T>(
if (options?.terminate?.(zipEntry)) {
readStream.destroy();
zip.close();
return;
return resolve(accumulator);
}
const chunks: Buffer[] = [];
+6 -16
View File
@@ -6,7 +6,7 @@ import { inflate } from "pako"
import { EMPTY, Observable, ReplaySubject, Subscriber, catchError, filter, from, lastValueFrom, mergeMap, scan, share, tap } from "rxjs";
import { Progression, hashFile } from "../helpers/fs.helpers";
import { OculusDownloaderErrorCodes } from "../../shared/models/bs-version-download/oculus-download.model";
import { processZip } from "main/helpers/zip.helpers";
import { getFilesFromZip } from "main/helpers/zip.helpers";
export class OculusDownloader {
@@ -34,24 +34,14 @@ export class OculusDownloader {
const buffer = await this.downloadManifestZip(downloadUrl)
.catch(err => CustomError.throw(err, "DOWNLOAD_MANIFEST_FAILED"));
let found = false;
let manifestString: string | null = null;
// NOTE: Might be better to do get file in zip.helpers
await processZip(buffer, {
terminate: () => found,
getBuffer: true,
}, (_, entry): void => {
if (entry.name !== "manifest.json") return;
found = true;
manifestString = entry.buffer.toString();
}, undefined);
if(!manifestString) {
const manifestName = "manifest.json";
const buffers = await getFilesFromZip(buffer, [manifestName]);
const manifest = buffers[manifestName];
if(!manifest) {
throw new CustomError("Manifest file not found", "MANIFEST_FILE_NOT_FOUND");
}
return JSON.parse(manifestString)
return JSON.parse(manifest.toString())
.catch((err: Error) => CustomError.throw(err, "PARSE_MANIFEST_FILE_FAILED"));
}