1
1
import * as fs from "fs" ;
2
- import { Extract } from "unzip" ;
2
+ import * as path from "path" ;
3
+ import * as yauzl from "yauzl" ;
4
+ import * as util from "util" ;
5
+
6
+ const access = util . promisify ( fs . access ) ;
7
+ const mkdir = util . promisify ( fs . mkdir ) ;
3
8
4
9
export class FileSystem {
5
10
public exists ( path : string ) : boolean {
6
11
return fs . existsSync ( path ) ;
7
12
}
8
13
9
14
public extractZip ( pathToZip : string , outputDir : string ) : Promise < void > {
10
- return new Promise < void > ( ( resolve , reject ) => {
11
- const stream = fs . createReadStream ( pathToZip ) . pipe ( Extract ( { path : outputDir } ) ) ;
12
- stream . on ( "close" , resolve ) ;
13
- stream . on ( "error" , reject ) ;
15
+ return new Promise ( ( resolve , reject ) => {
16
+ yauzl . open ( pathToZip , { autoClose : true , lazyEntries : true } , ( openError , zipFile ) => {
17
+ if ( openError ) {
18
+ return reject ( openError ) ;
19
+ }
20
+
21
+ zipFile . on ( 'entry' , entry => {
22
+ const fn = < string > entry . fileName ;
23
+ if ( / \/ $ / . test ( fn ) ) {
24
+ return zipFile . readEntry ( ) ;
25
+ }
26
+
27
+ zipFile . openReadStream ( entry , ( openStreamError , stream ) => {
28
+ if ( openStreamError ) {
29
+ return reject ( openStreamError ) ;
30
+ } ;
31
+
32
+ const filePath = `${ outputDir } /${ fn } ` ;
33
+
34
+ return createParentDirsIfNeeded ( filePath )
35
+ . catch ( createParentDirError => reject ( createParentDirError ) )
36
+ . then ( ( ) => {
37
+ const outfile = fs . createWriteStream ( filePath ) ;
38
+ stream . once ( 'end' , ( ) => {
39
+ zipFile . readEntry ( ) ;
40
+ outfile . close ( ) ;
41
+ } ) ;
42
+ stream . pipe ( outfile ) ;
43
+ } ) ;
44
+ } ) ;
45
+ } ) ;
46
+
47
+ zipFile . once ( 'end' , ( ) => resolve ( ) ) ;
48
+
49
+ zipFile . readEntry ( ) ;
50
+ } ) ;
14
51
} ) ;
15
52
}
16
53
@@ -23,3 +60,14 @@ export class FileSystem {
23
60
return JSON . parse ( content . toString ( ) ) ;
24
61
}
25
62
}
63
+
64
+ function createParentDirsIfNeeded ( filePath : string ) {
65
+ const dirs = path . dirname ( filePath ) . split ( path . sep ) ;
66
+ return dirs . reduce ( ( p , dir ) => p . then ( parent => {
67
+ const current = `${ parent } ${ path . sep } ${ dir } ` ;
68
+
69
+ return access ( current )
70
+ . catch ( e => mkdir ( current ) )
71
+ . then ( ( ) => current ) ;
72
+ } ) , Promise . resolve ( '' ) ) ;
73
+ }
0 commit comments