Skip to content

Commit ba6e42c

Browse files
committed
perf: Optimize decompression performance
1 parent 055fe97 commit ba6e42c

File tree

1 file changed

+26
-19
lines changed

1 file changed

+26
-19
lines changed

src/unzip.ts

Lines changed: 26 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -97,16 +97,13 @@ interface IEntryContext {
9797
* The name of the symlink file that has been processed.
9898
*/
9999
readonly symlinkFileNames: string[];
100-
/**
101-
* The name of the symlink folder that has been processed.
102-
*/
103-
readonly symlinkFolders: { folder: string, realpath: string }[];
104100
getFilePath(): string;
105101
/**
106102
* Whether the specified path is outside the target folder
107103
* @param tpath
108104
*/
109105
isOutsideTargetFolder(tpath: string): Promise<boolean>;
106+
ensureFolder(folder: string): Promise<void>;
110107
}
111108

112109
class EntryContext implements IEntryContext {
@@ -115,6 +112,7 @@ class EntryContext implements IEntryContext {
115112
private symlinkAsFileOnWindows: boolean) {
116113
this._symlinkFileNames = [];
117114
this._symlinkFolders = [];
115+
this._ensuredFolders = [];
118116
}
119117
private _decodeEntryFileName: string;
120118
public get decodeEntryFileName(): string {
@@ -133,33 +131,48 @@ class EntryContext implements IEntryContext {
133131
public get symlinkFileNames(): string[] {
134132
return this._symlinkFileNames;
135133
}
136-
private _symlinkFolders: { folder: string, realpath: string }[];
137-
public get symlinkFolders(): { folder: string, realpath: string }[] {
138-
return this._symlinkFolders;
134+
private _symlinkFolders: { folder: string, realpath: string }[]
135+
private _ensuredFolders: string[];
136+
137+
private addSymlinkFolder(folder: string, realpath: string): void {
138+
const find = this._symlinkFolders.find((item) => item.folder === folder);
139+
if (!find) {
140+
this._symlinkFolders.push({ folder, realpath });
141+
}
139142
}
140143

141144
public getFilePath(): string {
142145
return path.join(this.targetFolder, this.decodeEntryFileName);
143146
}
144147

148+
public async ensureFolder(folder: string): Promise<void> {
149+
if (this._ensuredFolders.includes(folder)) {
150+
return;
151+
}
152+
const folderStat = await exfs.ensureFolder(folder);
153+
if (folderStat.isSymbolicLink) {
154+
this.addSymlinkFolder(folder, folderStat.realpath!);
155+
}
156+
this._ensuredFolders.push(folder);
157+
}
158+
145159
public async isOutsideTargetFolder(tpath: string): Promise<boolean> {
146-
if (this.symlinkFileNames.length === 0 &&
147-
this.symlinkFolders.length === 0
148-
) {
160+
if (this._symlinkFileNames.length === 0 &&
161+
this._symlinkFolders.length === 0) {
149162
return false;
150163
}
151164
if (process.platform === "win32" &&
152165
this.symlinkAsFileOnWindows) {
153166
return false;
154167
}
155-
for (const { folder, realpath } of this.symlinkFolders) {
168+
for (const { folder, realpath } of this._symlinkFolders) {
156169
if (tpath.includes(folder)) {
157170
if (realpath.indexOf(this.realTargetFolder) !== 0) {
158171
return true;
159172
}
160173
}
161174
}
162-
for (const fileName of this.symlinkFileNames) {
175+
for (const fileName of this._symlinkFileNames) {
163176
if (tpath.includes(fileName)) {
164177
const realFilePath = await exfs.realpath(tpath);
165178
if (realFilePath.indexOf(this.realTargetFolder) !== 0) {
@@ -336,13 +349,7 @@ export class Unzip extends Cancelable {
336349
private async extractEntry(zfile: yauzl.ZipFile, entry: yauzl.Entry, entryContext: IEntryContext, token: CancellationToken): Promise<void> {
337350
const filePath = entryContext.getFilePath();
338351
const fileDir = path.dirname(filePath);
339-
const folderStat = await exfs.ensureFolder(fileDir);
340-
if (folderStat.isSymbolicLink) {
341-
entryContext.symlinkFolders.push({
342-
folder: fileDir,
343-
realpath: folderStat.realpath!,
344-
})
345-
}
352+
await entryContext.ensureFolder(fileDir);
346353
const outside = await entryContext.isOutsideTargetFolder(fileDir);
347354
if (outside) {
348355
const error = new Error(`Refuse to write file outside "${entryContext.targetFolder}", file: "${filePath}"`);

0 commit comments

Comments
 (0)