react-native-blob-util 0.22.0 → 0.22.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintrc.js +56 -56
- package/LICENSE +21 -21
- package/Migration.md +41 -41
- package/NuGet.config +11 -11
- package/README.md +1109 -1104
- package/android/gradle.properties +1 -1
- package/android/src/main/AndroidManifest.xml +37 -37
- package/android/src/main/java/com/ReactNativeBlobUtil/ReactNativeBlobUtilConfig.java +57 -57
- package/android/src/main/java/com/ReactNativeBlobUtil/ReactNativeBlobUtilConst.java +20 -20
- package/android/src/main/java/com/ReactNativeBlobUtil/ReactNativeBlobUtilFileTransformer.java +9 -9
- package/android/src/main/java/com/ReactNativeBlobUtil/ReactNativeBlobUtilImpl.java +431 -431
- package/android/src/main/java/com/ReactNativeBlobUtil/ReactNativeBlobUtilProgressConfig.java +39 -39
- package/android/src/main/java/com/ReactNativeBlobUtil/ReactNativeBlobUtilReq.java +1014 -1014
- package/android/src/main/java/com/ReactNativeBlobUtil/Response/ReactNativeBlobUtilDefaultResp.java +98 -98
- package/android/src/main/java/com/ReactNativeBlobUtil/Response/ReactNativeBlobUtilFileResp.java +166 -166
- package/android/src/main/java/com/ReactNativeBlobUtil/Utils/FileDescription.java +19 -19
- package/android/src/main/java/com/ReactNativeBlobUtil/Utils/FileProvider.java +4 -4
- package/android/src/main/java/com/ReactNativeBlobUtil/Utils/MimeType.java +70 -70
- package/android/src/main/java/com/ReactNativeBlobUtil/Utils/PathResolver.java +223 -223
- package/android/src/main/java/com/ReactNativeBlobUtil/Utils/Tls12SocketFactory.java +68 -68
- package/android/src/main/res/values/strings.xml +3 -3
- package/android/src/main/res/xml/provider_paths.xml +12 -12
- package/android.js +58 -58
- package/class/ReactNativeBlobUtilCanceledFetchError.js +9 -9
- package/class/ReactNativeBlobUtilFile.js +5 -5
- package/class/ReactNativeBlobUtilReadStream.js +84 -84
- package/class/ReactNativeBlobUtilSession.js +68 -68
- package/class/ReactNativeBlobUtilWriteStream.js +50 -50
- package/class/StatefulPromise.js +7 -7
- package/codegenSpecs/NativeBlobUtils.js +86 -86
- package/components/Fetch.onPress.js +10 -10
- package/components/Fetch.when.js +15 -15
- package/fetch.js +350 -350
- package/fs.js +480 -480
- package/index.js +64 -64
- package/index.js.flow +192 -192
- package/index.web.js +1 -1
- package/ios/PrivacyInfo.xcprivacy +28 -28
- package/ios/ReactNativeBlobUtil/ReactNativeBlobUtil.h +55 -55
- package/ios/ReactNativeBlobUtil/ReactNativeBlobUtil.mm +992 -992
- package/ios/ReactNativeBlobUtil.xcodeproj/project.pbxproj +405 -405
- package/ios/ReactNativeBlobUtilConst.h +58 -58
- package/ios/ReactNativeBlobUtilConst.mm +47 -47
- package/ios/ReactNativeBlobUtilFS.h +108 -108
- package/ios/ReactNativeBlobUtilFS.mm +1064 -1064
- package/ios/ReactNativeBlobUtilFileTransformer.h +23 -23
- package/ios/ReactNativeBlobUtilFileTransformer.mm +20 -20
- package/ios/ReactNativeBlobUtilNetwork.h +49 -49
- package/ios/ReactNativeBlobUtilNetwork.mm +159 -159
- package/ios/ReactNativeBlobUtilProgress.h +32 -32
- package/ios/ReactNativeBlobUtilProgress.mm +57 -57
- package/ios/ReactNativeBlobUtilReqBuilder.h +37 -37
- package/ios/ReactNativeBlobUtilReqBuilder.mm +292 -292
- package/ios/ReactNativeBlobUtilRequest.h +48 -48
- package/ios/ReactNativeBlobUtilRequest.mm +584 -584
- package/ios.js +61 -61
- package/json-stream.js +42 -42
- package/lib/oboe-browser.js +2703 -2703
- package/mediacollection.js +36 -36
- package/package.json +68 -68
- package/polyfill/Blob.js +362 -362
- package/polyfill/Event.js +11 -11
- package/polyfill/EventTarget.js +78 -78
- package/polyfill/Fetch.js +219 -219
- package/polyfill/File.js +27 -27
- package/polyfill/FileReader.js +91 -91
- package/polyfill/ProgressEvent.js +32 -32
- package/polyfill/XMLHttpRequest.js +466 -466
- package/polyfill/XMLHttpRequestEventTarget.js +126 -126
- package/polyfill/index.js +11 -11
- package/react-native-blob-util.podspec +48 -48
- package/types.js +69 -69
- package/utils/log.js +40 -40
- package/utils/unicode.js +7 -7
- package/utils/uri.js +33 -33
- package/utils/uuid.js +4 -4
- package/windows/ExperimentalFeatures.props +33 -33
- package/windows/README.md +2 -2
- package/windows/ReactNativeBlobUtil/PropertySheet.props +15 -15
- package/windows/ReactNativeBlobUtil/ReactNativeBlobUtil.cpp +1836 -1836
- package/windows/ReactNativeBlobUtil/ReactNativeBlobUtil.def +3 -3
- package/windows/ReactNativeBlobUtil/ReactNativeBlobUtil.h +372 -372
- package/windows/ReactNativeBlobUtil/ReactNativeBlobUtil.vcxproj +139 -139
- package/windows/ReactNativeBlobUtil/ReactNativeBlobUtil.vcxproj.filters +43 -43
- package/windows/ReactNativeBlobUtil/ReactPackageProvider.cpp +20 -20
- package/windows/ReactNativeBlobUtil/ReactPackageProvider.h +24 -24
- package/windows/ReactNativeBlobUtil/ReactPackageProvider.idl +9 -9
- package/windows/ReactNativeBlobUtil/codegen/.clang-format +1 -1
- package/windows/ReactNativeBlobUtil/codegen/NativeBlobUtilsDataTypes.g.h +42 -42
- package/windows/ReactNativeBlobUtil/codegen/NativeBlobUtilsSpec.g.h +353 -353
- package/windows/ReactNativeBlobUtil/packages.config +3 -3
- package/windows/ReactNativeBlobUtil/packages.lock.json +59 -59
- package/windows/ReactNativeBlobUtil/pch.cpp +1 -1
- package/windows/ReactNativeBlobUtil/pch.h +30 -30
- package/windows/ReactNativeBlobUtil/resource.h +5 -5
- package/windows/ReactNativeBlobUtil/targetver.h +8 -8
- package/windows/ReactNativeBlobUtil.sln +43 -43
package/polyfill/Blob.js
CHANGED
|
@@ -1,362 +1,362 @@
|
|
|
1
|
-
// Copyright 2016 wkh237@github. All rights reserved.
|
|
2
|
-
// Use of this source code is governed by a MIT-style license that can be
|
|
3
|
-
// found in the LICENSE file.
|
|
4
|
-
|
|
5
|
-
import fs from '../fs.js';
|
|
6
|
-
import getUUID from '../utils/uuid';
|
|
7
|
-
import Log from '../utils/log.js';
|
|
8
|
-
import URIUtil from "../utils/uri";
|
|
9
|
-
import EventTarget from './EventTarget';
|
|
10
|
-
|
|
11
|
-
const log = new Log('Blob');
|
|
12
|
-
const blobCacheDir = fs.dirs.DocumentDir + '/ReactNativeBlobUtil-blobs/';
|
|
13
|
-
|
|
14
|
-
log.disable();
|
|
15
|
-
// log.level(3)
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* A ReactNativeBlobUtil style Blob polyfill class, this is a Blob which compatible to
|
|
19
|
-
* Response object attain fron ReactNativeBlobUtil.fetch.
|
|
20
|
-
*/
|
|
21
|
-
export default class Blob extends EventTarget {
|
|
22
|
-
|
|
23
|
-
cacheName: string;
|
|
24
|
-
type: string;
|
|
25
|
-
size: number;
|
|
26
|
-
isReactNativeBlobUtilPolyfill: boolean = true;
|
|
27
|
-
multipartBoundary: string = null;
|
|
28
|
-
|
|
29
|
-
_ref: string = null;
|
|
30
|
-
_blobCreated: boolean = false;
|
|
31
|
-
_onCreated: Array<any> = [];
|
|
32
|
-
_closed: boolean = false;
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* Static method that remove all files in Blob cache folder.
|
|
36
|
-
* @nonstandard
|
|
37
|
-
* @return {Promise}
|
|
38
|
-
*/
|
|
39
|
-
static clearCache() {
|
|
40
|
-
return fs.unlink(blobCacheDir).then(() => fs.mkdir(blobCacheDir));
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
static build(data: any, cType: any): Promise<Blob> {
|
|
44
|
-
return new Promise((resolve, reject) => {
|
|
45
|
-
new Blob(data, cType).onCreated(resolve);
|
|
46
|
-
});
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
get blobPath() {
|
|
50
|
-
return this._ref;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
static setLog(level: number) {
|
|
54
|
-
if (level === -1)
|
|
55
|
-
log.disable();
|
|
56
|
-
else
|
|
57
|
-
log.level(level);
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
/**
|
|
61
|
-
* ReactNativeBlobUtil Blob polyfill, create a Blob directly from file path, BASE64
|
|
62
|
-
* encoded data, and string. The conversion is done implicitly according to
|
|
63
|
-
* given `mime`. However, the blob creation is asynchronously, to register
|
|
64
|
-
* event `onCreated` is need to ensure the Blob is creadted.
|
|
65
|
-
* @param {any} data Content of Blob object
|
|
66
|
-
* @param {any} mime Content type settings of Blob object, `text/plain`
|
|
67
|
-
* by default
|
|
68
|
-
* @param {boolean} defer When this argument set to `true`, blob constructor
|
|
69
|
-
* will not invoke blob created event automatically.
|
|
70
|
-
*/
|
|
71
|
-
constructor(data: any, cType: any, defer: boolean) {
|
|
72
|
-
super();
|
|
73
|
-
cType = cType || {};
|
|
74
|
-
this.cacheName = getBlobName();
|
|
75
|
-
this.isReactNativeBlobUtilPolyfill = true;
|
|
76
|
-
this.isDerived = defer;
|
|
77
|
-
this.type = cType.type || 'text/plain';
|
|
78
|
-
log.verbose('Blob constructor called', 'mime', this.type, 'type', typeof data, 'length', data ? data.length : 0);
|
|
79
|
-
this._ref = blobCacheDir + this.cacheName;
|
|
80
|
-
let p = null;
|
|
81
|
-
if (!data)
|
|
82
|
-
data = '';
|
|
83
|
-
if (data.isReactNativeBlobUtilPolyfill) {
|
|
84
|
-
log.verbose('create Blob cache file from Blob object');
|
|
85
|
-
let size = 0;
|
|
86
|
-
this._ref = String(data.getReactNativeBlobUtilRef());
|
|
87
|
-
let orgPath = this._ref;
|
|
88
|
-
|
|
89
|
-
p = fs.exists(orgPath)
|
|
90
|
-
.then((exist) => {
|
|
91
|
-
if (exist)
|
|
92
|
-
return fs.writeFile(orgPath, data, 'uri')
|
|
93
|
-
.then((size) => Promise.resolve(size))
|
|
94
|
-
.catch((err) => {
|
|
95
|
-
throw `ReactNativeBlobUtil Blob file creation error, ${err}`;
|
|
96
|
-
});
|
|
97
|
-
else
|
|
98
|
-
throw `could not create Blob from path ${orgPath}, file not exists`;
|
|
99
|
-
});
|
|
100
|
-
}
|
|
101
|
-
// process FormData
|
|
102
|
-
else if (data instanceof FormData) {
|
|
103
|
-
log.verbose('create Blob cache file from FormData', data);
|
|
104
|
-
let boundary = `ReactNativeBlobUtil-${this.cacheName}-${Date.now()}`;
|
|
105
|
-
this.multipartBoundary = boundary;
|
|
106
|
-
let parts = data.getParts();
|
|
107
|
-
let formArray = [];
|
|
108
|
-
if (!parts) {
|
|
109
|
-
p = fs.writeFile(this._ref, '', 'utf8');
|
|
110
|
-
}
|
|
111
|
-
else {
|
|
112
|
-
for (let i in parts) {
|
|
113
|
-
formArray.push('\r\n--' + boundary + '\r\n');
|
|
114
|
-
let part = parts[i];
|
|
115
|
-
for (let j in part.headers) {
|
|
116
|
-
formArray.push(j + ': ' + part.headers[j] + '\r\n');
|
|
117
|
-
}
|
|
118
|
-
formArray.push('\r\n');
|
|
119
|
-
if (part.isReactNativeBlobUtilPolyfill)
|
|
120
|
-
formArray.push(part);
|
|
121
|
-
else
|
|
122
|
-
formArray.push(part.string);
|
|
123
|
-
}
|
|
124
|
-
log.verbose('FormData array', formArray);
|
|
125
|
-
formArray.push('\r\n--' + boundary + '--\r\n');
|
|
126
|
-
p = createMixedBlobData(this._ref, formArray);
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
// if the data is a string starts with `ReactNativeBlobUtil-file://`, append the
|
|
130
|
-
// Blob data from file path
|
|
131
|
-
else if (typeof data === 'string' && data.startsWith('ReactNativeBlobUtil-file://')) {
|
|
132
|
-
log.verbose('create Blob cache file from file path', data);
|
|
133
|
-
// set this flag so that we know this blob is a wrapper of an existing file
|
|
134
|
-
this._isReference = true;
|
|
135
|
-
this._ref = String(data).replace('ReactNativeBlobUtil-file://', '');
|
|
136
|
-
let orgPath = this._ref;
|
|
137
|
-
if (defer)
|
|
138
|
-
return;
|
|
139
|
-
else {
|
|
140
|
-
p = fs.stat(orgPath)
|
|
141
|
-
.then((stat) => {
|
|
142
|
-
return Promise.resolve(stat.size);
|
|
143
|
-
});
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
// content from variable need create file
|
|
147
|
-
else if (typeof data === 'string') {
|
|
148
|
-
let encoding = 'utf8';
|
|
149
|
-
let mime = String(this.type);
|
|
150
|
-
// when content type contains application/octet* or *;base64, ReactNativeBlobUtil
|
|
151
|
-
// fs will treat it as BASE64 encoded string binary data
|
|
152
|
-
if (/(application\/octet|\;base64)/i.test(mime))
|
|
153
|
-
encoding = 'base64';
|
|
154
|
-
else
|
|
155
|
-
data = data.toString();
|
|
156
|
-
// create cache file
|
|
157
|
-
this.type = String(this.type).replace(/;base64/ig, '');
|
|
158
|
-
log.verbose('create Blob cache file from string', 'encode', encoding);
|
|
159
|
-
p = fs.writeFile(this._ref, data, encoding)
|
|
160
|
-
.then((size) => {
|
|
161
|
-
return Promise.resolve(size);
|
|
162
|
-
});
|
|
163
|
-
|
|
164
|
-
}
|
|
165
|
-
// TODO : ArrayBuffer support
|
|
166
|
-
// else if (data instanceof ArrayBuffer ) {
|
|
167
|
-
//
|
|
168
|
-
// }
|
|
169
|
-
// when input is an array of mixed data types, create a file cache
|
|
170
|
-
else if (Array.isArray(data)) {
|
|
171
|
-
log.verbose('create Blob cache file from mixed array', data);
|
|
172
|
-
p = createMixedBlobData(this._ref, data);
|
|
173
|
-
}
|
|
174
|
-
else {
|
|
175
|
-
data = data.toString();
|
|
176
|
-
p = fs.writeFile(this._ref, data, 'utf8')
|
|
177
|
-
.then((size) => Promise.resolve(size));
|
|
178
|
-
}
|
|
179
|
-
p && p.then((size) => {
|
|
180
|
-
this.size = size;
|
|
181
|
-
this._invokeOnCreateEvent();
|
|
182
|
-
})
|
|
183
|
-
.catch((err) => {
|
|
184
|
-
log.error('ReactNativeBlobUtil could not create Blob : ' + this._ref, err);
|
|
185
|
-
});
|
|
186
|
-
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
/**
|
|
190
|
-
* Since Blob content will asynchronously write to a file during creation,
|
|
191
|
-
* use this method to register an event handler for Blob initialized event.
|
|
192
|
-
* @nonstandard
|
|
193
|
-
* @param {(b:Blob) => void} An event handler invoked when Blob created
|
|
194
|
-
* @return {Blob} The Blob object instance itself
|
|
195
|
-
*/
|
|
196
|
-
onCreated(fn: () => void): Blob {
|
|
197
|
-
log.verbose('#register blob onCreated', this._blobCreated);
|
|
198
|
-
if (!this._blobCreated)
|
|
199
|
-
this._onCreated.push(fn);
|
|
200
|
-
else {
|
|
201
|
-
fn(this);
|
|
202
|
-
}
|
|
203
|
-
return this;
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
markAsDerived() {
|
|
207
|
-
this._isDerived = true;
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
get isDerived() {
|
|
211
|
-
return this._isDerived || false;
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
/**
|
|
215
|
-
* Get file reference of the Blob object.
|
|
216
|
-
* @nonstandard
|
|
217
|
-
* @return {string} Blob file reference which can be consumed by ReactNativeBlobUtil fs
|
|
218
|
-
*/
|
|
219
|
-
getReactNativeBlobUtilRef() {
|
|
220
|
-
return this._ref;
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
/**
|
|
224
|
-
* Create a Blob object which is sliced from current object
|
|
225
|
-
* @param {number} start Start byte number
|
|
226
|
-
* @param {number} end End byte number
|
|
227
|
-
* @param {string} contentType Optional, content type of new Blob object
|
|
228
|
-
* @return {Blob}
|
|
229
|
-
*/
|
|
230
|
-
slice(start: ?number, end: ?number, contentType: ?string = ''): Blob {
|
|
231
|
-
if (this._closed)
|
|
232
|
-
throw 'Blob has been released.';
|
|
233
|
-
log.verbose('slice called', start, end, contentType);
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
let resPath = blobCacheDir + getBlobName();
|
|
237
|
-
let pass = false;
|
|
238
|
-
log.debug('fs.slice new blob will at', resPath);
|
|
239
|
-
let result = new Blob(URIUtil.wrap(resPath), {type: contentType}, true);
|
|
240
|
-
fs.exists(blobCacheDir)
|
|
241
|
-
.then((exist) => {
|
|
242
|
-
if (exist)
|
|
243
|
-
return Promise.resolve();
|
|
244
|
-
return fs.mkdir(blobCacheDir);
|
|
245
|
-
})
|
|
246
|
-
.then(() => fs.slice(this._ref, resPath, start, end))
|
|
247
|
-
.then((dest) => {
|
|
248
|
-
log.debug('fs.slice done', dest);
|
|
249
|
-
result._invokeOnCreateEvent();
|
|
250
|
-
pass = true;
|
|
251
|
-
})
|
|
252
|
-
.catch((err) => {
|
|
253
|
-
console.warn('Blob.slice failed:', err);
|
|
254
|
-
pass = true;
|
|
255
|
-
});
|
|
256
|
-
log.debug('slice returning new Blob');
|
|
257
|
-
|
|
258
|
-
return result;
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
/**
|
|
262
|
-
* Read data of the Blob object, this is not standard method.
|
|
263
|
-
* @nonstandard
|
|
264
|
-
* @param {string} encoding Read data with encoding
|
|
265
|
-
* @return {Promise}
|
|
266
|
-
*/
|
|
267
|
-
readBlob(encoding: string): Promise<any> {
|
|
268
|
-
if (this._closed)
|
|
269
|
-
throw 'Blob has been released.';
|
|
270
|
-
return fs.readFile(this._ref, encoding || 'utf8');
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
/**
|
|
274
|
-
* Release the resource of the Blob object.
|
|
275
|
-
* @nonstandard
|
|
276
|
-
* @return {Promise}
|
|
277
|
-
*/
|
|
278
|
-
close() {
|
|
279
|
-
if (this._closed)
|
|
280
|
-
return Promise.reject('Blob has been released.');
|
|
281
|
-
this._closed = true;
|
|
282
|
-
return fs.unlink(this._ref).catch((err) => {
|
|
283
|
-
console.warn(err);
|
|
284
|
-
});
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
safeClose() {
|
|
288
|
-
if (this._closed)
|
|
289
|
-
return Promise.reject('Blob has been released.');
|
|
290
|
-
this._closed = true;
|
|
291
|
-
if (!this._isReference) {
|
|
292
|
-
return fs.unlink(this._ref).catch((err) => {
|
|
293
|
-
console.warn(err);
|
|
294
|
-
});
|
|
295
|
-
}
|
|
296
|
-
else {
|
|
297
|
-
return Promise.resolve();
|
|
298
|
-
}
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
_invokeOnCreateEvent() {
|
|
302
|
-
log.verbose('invoke create event', this._onCreated);
|
|
303
|
-
this._blobCreated = true;
|
|
304
|
-
let fns = this._onCreated;
|
|
305
|
-
for (let i in fns) {
|
|
306
|
-
if (typeof fns[i] === 'function') {
|
|
307
|
-
fns[i](this);
|
|
308
|
-
}
|
|
309
|
-
}
|
|
310
|
-
delete this._onCreated;
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
/**
|
|
316
|
-
* Get a temp filename for Blob object
|
|
317
|
-
* @return {string} Temporary filename
|
|
318
|
-
*/
|
|
319
|
-
function getBlobName() {
|
|
320
|
-
return 'blob-' + getUUID();
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
/**
|
|
324
|
-
* Create a file according to given array. The element in array can be a number,
|
|
325
|
-
* Blob, String, Array.
|
|
326
|
-
* @param {string} ref File path reference
|
|
327
|
-
* @param {Array} dataArray An array contains different types of data.
|
|
328
|
-
* @return {Promise}
|
|
329
|
-
*/
|
|
330
|
-
function createMixedBlobData(ref, dataArray) {
|
|
331
|
-
// create an empty file for store blob data
|
|
332
|
-
let p = fs.writeFile(ref, '');
|
|
333
|
-
let args = [];
|
|
334
|
-
let size = 0;
|
|
335
|
-
for (let i in dataArray) {
|
|
336
|
-
let part = dataArray[i];
|
|
337
|
-
if (!part)
|
|
338
|
-
continue;
|
|
339
|
-
if (part.isReactNativeBlobUtilPolyfill) {
|
|
340
|
-
args.push([ref, part._ref, 'uri']);
|
|
341
|
-
}
|
|
342
|
-
else if (typeof part === 'string')
|
|
343
|
-
args.push([ref, part, 'utf8']);
|
|
344
|
-
// TODO : ArrayBuffer
|
|
345
|
-
// else if (part instanceof ArrayBuffer) {
|
|
346
|
-
//
|
|
347
|
-
// }
|
|
348
|
-
else if (Array.isArray(part))
|
|
349
|
-
args.push([ref, part, 'ascii']);
|
|
350
|
-
}
|
|
351
|
-
// start write blob data
|
|
352
|
-
for (let i in args) {
|
|
353
|
-
p = p.then(function (written) {
|
|
354
|
-
let arg = this;
|
|
355
|
-
if (written)
|
|
356
|
-
size += written;
|
|
357
|
-
log.verbose('mixed blob write', args[i], written);
|
|
358
|
-
return fs.appendFile(...arg);
|
|
359
|
-
}.bind(args[i]));
|
|
360
|
-
}
|
|
361
|
-
return p.then(() => Promise.resolve(size));
|
|
362
|
-
}
|
|
1
|
+
// Copyright 2016 wkh237@github. All rights reserved.
|
|
2
|
+
// Use of this source code is governed by a MIT-style license that can be
|
|
3
|
+
// found in the LICENSE file.
|
|
4
|
+
|
|
5
|
+
import fs from '../fs.js';
|
|
6
|
+
import getUUID from '../utils/uuid';
|
|
7
|
+
import Log from '../utils/log.js';
|
|
8
|
+
import URIUtil from "../utils/uri";
|
|
9
|
+
import EventTarget from './EventTarget';
|
|
10
|
+
|
|
11
|
+
const log = new Log('Blob');
|
|
12
|
+
const blobCacheDir = fs.dirs.DocumentDir + '/ReactNativeBlobUtil-blobs/';
|
|
13
|
+
|
|
14
|
+
log.disable();
|
|
15
|
+
// log.level(3)
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* A ReactNativeBlobUtil style Blob polyfill class, this is a Blob which compatible to
|
|
19
|
+
* Response object attain fron ReactNativeBlobUtil.fetch.
|
|
20
|
+
*/
|
|
21
|
+
export default class Blob extends EventTarget {
|
|
22
|
+
|
|
23
|
+
cacheName: string;
|
|
24
|
+
type: string;
|
|
25
|
+
size: number;
|
|
26
|
+
isReactNativeBlobUtilPolyfill: boolean = true;
|
|
27
|
+
multipartBoundary: string = null;
|
|
28
|
+
|
|
29
|
+
_ref: string = null;
|
|
30
|
+
_blobCreated: boolean = false;
|
|
31
|
+
_onCreated: Array<any> = [];
|
|
32
|
+
_closed: boolean = false;
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Static method that remove all files in Blob cache folder.
|
|
36
|
+
* @nonstandard
|
|
37
|
+
* @return {Promise}
|
|
38
|
+
*/
|
|
39
|
+
static clearCache() {
|
|
40
|
+
return fs.unlink(blobCacheDir).then(() => fs.mkdir(blobCacheDir));
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
static build(data: any, cType: any): Promise<Blob> {
|
|
44
|
+
return new Promise((resolve, reject) => {
|
|
45
|
+
new Blob(data, cType).onCreated(resolve);
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
get blobPath() {
|
|
50
|
+
return this._ref;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
static setLog(level: number) {
|
|
54
|
+
if (level === -1)
|
|
55
|
+
log.disable();
|
|
56
|
+
else
|
|
57
|
+
log.level(level);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* ReactNativeBlobUtil Blob polyfill, create a Blob directly from file path, BASE64
|
|
62
|
+
* encoded data, and string. The conversion is done implicitly according to
|
|
63
|
+
* given `mime`. However, the blob creation is asynchronously, to register
|
|
64
|
+
* event `onCreated` is need to ensure the Blob is creadted.
|
|
65
|
+
* @param {any} data Content of Blob object
|
|
66
|
+
* @param {any} mime Content type settings of Blob object, `text/plain`
|
|
67
|
+
* by default
|
|
68
|
+
* @param {boolean} defer When this argument set to `true`, blob constructor
|
|
69
|
+
* will not invoke blob created event automatically.
|
|
70
|
+
*/
|
|
71
|
+
constructor(data: any, cType: any, defer: boolean) {
|
|
72
|
+
super();
|
|
73
|
+
cType = cType || {};
|
|
74
|
+
this.cacheName = getBlobName();
|
|
75
|
+
this.isReactNativeBlobUtilPolyfill = true;
|
|
76
|
+
this.isDerived = defer;
|
|
77
|
+
this.type = cType.type || 'text/plain';
|
|
78
|
+
log.verbose('Blob constructor called', 'mime', this.type, 'type', typeof data, 'length', data ? data.length : 0);
|
|
79
|
+
this._ref = blobCacheDir + this.cacheName;
|
|
80
|
+
let p = null;
|
|
81
|
+
if (!data)
|
|
82
|
+
data = '';
|
|
83
|
+
if (data.isReactNativeBlobUtilPolyfill) {
|
|
84
|
+
log.verbose('create Blob cache file from Blob object');
|
|
85
|
+
let size = 0;
|
|
86
|
+
this._ref = String(data.getReactNativeBlobUtilRef());
|
|
87
|
+
let orgPath = this._ref;
|
|
88
|
+
|
|
89
|
+
p = fs.exists(orgPath)
|
|
90
|
+
.then((exist) => {
|
|
91
|
+
if (exist)
|
|
92
|
+
return fs.writeFile(orgPath, data, 'uri')
|
|
93
|
+
.then((size) => Promise.resolve(size))
|
|
94
|
+
.catch((err) => {
|
|
95
|
+
throw `ReactNativeBlobUtil Blob file creation error, ${err}`;
|
|
96
|
+
});
|
|
97
|
+
else
|
|
98
|
+
throw `could not create Blob from path ${orgPath}, file not exists`;
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
// process FormData
|
|
102
|
+
else if (data instanceof FormData) {
|
|
103
|
+
log.verbose('create Blob cache file from FormData', data);
|
|
104
|
+
let boundary = `ReactNativeBlobUtil-${this.cacheName}-${Date.now()}`;
|
|
105
|
+
this.multipartBoundary = boundary;
|
|
106
|
+
let parts = data.getParts();
|
|
107
|
+
let formArray = [];
|
|
108
|
+
if (!parts) {
|
|
109
|
+
p = fs.writeFile(this._ref, '', 'utf8');
|
|
110
|
+
}
|
|
111
|
+
else {
|
|
112
|
+
for (let i in parts) {
|
|
113
|
+
formArray.push('\r\n--' + boundary + '\r\n');
|
|
114
|
+
let part = parts[i];
|
|
115
|
+
for (let j in part.headers) {
|
|
116
|
+
formArray.push(j + ': ' + part.headers[j] + '\r\n');
|
|
117
|
+
}
|
|
118
|
+
formArray.push('\r\n');
|
|
119
|
+
if (part.isReactNativeBlobUtilPolyfill)
|
|
120
|
+
formArray.push(part);
|
|
121
|
+
else
|
|
122
|
+
formArray.push(part.string);
|
|
123
|
+
}
|
|
124
|
+
log.verbose('FormData array', formArray);
|
|
125
|
+
formArray.push('\r\n--' + boundary + '--\r\n');
|
|
126
|
+
p = createMixedBlobData(this._ref, formArray);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
// if the data is a string starts with `ReactNativeBlobUtil-file://`, append the
|
|
130
|
+
// Blob data from file path
|
|
131
|
+
else if (typeof data === 'string' && data.startsWith('ReactNativeBlobUtil-file://')) {
|
|
132
|
+
log.verbose('create Blob cache file from file path', data);
|
|
133
|
+
// set this flag so that we know this blob is a wrapper of an existing file
|
|
134
|
+
this._isReference = true;
|
|
135
|
+
this._ref = String(data).replace('ReactNativeBlobUtil-file://', '');
|
|
136
|
+
let orgPath = this._ref;
|
|
137
|
+
if (defer)
|
|
138
|
+
return;
|
|
139
|
+
else {
|
|
140
|
+
p = fs.stat(orgPath)
|
|
141
|
+
.then((stat) => {
|
|
142
|
+
return Promise.resolve(stat.size);
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
// content from variable need create file
|
|
147
|
+
else if (typeof data === 'string') {
|
|
148
|
+
let encoding = 'utf8';
|
|
149
|
+
let mime = String(this.type);
|
|
150
|
+
// when content type contains application/octet* or *;base64, ReactNativeBlobUtil
|
|
151
|
+
// fs will treat it as BASE64 encoded string binary data
|
|
152
|
+
if (/(application\/octet|\;base64)/i.test(mime))
|
|
153
|
+
encoding = 'base64';
|
|
154
|
+
else
|
|
155
|
+
data = data.toString();
|
|
156
|
+
// create cache file
|
|
157
|
+
this.type = String(this.type).replace(/;base64/ig, '');
|
|
158
|
+
log.verbose('create Blob cache file from string', 'encode', encoding);
|
|
159
|
+
p = fs.writeFile(this._ref, data, encoding)
|
|
160
|
+
.then((size) => {
|
|
161
|
+
return Promise.resolve(size);
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
}
|
|
165
|
+
// TODO : ArrayBuffer support
|
|
166
|
+
// else if (data instanceof ArrayBuffer ) {
|
|
167
|
+
//
|
|
168
|
+
// }
|
|
169
|
+
// when input is an array of mixed data types, create a file cache
|
|
170
|
+
else if (Array.isArray(data)) {
|
|
171
|
+
log.verbose('create Blob cache file from mixed array', data);
|
|
172
|
+
p = createMixedBlobData(this._ref, data);
|
|
173
|
+
}
|
|
174
|
+
else {
|
|
175
|
+
data = data.toString();
|
|
176
|
+
p = fs.writeFile(this._ref, data, 'utf8')
|
|
177
|
+
.then((size) => Promise.resolve(size));
|
|
178
|
+
}
|
|
179
|
+
p && p.then((size) => {
|
|
180
|
+
this.size = size;
|
|
181
|
+
this._invokeOnCreateEvent();
|
|
182
|
+
})
|
|
183
|
+
.catch((err) => {
|
|
184
|
+
log.error('ReactNativeBlobUtil could not create Blob : ' + this._ref, err);
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Since Blob content will asynchronously write to a file during creation,
|
|
191
|
+
* use this method to register an event handler for Blob initialized event.
|
|
192
|
+
* @nonstandard
|
|
193
|
+
* @param {(b:Blob) => void} An event handler invoked when Blob created
|
|
194
|
+
* @return {Blob} The Blob object instance itself
|
|
195
|
+
*/
|
|
196
|
+
onCreated(fn: () => void): Blob {
|
|
197
|
+
log.verbose('#register blob onCreated', this._blobCreated);
|
|
198
|
+
if (!this._blobCreated)
|
|
199
|
+
this._onCreated.push(fn);
|
|
200
|
+
else {
|
|
201
|
+
fn(this);
|
|
202
|
+
}
|
|
203
|
+
return this;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
markAsDerived() {
|
|
207
|
+
this._isDerived = true;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
get isDerived() {
|
|
211
|
+
return this._isDerived || false;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* Get file reference of the Blob object.
|
|
216
|
+
* @nonstandard
|
|
217
|
+
* @return {string} Blob file reference which can be consumed by ReactNativeBlobUtil fs
|
|
218
|
+
*/
|
|
219
|
+
getReactNativeBlobUtilRef() {
|
|
220
|
+
return this._ref;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* Create a Blob object which is sliced from current object
|
|
225
|
+
* @param {number} start Start byte number
|
|
226
|
+
* @param {number} end End byte number
|
|
227
|
+
* @param {string} contentType Optional, content type of new Blob object
|
|
228
|
+
* @return {Blob}
|
|
229
|
+
*/
|
|
230
|
+
slice(start: ?number, end: ?number, contentType: ?string = ''): Blob {
|
|
231
|
+
if (this._closed)
|
|
232
|
+
throw 'Blob has been released.';
|
|
233
|
+
log.verbose('slice called', start, end, contentType);
|
|
234
|
+
|
|
235
|
+
|
|
236
|
+
let resPath = blobCacheDir + getBlobName();
|
|
237
|
+
let pass = false;
|
|
238
|
+
log.debug('fs.slice new blob will at', resPath);
|
|
239
|
+
let result = new Blob(URIUtil.wrap(resPath), {type: contentType}, true);
|
|
240
|
+
fs.exists(blobCacheDir)
|
|
241
|
+
.then((exist) => {
|
|
242
|
+
if (exist)
|
|
243
|
+
return Promise.resolve();
|
|
244
|
+
return fs.mkdir(blobCacheDir);
|
|
245
|
+
})
|
|
246
|
+
.then(() => fs.slice(this._ref, resPath, start, end))
|
|
247
|
+
.then((dest) => {
|
|
248
|
+
log.debug('fs.slice done', dest);
|
|
249
|
+
result._invokeOnCreateEvent();
|
|
250
|
+
pass = true;
|
|
251
|
+
})
|
|
252
|
+
.catch((err) => {
|
|
253
|
+
console.warn('Blob.slice failed:', err);
|
|
254
|
+
pass = true;
|
|
255
|
+
});
|
|
256
|
+
log.debug('slice returning new Blob');
|
|
257
|
+
|
|
258
|
+
return result;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* Read data of the Blob object, this is not standard method.
|
|
263
|
+
* @nonstandard
|
|
264
|
+
* @param {string} encoding Read data with encoding
|
|
265
|
+
* @return {Promise}
|
|
266
|
+
*/
|
|
267
|
+
readBlob(encoding: string): Promise<any> {
|
|
268
|
+
if (this._closed)
|
|
269
|
+
throw 'Blob has been released.';
|
|
270
|
+
return fs.readFile(this._ref, encoding || 'utf8');
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
/**
|
|
274
|
+
* Release the resource of the Blob object.
|
|
275
|
+
* @nonstandard
|
|
276
|
+
* @return {Promise}
|
|
277
|
+
*/
|
|
278
|
+
close() {
|
|
279
|
+
if (this._closed)
|
|
280
|
+
return Promise.reject('Blob has been released.');
|
|
281
|
+
this._closed = true;
|
|
282
|
+
return fs.unlink(this._ref).catch((err) => {
|
|
283
|
+
console.warn(err);
|
|
284
|
+
});
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
safeClose() {
|
|
288
|
+
if (this._closed)
|
|
289
|
+
return Promise.reject('Blob has been released.');
|
|
290
|
+
this._closed = true;
|
|
291
|
+
if (!this._isReference) {
|
|
292
|
+
return fs.unlink(this._ref).catch((err) => {
|
|
293
|
+
console.warn(err);
|
|
294
|
+
});
|
|
295
|
+
}
|
|
296
|
+
else {
|
|
297
|
+
return Promise.resolve();
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
_invokeOnCreateEvent() {
|
|
302
|
+
log.verbose('invoke create event', this._onCreated);
|
|
303
|
+
this._blobCreated = true;
|
|
304
|
+
let fns = this._onCreated;
|
|
305
|
+
for (let i in fns) {
|
|
306
|
+
if (typeof fns[i] === 'function') {
|
|
307
|
+
fns[i](this);
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
delete this._onCreated;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
/**
|
|
316
|
+
* Get a temp filename for Blob object
|
|
317
|
+
* @return {string} Temporary filename
|
|
318
|
+
*/
|
|
319
|
+
function getBlobName() {
|
|
320
|
+
return 'blob-' + getUUID();
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
/**
|
|
324
|
+
* Create a file according to given array. The element in array can be a number,
|
|
325
|
+
* Blob, String, Array.
|
|
326
|
+
* @param {string} ref File path reference
|
|
327
|
+
* @param {Array} dataArray An array contains different types of data.
|
|
328
|
+
* @return {Promise}
|
|
329
|
+
*/
|
|
330
|
+
function createMixedBlobData(ref, dataArray) {
|
|
331
|
+
// create an empty file for store blob data
|
|
332
|
+
let p = fs.writeFile(ref, '');
|
|
333
|
+
let args = [];
|
|
334
|
+
let size = 0;
|
|
335
|
+
for (let i in dataArray) {
|
|
336
|
+
let part = dataArray[i];
|
|
337
|
+
if (!part)
|
|
338
|
+
continue;
|
|
339
|
+
if (part.isReactNativeBlobUtilPolyfill) {
|
|
340
|
+
args.push([ref, part._ref, 'uri']);
|
|
341
|
+
}
|
|
342
|
+
else if (typeof part === 'string')
|
|
343
|
+
args.push([ref, part, 'utf8']);
|
|
344
|
+
// TODO : ArrayBuffer
|
|
345
|
+
// else if (part instanceof ArrayBuffer) {
|
|
346
|
+
//
|
|
347
|
+
// }
|
|
348
|
+
else if (Array.isArray(part))
|
|
349
|
+
args.push([ref, part, 'ascii']);
|
|
350
|
+
}
|
|
351
|
+
// start write blob data
|
|
352
|
+
for (let i in args) {
|
|
353
|
+
p = p.then(function (written) {
|
|
354
|
+
let arg = this;
|
|
355
|
+
if (written)
|
|
356
|
+
size += written;
|
|
357
|
+
log.verbose('mixed blob write', args[i], written);
|
|
358
|
+
return fs.appendFile(...arg);
|
|
359
|
+
}.bind(args[i]));
|
|
360
|
+
}
|
|
361
|
+
return p.then(() => Promise.resolve(size));
|
|
362
|
+
}
|