@@ -6,6 +6,9 @@ import ip from './ip';
6
6
import isGzip from './is-gzip' ;
7
7
import utils from './utils' ;
8
8
9
+ const LARGE_FILE_THRESHOLD = 512 * 1024 * 1024 ;
10
+ const STREAM_WATERMARK = 8 * 1024 * 1024 ;
11
+
9
12
type Callback = ( ) => void ;
10
13
11
14
export interface OpenOpts {
@@ -17,27 +20,48 @@ export interface OpenOpts {
17
20
watchForUpdatesHook ?: Callback ;
18
21
}
19
22
20
- const readFile = async ( filepath : string ) : Promise < Buffer > => {
21
- return new Promise ( ( resolve , reject ) => {
22
- const chunks : Buffer [ ] = [ ] ;
23
+ /**
24
+ * Read large file in chunks.
25
+ *
26
+ * Reason it's not used for all file sizes is that it's slower than fs.readFile and uses
27
+ * a bit more memory due to the buffer operations.
28
+ *
29
+ * Node seems to have a limit of 2GB for fs.readFileSync, so we need to use streams for
30
+ * larger files.
31
+ *
32
+ * @param filepath
33
+ * @param size
34
+ * @returns
35
+ */
36
+ const readLargeFile = async ( filepath : string , size : number ) : Promise < Buffer > =>
37
+ new Promise ( ( resolve , reject ) => {
38
+ let buffer = Buffer . allocUnsafe ( size ) ;
39
+ let offset = 0 ;
23
40
const stream = fs . createReadStream ( filepath , {
24
- highWaterMark : 64 * 1024 * 1024 , // 64 MB chunks
41
+ highWaterMark : STREAM_WATERMARK ,
25
42
} ) ;
26
43
27
44
stream . on ( 'data' , ( chunk : Buffer ) => {
28
- chunks . push ( chunk ) ;
45
+ chunk . copy ( buffer , offset ) ;
46
+ offset += chunk . length ;
29
47
} ) ;
30
48
31
49
stream . on ( 'end' , ( ) => {
32
- resolve ( Buffer . concat ( chunks ) ) ;
50
+ stream . close ( ) ;
51
+ resolve ( buffer ) ;
33
52
} ) ;
34
53
35
54
stream . on ( 'error' , ( err ) => {
36
55
reject ( err ) ;
37
56
} ) ;
38
57
} ) ;
39
- } ;
40
58
59
+ const readFile = async ( filepath : string ) : Promise < Buffer > => {
60
+ const fstat = await fs . stat ( filepath ) ;
61
+ return fstat . size < LARGE_FILE_THRESHOLD
62
+ ? fs . readFile ( filepath )
63
+ : readLargeFile ( filepath , fstat . size ) ;
64
+ } ;
41
65
42
66
export const open = async < T extends Response > (
43
67
filepath : string ,
0 commit comments