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 ) ;
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+ } ) ;
1451 } ) ;
1552 }
1653
@@ -23,3 +60,14 @@ export class FileSystem {
2360 return JSON . parse ( content . toString ( ) ) ;
2461 }
2562}
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