1- /* global XMLHttpRequest,  Blob */ 
1+ /* global Blob */ 
22import  CoreManager  from  './CoreManager' ; 
33import  type  {  FullOptions  }  from  './RESTController' ; 
44import  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- } 
145
156type  Base64  =  {  base64 : string  } ; 
167type  Uri  =  {  uri : string  } ; 
@@ -151,18 +142,29 @@ class ParseFile {
151142   * Data is present if initialized with Byte Array, Base64 or Saved with Uri. 
152143   * Data is cleared if saved with File object selected with a file upload control 
153144   * 
145+    * @param  {object } options 
146+    * @param  {function } [options.progress] callback for download progress 
147+    * <pre> 
148+    * const parseFile = new Parse.File(name, file); 
149+    * parseFile.getData({ 
150+    *   progress: (progressValue, loaded, total) => { 
151+    *     if (progressValue !== null) { 
152+    *       // Update the UI using progressValue 
153+    *     } 
154+    *   } 
155+    * }); 
156+    * </pre> 
154157   * @returns  {Promise } Promise that is resolve with base64 data 
155158   */ 
156-   async  getData ( ) : Promise < string >  { 
159+   async  getData ( options ) : Promise < string >  { 
160+     options  =  options  ||  { } ; 
157161    if  ( this . _data )  { 
158162      return  this . _data ; 
159163    } 
160164    if  ( ! this . _url )  { 
161165      throw  new  Error ( 'Cannot retrieve data for unsaved ParseFile.' ) ; 
162166    } 
163-     const  options  =  { 
164-       requestTask : task  =>  ( this . _requestTask  =  task ) , 
165-     } ; 
167+     options . requestTask  =  task  =>  ( this . _requestTask  =  task ) ; 
166168    const  controller  =  CoreManager . getFileController ( ) ; 
167169    const  result  =  await  controller . download ( this . _url ,  options ) ; 
168170    this . _data  =  result . base64 ; 
@@ -227,12 +229,12 @@ class ParseFile {
227229   *     be used for this request. 
228230   *   <li>sessionToken: A valid session token, used for making a request on 
229231   *     behalf of a specific user. 
230-    *   <li>progress: In Browser only,  callback for upload progress. For example: 
232+    *   <li>progress: callback for upload progress. For example: 
231233   * <pre> 
232234   * let parseFile = new Parse.File(name, file); 
233235   * parseFile.save({ 
234-    *   progress: (progressValue, loaded, total, { type } ) => { 
235-    *     if (type === "upload" &&  progressValue !== null) { 
236+    *   progress: (progressValue, loaded, total) => { 
237+    *     if (progressValue !== null) { 
236238   *       // Update the UI using progressValue 
237239   *     } 
238240   *   } 
@@ -479,58 +481,50 @@ const DefaultController = {
479481    return  CoreManager . getRESTController ( ) . request ( 'POST' ,  path ,  data ,  options ) ; 
480482  } , 
481483
482-   download : function  ( uri ,  options )  { 
483-     if  ( XHR )  { 
484-       return  this . downloadAjax ( uri ,  options ) ; 
485-     }  else  if  ( process . env . PARSE_BUILD  ===  'node' )  { 
486-       return  new  Promise ( ( resolve ,  reject )  =>  { 
487-         const  client  =  uri . indexOf ( 'https' )  ===  0  ? require ( 'https' )  : require ( 'http' ) ; 
488-         const  req  =  client . get ( uri ,  resp  =>  { 
489-           resp . setEncoding ( 'base64' ) ; 
490-           let  base64  =  '' ; 
491-           resp . on ( 'data' ,  data  =>  ( base64  +=  data ) ) ; 
492-           resp . on ( 'end' ,  ( )  =>  { 
493-             resolve ( { 
494-               base64, 
495-               contentType : resp . headers [ 'content-type' ] , 
496-             } ) ; 
497-           } ) ; 
498-         } ) ; 
499-         req . on ( 'abort' ,  ( )  =>  { 
500-           resolve ( { } ) ; 
501-         } ) ; 
502-         req . on ( 'error' ,  reject ) ; 
503-         options . requestTask ( req ) ; 
504-       } ) ; 
505-     }  else  { 
506-       return  Promise . reject ( 'Cannot make a request: No definition of XMLHttpRequest was found.' ) ; 
507-     } 
508-   } , 
509- 
510-   downloadAjax : function  ( uri : string ,  options : any )  { 
511-     return  new  Promise ( ( resolve ,  reject )  =>  { 
512-       const  xhr  =  new  XHR ( ) ; 
513-       xhr . open ( 'GET' ,  uri ,  true ) ; 
514-       xhr . responseType  =  'arraybuffer' ; 
515-       xhr . onerror  =  function  ( e )  { 
516-         reject ( e ) ; 
517-       } ; 
518-       xhr . onreadystatechange  =  function  ( )  { 
519-         if  ( xhr . readyState  !==  xhr . DONE )  { 
520-           return ; 
521-         } 
522-         if  ( ! this . response )  { 
523-           return  resolve ( { } ) ; 
484+   download : async  function  ( uri ,  options )  { 
485+     const  controller  =  new  AbortController ( ) ; 
486+     options . requestTask ( controller ) ; 
487+     const  {  signal }  =  controller ; 
488+     try  { 
489+       const  response  =  await  fetch ( uri ,  {  signal } ) ; 
490+       const  reader  =  response . body . getReader ( ) ; 
491+       const  length  =  + response . headers . get ( 'Content-Length' )  ||  0 ; 
492+       const  contentType  =  response . headers . get ( 'Content-Type' ) ; 
493+       if  ( length  ===  0 )  { 
494+         options . progress ?.( null ,  null ,  null ) ; 
495+         return  { 
496+           base64 : '' , 
497+           contentType, 
498+         } ; 
499+       } 
500+       let  recieved  =  0 ; 
501+       const  chunks  =  [ ] ; 
502+       while  ( true )  { 
503+         const  {  done,  value }  =  await  reader . read ( ) ; 
504+         if  ( done )  { 
505+           break ; 
524506        } 
525-         const  bytes  =  new  Uint8Array ( this . response ) ; 
526-         resolve ( { 
527-           base64 : ParseFile . encodeBase64 ( bytes ) , 
528-           contentType : xhr . getResponseHeader ( 'content-type' ) , 
529-         } ) ; 
507+         chunks . push ( value ) ; 
508+         recieved  +=  value ?. length  ||  0 ; 
509+         options . progress ?.( recieved  /  length ,  recieved ,  length ) ; 
510+       } 
511+       const  body  =  new  Uint8Array ( recieved ) ; 
512+       let  offset  =  0 ; 
513+       for  ( const  chunk  of  chunks )  { 
514+         body . set ( chunk ,  offset ) ; 
515+         offset  +=  chunk . length ; 
516+       } 
517+       return  { 
518+         base64 : ParseFile . encodeBase64 ( body ) , 
519+         contentType, 
530520      } ; 
531-       options . requestTask ( xhr ) ; 
532-       xhr . send ( ) ; 
533-     } ) ; 
521+     }  catch  ( error )  { 
522+       if  ( error . name  ===  'AbortError' )  { 
523+         return  { } ; 
524+       }  else  { 
525+         throw  error ; 
526+       } 
527+     } 
534528  } , 
535529
536530  deleteFile : function  ( name : string ,  options ?: FullOptions )  { 
@@ -549,21 +543,13 @@ const DefaultController = {
549543      . ajax ( 'DELETE' ,  url ,  '' ,  headers ) 
550544      . catch ( response  =>  { 
551545        // TODO: return JSON object in server 
552-         if  ( ! response  ||  response  ===  'SyntaxError: Unexpected end of JSON input' )  { 
546+         if  ( ! response  ||  response . toString ( )  ===  'SyntaxError: Unexpected end of JSON input' )  { 
553547          return  Promise . resolve ( ) ; 
554548        }  else  { 
555549          return  CoreManager . getRESTController ( ) . handleError ( response ) ; 
556550        } 
557551      } ) ; 
558552  } , 
559- 
560-   _setXHR ( xhr : any )  { 
561-     XHR  =  xhr ; 
562-   } , 
563- 
564-   _getXHR ( )  { 
565-     return  XHR ; 
566-   } , 
567553} ; 
568554
569555CoreManager . setFileController ( DefaultController ) ; 
0 commit comments