1
- /* global XMLHttpRequest, Blob */
1
+ /* global Blob */
2
2
import CoreManager from './CoreManager' ;
3
3
import type { FullOptions } from './RESTController' ;
4
4
import ParseError from './ParseError' ;
5
- import XhrWeapp from './Xhr.weapp' ;
6
-
7
- let XHR : any = null ;
8
- if ( typeof XMLHttpRequest !== 'undefined' ) {
9
- XHR = XMLHttpRequest ;
10
- }
11
- if ( process . env . PARSE_BUILD === 'weapp' ) {
12
- XHR = XhrWeapp ;
13
- }
14
5
15
6
interface Base64 {
16
7
base64 : string ;
@@ -155,18 +146,29 @@ class ParseFile {
155
146
* Data is present if initialized with Byte Array, Base64 or Saved with Uri.
156
147
* Data is cleared if saved with File object selected with a file upload control
157
148
*
149
+ * @param {object } options
150
+ * @param {function } [options.progress] callback for download progress
151
+ * <pre>
152
+ * const parseFile = new Parse.File(name, file);
153
+ * parseFile.getData({
154
+ * progress: (progressValue, loaded, total) => {
155
+ * if (progressValue !== null) {
156
+ * // Update the UI using progressValue
157
+ * }
158
+ * }
159
+ * });
160
+ * </pre>
158
161
* @returns {Promise } Promise that is resolve with base64 data
159
162
*/
160
- async getData ( ) : Promise < string > {
163
+ async getData ( options ?: { progress ?: ( ) => void } ) : Promise < string > {
164
+ options = options || { } ;
161
165
if ( this . _data ) {
162
166
return this . _data ;
163
167
}
164
168
if ( ! this . _url ) {
165
169
throw new Error ( 'Cannot retrieve data for unsaved ParseFile.' ) ;
166
170
}
167
- const options = {
168
- requestTask : task => ( this . _requestTask = task ) ,
169
- } ;
171
+ ( options as any ) . requestTask = task => ( this . _requestTask = task ) ;
170
172
const controller = CoreManager . getFileController ( ) ;
171
173
const result = await controller . download ( this . _url , options ) ;
172
174
this . _data = result . base64 ;
@@ -231,12 +233,12 @@ class ParseFile {
231
233
* be used for this request.
232
234
* <li>sessionToken: A valid session token, used for making a request on
233
235
* behalf of a specific user.
234
- * <li>progress: In Browser only, callback for upload progress. For example:
236
+ * <li>progress: callback for upload progress. For example:
235
237
* <pre>
236
238
* let parseFile = new Parse.File(name, file);
237
239
* parseFile.save({
238
- * progress: (progressValue, loaded, total, { type } ) => {
239
- * if (type === "upload" && progressValue !== null) {
240
+ * progress: (progressValue, loaded, total) => {
241
+ * if (progressValue !== null) {
240
242
* // Update the UI using progressValue
241
243
* }
242
244
* }
@@ -483,58 +485,50 @@ const DefaultController = {
483
485
return CoreManager . getRESTController ( ) . request ( 'POST' , path , data , options ) ;
484
486
} ,
485
487
486
- download : function ( uri , options ) {
487
- if ( XHR ) {
488
- return this . downloadAjax ( uri , options ) ;
489
- } else if ( process . env . PARSE_BUILD === 'node' ) {
490
- return new Promise ( ( resolve , reject ) => {
491
- const client = uri . indexOf ( 'https' ) === 0 ? require ( 'https' ) : require ( 'http' ) ;
492
- const req = client . get ( uri , resp => {
493
- resp . setEncoding ( 'base64' ) ;
494
- let base64 = '' ;
495
- resp . on ( 'data' , data => ( base64 += data ) ) ;
496
- resp . on ( 'end' , ( ) => {
497
- resolve ( {
498
- base64,
499
- contentType : resp . headers [ 'content-type' ] ,
500
- } ) ;
501
- } ) ;
502
- } ) ;
503
- req . on ( 'abort' , ( ) => {
504
- resolve ( { } ) ;
505
- } ) ;
506
- req . on ( 'error' , reject ) ;
507
- options . requestTask ( req ) ;
508
- } ) ;
509
- } else {
510
- return Promise . reject ( 'Cannot make a request: No definition of XMLHttpRequest was found.' ) ;
511
- }
512
- } ,
513
-
514
- downloadAjax : function ( uri : string , options : any ) {
515
- return new Promise ( ( resolve , reject ) => {
516
- const xhr = new XHR ( ) ;
517
- xhr . open ( 'GET' , uri , true ) ;
518
- xhr . responseType = 'arraybuffer' ;
519
- xhr . onerror = function ( e ) {
520
- reject ( e ) ;
521
- } ;
522
- xhr . onreadystatechange = function ( ) {
523
- if ( xhr . readyState !== xhr . DONE ) {
524
- return ;
525
- }
526
- if ( ! this . response ) {
527
- return resolve ( { } ) ;
488
+ download : async function ( uri , options ) {
489
+ const controller = new AbortController ( ) ;
490
+ options . requestTask ( controller ) ;
491
+ const { signal } = controller ;
492
+ try {
493
+ const response = await fetch ( uri , { signal } ) ;
494
+ const reader = response . body . getReader ( ) ;
495
+ const length = + response . headers . get ( 'Content-Length' ) || 0 ;
496
+ const contentType = response . headers . get ( 'Content-Type' ) ;
497
+ if ( length === 0 ) {
498
+ options . progress ?.( null , null , null ) ;
499
+ return {
500
+ base64 : '' ,
501
+ contentType,
502
+ } ;
503
+ }
504
+ let recieved = 0 ;
505
+ const chunks = [ ] ;
506
+ while ( true ) {
507
+ const { done, value } = await reader . read ( ) ;
508
+ if ( done ) {
509
+ break ;
528
510
}
529
- const bytes = new Uint8Array ( this . response ) ;
530
- resolve ( {
531
- base64 : ParseFile . encodeBase64 ( bytes ) ,
532
- contentType : xhr . getResponseHeader ( 'content-type' ) ,
533
- } ) ;
511
+ chunks . push ( value ) ;
512
+ recieved += value ?. length || 0 ;
513
+ options . progress ?.( recieved / length , recieved , length ) ;
514
+ }
515
+ const body = new Uint8Array ( recieved ) ;
516
+ let offset = 0 ;
517
+ for ( const chunk of chunks ) {
518
+ body . set ( chunk , offset ) ;
519
+ offset += chunk . length ;
520
+ }
521
+ return {
522
+ base64 : ParseFile . encodeBase64 ( body ) ,
523
+ contentType,
534
524
} ;
535
- options . requestTask ( xhr ) ;
536
- xhr . send ( ) ;
537
- } ) ;
525
+ } catch ( error ) {
526
+ if ( error . name === 'AbortError' ) {
527
+ return { } ;
528
+ } else {
529
+ throw error ;
530
+ }
531
+ }
538
532
} ,
539
533
540
534
deleteFile : function ( name : string , options ?: FullOptions ) {
@@ -553,21 +547,13 @@ const DefaultController = {
553
547
. ajax ( 'DELETE' , url , '' , headers )
554
548
. catch ( response => {
555
549
// TODO: return JSON object in server
556
- if ( ! response || response === 'SyntaxError: Unexpected end of JSON input' ) {
550
+ if ( ! response || response . toString ( ) === 'SyntaxError: Unexpected end of JSON input' ) {
557
551
return Promise . resolve ( ) ;
558
552
} else {
559
553
return CoreManager . getRESTController ( ) . handleError ( response ) ;
560
554
}
561
555
} ) ;
562
556
} ,
563
-
564
- _setXHR ( xhr : any ) {
565
- XHR = xhr ;
566
- } ,
567
-
568
- _getXHR ( ) {
569
- return XHR ;
570
- } ,
571
557
} ;
572
558
573
559
CoreManager . setFileController ( DefaultController ) ;
0 commit comments