@@ -36,17 +36,27 @@ export class FilesRouter {
36
36
const config = new Config ( req . params . appId ) ;
37
37
const filesController = config . filesController ;
38
38
const filename = req . params . filename ;
39
- filesController . getFileData ( config , filename ) . then ( ( data ) => {
40
- res . status ( 200 ) ;
41
- var contentType = mime . lookup ( filename ) ;
42
- res . set ( 'Content-Type' , contentType ) ;
43
- res . set ( 'Content-Length' , data . length ) ;
44
- res . end ( data ) ;
45
- } ) . catch ( ( err ) => {
46
- res . status ( 404 ) ;
47
- res . set ( 'Content-Type' , 'text/plain' ) ;
48
- res . end ( 'File not found.' ) ;
49
- } ) ;
39
+ const contentType = mime . lookup ( filename ) ;
40
+ if ( isFileStreamable ( req , filesController ) ) {
41
+ filesController . getFileStream ( config , filename ) . then ( ( stream ) => {
42
+ handleFileStream ( stream , req , res , contentType ) ;
43
+ } ) . catch ( ( err ) => {
44
+ res . status ( 404 ) ;
45
+ res . set ( 'Content-Type' , 'text/plain' ) ;
46
+ res . end ( 'File not found.' ) ;
47
+ } ) ;
48
+ } else {
49
+ filesController . getFileData ( config , filename ) . then ( ( data ) => {
50
+ res . status ( 200 ) ;
51
+ res . set ( 'Content-Type' , contentType ) ;
52
+ res . set ( 'Content-Length' , data . length ) ;
53
+ res . end ( data ) ;
54
+ } ) . catch ( ( err ) => {
55
+ res . status ( 404 ) ;
56
+ res . set ( 'Content-Type' , 'text/plain' ) ;
57
+ res . end ( 'File not found.' ) ;
58
+ } ) ;
59
+ }
50
60
}
51
61
52
62
createHandler ( req , res , next ) {
@@ -94,3 +104,97 @@ export class FilesRouter {
94
104
} ) ;
95
105
}
96
106
}
107
+
108
+ function isFileStreamable ( req , filesController ) {
109
+ if ( req . get [ 'Range' ] ) {
110
+ if ( ! ( typeof filesController . adapter . getFileStream === 'function' ) ) {
111
+ return false ;
112
+ }
113
+ if ( typeof filesController . adapter . constructor . name !== 'undefined' ) {
114
+ if ( filesController . adapter . constructor . name == 'GridStoreAdapter' ) {
115
+ return true ;
116
+ }
117
+ }
118
+ }
119
+ return false ;
120
+ }
121
+
122
+ // handleFileStream is licenced under Creative Commons Attribution 4.0 International License (https://creativecommons.org/licenses/by/4.0/).
123
+ // Author: LEROIB at weightingformypizza (https://weightingformypizza.wordpress.com/2015/06/24/stream-html5-media-content-like-video-audio-from-mongodb-using-express-and-gridstore/).
124
+ function handleFileStream ( stream , req , res , contentType ) {
125
+ var buffer_size = 1024 * 1024 ; //1024Kb
126
+ // Range request, partiall stream the file
127
+ var parts = req . get [ "Range" ] . replace ( / b y t e s = / , "" ) . split ( "-" ) ;
128
+ var partialstart = parts [ 0 ] ;
129
+ var partialend = parts [ 1 ] ;
130
+ var start = partialstart ? parseInt ( partialstart , 10 ) : 0 ;
131
+ var end = partialend ? parseInt ( partialend , 10 ) : stream . length - 1 ;
132
+ var chunksize = ( end - start ) + 1 ;
133
+
134
+ if ( chunksize == 1 ) {
135
+ start = 0 ;
136
+ partialend = false ;
137
+ }
138
+
139
+ if ( ! partialend ) {
140
+ if ( ( ( stream . length - 1 ) - start ) < ( buffer_size ) ) {
141
+ end = stream . length - 1 ;
142
+ } else {
143
+ end = start + ( buffer_size ) ;
144
+ }
145
+ chunksize = ( end - start ) + 1 ;
146
+ }
147
+
148
+ if ( start == 0 && end == 2 ) {
149
+ chunksize = 1 ;
150
+ }
151
+
152
+ res . writeHead ( 206 , {
153
+ 'Content-Range' : 'bytes ' + start + '-' + end + '/' + stream . length ,
154
+ 'Accept-Ranges' : 'bytes' ,
155
+ 'Content-Length' : chunksize ,
156
+ 'Content-Type' : contentType ,
157
+ } ) ;
158
+
159
+ stream . seek ( start , function ( ) {
160
+ // get gridFile stream
161
+ var gridFileStream = stream . stream ( true ) ;
162
+ var ended = false ;
163
+ var bufferIdx = 0 ;
164
+ var bufferAvail = 0 ;
165
+ var range = ( end - start ) + 1 ;
166
+ var totalbyteswanted = ( end - start ) + 1 ;
167
+ var totalbyteswritten = 0 ;
168
+ // write to response
169
+ gridFileStream . on ( 'data' , function ( buff ) {
170
+ bufferAvail += buff . length ;
171
+ //Ok check if we have enough to cover our range
172
+ if ( bufferAvail < range ) {
173
+ //Not enough bytes to satisfy our full range
174
+ if ( bufferAvail > 0 ) {
175
+ //Write full buffer
176
+ res . write ( buff ) ;
177
+ totalbyteswritten += buff . length ;
178
+ range -= buff . length ;
179
+ bufferIdx += buff . length ;
180
+ bufferAvail -= buff . length ;
181
+ }
182
+ } else {
183
+ //Enough bytes to satisfy our full range!
184
+ if ( bufferAvail > 0 ) {
185
+ const buffer = buff . slice ( 0 , range ) ;
186
+ res . write ( buffer ) ;
187
+ totalbyteswritten += buffer . length ;
188
+ bufferIdx += range ;
189
+ bufferAvail -= range ;
190
+ }
191
+ }
192
+ if ( totalbyteswritten >= totalbyteswanted ) {
193
+ //totalbytes = 0;
194
+ stream . close ( ) ;
195
+ res . end ( ) ;
196
+ this . destroy ( ) ;
197
+ }
198
+ } ) ;
199
+ } ) ;
200
+ }
0 commit comments