瀏覽代碼

Add Blob size support #44

Ben Hsieh 8 年之前
父節點
當前提交
d1bcfbf8b0
共有 3 個檔案被更改,包括 68 行新增24 行删除
  1. 16
    7
      src/index.js
  2. 51
    15
      src/polyfill/Blob.js
  3. 1
    2
      src/polyfill/XMLHttpRequest.js

+ 16
- 7
src/index.js 查看文件

@@ -13,7 +13,8 @@ import {
13 13
 import type {
14 14
   RNFetchBlobNative,
15 15
   RNFetchBlobConfig,
16
-  RNFetchBlobStream
16
+  RNFetchBlobStream,
17
+  RNFetchBlobResponseInfo
17 18
 } from './types'
18 19
 import fs from './fs'
19 20
 import getUUID from './utils/uuid'
@@ -142,7 +143,7 @@ function fetch(...args:any):Promise {
142 143
     }
143 144
 
144 145
     let req = RNFetchBlob[nativeMethodName]
145
-    req(options, taskId, method, url, headers || {}, body, (err, data) => {
146
+    req(options, taskId, method, url, headers || {}, body, (err, info, data) => {
146 147
 
147 148
       // task done, remove event listener
148 149
       subscription.remove()
@@ -151,14 +152,16 @@ function fetch(...args:any):Promise {
151 152
       if(err)
152 153
         reject(new Error(err, data))
153 154
       else {
154
-        let respType = 'base64'
155
+        let rnfbEncode = 'base64'
155 156
         // response data is saved to storage
156 157
         if(options.path || options.fileCache || options.addAndroidDownloads || options.key) {
157
-          respType = 'path'
158
+          rnfbEncode = 'path'
158 159
           if(options.session)
159 160
             session(options.session).add(data)
160 161
         }
161
-        resolve(new FetchBlobResponse(taskId, respType, data))
162
+        info = Object.assign({}, info, { rnfbEncode })
163
+
164
+        resolve(new FetchBlobResponse(taskId, info, data))
162 165
       }
163 166
     })
164 167
 
@@ -204,16 +207,22 @@ class FetchBlobResponse {
204 207
   json : () => any;
205 208
   base64 : () => any;
206 209
   flush : () => void;
210
+  respInfo : RNFetchBlobResponseInfo;
207 211
   session : (name:string) => RNFetchBlobSession | null;
208 212
   readFile : (encode: 'base64' | 'utf8' | 'ascii') => ?Promise;
209 213
   readStream : (
210 214
     encode: 'utf8' | 'ascii' | 'base64',
211 215
   ) => RNFetchBlobStream | null;
212 216
 
213
-  constructor(taskId:string, type:'base64' | 'path', data:any) {
217
+  constructor(taskId:string, info:RNFetchBlobResponseInfo, data:any) {
214 218
     this.data = data
215 219
     this.taskId = taskId
216
-    this.type = type
220
+    this.type = info.rnfbEncode
221
+    this.respInfo = info
222
+
223
+    this.info = ():RNFetchBlobResponseInfo => {
224
+      return this.respInfo
225
+    }
217 226
     /**
218 227
      * Convert result to javascript Blob object.
219 228
      * @param  {string} contentType MIME type of the blob object.

+ 51
- 15
src/polyfill/Blob.js 查看文件

@@ -27,10 +27,24 @@ export default class Blob {
27 27
   _blobCreated:boolean = false;
28 28
   _onCreated:Array<any> = [];
29 29
 
30
-  static Instances:any = {}
30
+  /**
31
+   * Static method that remove all files in Blob cache folder.
32
+   * @nonstandard
33
+   * @return {Promise}
34
+   */
35
+  static clearCache() {
36
+    return fs.unlink(blobCacheDir).then(() => fs.mkdir(blobCacheDir))
37
+  }
31 38
 
32
-  // legacy constructor
33
-  constructor(data:any, mime:?string) {
39
+  /**
40
+   * RNFetchBlob Blob polyfill, create a Blob directly from file path, BASE64
41
+   * encoded data, and string. The conversion is done implicitly according to
42
+   * given `mime`. However, the blob creation is asynchronously, to register
43
+   * event `onCreated` is need to ensure the Blob is creadted.
44
+   * @param  {any} data Content of Blob object
45
+   * @param  {string} mime Content type of Blob object, `text/plain` by default
46
+   */
47
+  constructor(data:any, mime='text/plain':?string) {
34 48
 
35 49
     this.cacheName = getBlobName()
36 50
     this.isRNFetchBlobPolyfill = true
@@ -38,22 +52,28 @@ export default class Blob {
38 52
     log.verbose('Blob constructor called', 'mime', mime)
39 53
     this._ref = blobCacheDir + this.cacheName
40 54
     let p = null
41
-    // content from file
55
+    // if the data is a string starts with `RNFetchBlob-file://`, append the
56
+    // Blob data from file path
42 57
     if(typeof data === 'string' && data.startsWith('RNFetchBlob-file://')) {
43 58
       log.verbose('create Blob cache file from file path')
44 59
       this._ref = data
45
-      p = Promise.resolve()
60
+      p = fs.stat(data.replace('RNFetchBlob-file://'))
61
+            .then((stat) =>  Promise.resolve(stat.size))
46 62
     }
47 63
     // content from variable need create file
48 64
     else if(typeof data === 'string') {
49 65
       log.verbose('create Blob cache file from string')
50 66
       let encoding = 'utf8'
51
-      if(String(mime).match('application/octet'))
67
+      let mime = String(mime)
68
+      // when content type contains application/octet* or *;base64, RNFetchBlob
69
+      // fs will treat it as BASE64 encoded string binary data
70
+      if(mime.match(/application\/octet/i) || mime.match(/\;base64/i))
52 71
         encoding = 'base64'
53
-      else if(Array.isArray(data))
54
-        encoding = 'ascii'
72
+      else
73
+        data = data.toString()
55 74
       // create cache file
56 75
       p = fs.writeFile(this._ref, data, encoding)
76
+            .then((size) => Promise.resolve(size))
57 77
 
58 78
     }
59 79
     // when input is an array of mixed data types, create a file cache
@@ -61,21 +81,34 @@ export default class Blob {
61 81
       log.verbose('create Blob cache file from mixed array', data)
62 82
       p = createMixedBlobData(this._ref, data)
63 83
     }
64
-    p && p.then(() => {
84
+    else {
85
+      data = data.toString()
86
+      p = Promise.resolve(data.length)
87
+    }
88
+    p && p.then((size) => {
89
+      this.size = size
65 90
       this._invokeOnCreateEvent()
66 91
     })
67 92
     .catch((err) => {
68
-      log.error('RNFetchBlob cannot create Blob : '+ this._ref)
93
+      log.error('RNFetchBlob could not create Blob : '+ this._ref, err)
69 94
     })
70 95
 
71 96
   }
72 97
 
98
+  /**
99
+   * Since Blob content will asynchronously write to a file during creation,
100
+   * use this method to register an event handler for Blob initialized event.
101
+   * @nonstandard
102
+   * @param  {[type]} fn:( [description]
103
+   * @return {[type]}      [description]
104
+   */
73 105
   onCreated(fn:() => void) {
74 106
     log.verbose('register blob onCreated', this._onCreated.length)
75 107
     this._onCreated.push(fn)
76 108
   }
77 109
 
78 110
   /**
111
+   * Get file reference of the Blob object.
79 112
    * @nonstandard
80 113
    * @return {string} Blob file reference which can be consumed by RNFetchBlob fs
81 114
    */
@@ -92,6 +125,7 @@ export default class Blob {
92 125
    */
93 126
   slice(start:?number, end:?number, encoding:?string):Blob {
94 127
     log.verbose('slice called')
128
+    // TODO : fs.slice
95 129
     // return fs.slice(this.cacheName, getBlobName(), contentType, start, end)
96 130
   }
97 131
 
@@ -104,10 +138,6 @@ export default class Blob {
104 138
     return fs.unlink(this._ref)
105 139
   }
106 140
 
107
-  clearCache() {
108
-
109
-  }
110
-
111 141
   _invokeOnCreateEvent() {
112 142
     log.verbose('invoke create event')
113 143
     this._blobCreated = true
@@ -139,6 +169,7 @@ function getBlobName() {
139 169
 function createMixedBlobData(ref, dataArray) {
140 170
   let p = fs.writeFile(ref, '')
141 171
   let args = []
172
+  let size = 0
142 173
   for(let i in dataArray) {
143 174
     let part = dataArray[i]
144 175
     if(part instanceof Blob)
@@ -152,6 +183,11 @@ function createMixedBlobData(ref, dataArray) {
152 183
     let promises = args.map((p) => {
153 184
       return fs.appendFile.call(this, ...p)
154 185
     })
155
-    return Promise.all(promises)
186
+    return Promise.all(promises).then((sizes) => {
187
+      for(let i in sizes) {
188
+        size += sizes[i]
189
+      }
190
+      return Promise.resolve(size)
191
+    })
156 192
   })
157 193
 }

+ 1
- 2
src/polyfill/XMLHttpRequest.js 查看文件

@@ -222,12 +222,11 @@ export default class XMLHttpRequest extends XMLHttpRequestEventTarget{
222 222
             this._response = resp.json()
223 223
         }
224 224
         else {
225
-          this._responseType = resp.text()
225
+          this._responseText = resp.text()
226 226
           this._response = this.responseText
227 227
         }
228 228
       break;
229 229
       case 'path' :
230
-        this.responseType = 'blob'
231 230
         this.response = resp.blob()
232 231
       break;
233 232
     }