11import * 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 ) ;
38
49export class FileSystem {
510 public exists ( path : string ) : boolean {
611 return fs . existsSync ( path ) ;
712 }
813
914 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 ) ;
14- } ) ;
15+ return new Promise ( ( resolve , reject ) => {
16+ yauzl . open ( pathToZip , { autoClose : true , lazyEntries : true } , ( e , zipFile ) => {
17+ if ( e ) return reject ( e ) ;
18+
19+ zipFile . on ( 'entry' , entry => {
20+ const fn = < string > entry . fileName ;
21+ if ( / \/ $ / . test ( fn ) ) {
22+ return zipFile . readEntry ( ) ;
23+ }
24+
25+ zipFile . openReadStream ( entry , ( err , stream ) => {
26+ if ( err ) return reject ( err ) ;
27+
28+ const filePath = `${ outputDir } /${ fn } ` ;
29+
30+ return createParentDirsIfNeeded ( filePath )
31+ . catch ( e => reject ( e ) )
32+ . then ( ( ) => {
33+ const outfile = createOutfile ( filePath ) ;
34+ stream . once ( 'end' , ( ) => {
35+ zipFile . readEntry ( ) ;
36+ outfile . close ( ) ;
37+ } ) ;
38+ stream . pipe ( outfile ) ;
39+ } ) ;
40+ } ) ;
41+ } ) ;
42+
43+ zipFile . once ( 'end' , ( ) => resolve ( ) ) ;
44+
45+ zipFile . readEntry ( ) ;
46+ } ) ;
47+ } )
1548 }
1649
1750 public readDirectory ( path : string ) : string [ ] {
@@ -23,3 +56,18 @@ export class FileSystem {
2356 return JSON . parse ( content . toString ( ) ) ;
2457 }
2558}
59+
60+ function createParentDirsIfNeeded ( filePath : string ) {
61+ const dirs = path . dirname ( filePath ) . split ( path . sep ) ;
62+ return dirs . reduce ( ( p , dir , index ) => p . then ( parent => {
63+ const current = `${ parent } ${ path . sep } ${ dir } ` ;
64+
65+ return access ( current )
66+ . catch ( e => mkdir ( current ) )
67+ . then ( ( ) => current ) ;
68+ } ) , Promise . resolve ( '' ) ) ;
69+ }
70+
71+ function createOutfile ( path : string ) : fs . WriteStream {
72+ return fs . createWriteStream ( path ) ;
73+ }
0 commit comments