mirror of
https://github.com/Zagrios/bs-manager.git
synced 2026-07-03 14:08:25 +02:00
[bugfix] created a get files' buffer from zip helper
This commit is contained in:
@@ -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)) {
|
||||
|
||||
@@ -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,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"));
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user