react-native-blob-util
Version:
A module provides upload, download, and files access API. Supports file stream read/write for process large files.
220 lines (193 loc) • 7.67 kB
JavaScript
import Log from '../utils/log.js';
import Blob from './Blob';
import {config as RNconfig} from "../fetch";
import type {ReactNativeBlobUtilConfig} from "../types";
import URIUtil from "../utils/uri";
import {FetchBlobResponse} from "../class/ReactNativeBlobUtilBlobResponse";
const log = new Log('FetchPolyfill');
log.disable();
// log.level(3)
export default class Fetch {
constructor(config: ReactNativeBlobUtilConfig) {
Object.assign(this, new ReactNativeBlobUtilFetchPolyfill(config));
}
}
class ReactNativeBlobUtilFetchPolyfill {
constructor(config: ReactNativeBlobUtilConfig) {
this.build = () => (url, options = {}) => {
let body = options.body;
let promise = Promise.resolve();
let blobCache = null;
options.headers = options.headers || {};
let ctype = options['Content-Type'] || options['content-type'];
let ctypeH = options.headers['Content-Type'] || options.headers['content-type'];
options.headers['Content-Type'] = ctype || ctypeH;
options.headers['content-type'] = ctype || ctypeH;
options.method = options.method || 'GET';
if (body) {
// When the request body is an instance of FormData, create a Blob cache
// to upload the body.
if (body instanceof FormData) {
log.verbose('convert FormData to blob body');
promise = Blob.build(body).then((b) => {
blobCache = b;
const contentType = 'multipart/form-data;boundary=' + b.multipartBoundary
options.headers['Content-Type'] = contentType;
options.headers['content-type'] = contentType;
return Promise.resolve(URIUtil.wrap(b._ref));
});
}
// When request body is a Blob, use file URI of the Blob as request body.
else if (body.isReactNativeBlobUtilPolyfill)
promise = Promise.resolve(URIUtil.wrap(body.blobPath));
else if (typeof body !== 'object' && options.headers['Content-Type'] !== 'application/json')
promise = Promise.resolve(JSON.stringify(body));
else if (typeof body !== 'string')
promise = Promise.resolve(body.toString());
// send it as-is, leave the native module decide how to send the body.
else
promise = Promise.resolve(body);
}
// task is a progress reportable and cancellable Promise, however,
// task.then is not, so we have to extend task.then with progress and
// cancel function
let progressHandler, uploadHandler, cancelHandler;
let scopedTask = null;
let statefulPromise = promise
.then((body) => {
let task = RNconfig(config)
.fetch(options.method, url, options.headers, body);
scopedTask = task;
if (progressHandler)
task.progress(progressHandler);
if (uploadHandler)
task.uploadProgress(uploadHandler);
if (cancelHandler)
task.cancel();
return task.then((resp) => {
log.verbose('response', resp);
// release blob cache created when sending request
if (blobCache !== null && blobCache instanceof Blob)
blobCache.close();
return Promise.resolve(new ReactNativeBlobUtilFetchResponse(resp));
});
});
// extend task.then progress with report and cancelling functions
statefulPromise.progress = (fn) => {
progressHandler = fn;
};
statefulPromise.uploadProgress = (fn) => {
uploadHandler = fn;
};
statefulPromise.cancel = () => {
cancelHandler = true;
if (scopedTask && scopedTask.cancel)
scopedTask.cancel();
};
return statefulPromise;
};
}
}
class ReactNativeBlobUtilFetchResponse {
constructor(resp: FetchBlobResponse) {
let info = resp.info();
this.headers = info.headers;
this.ok = info.status >= 200 && info.status <= 299,
this.status = info.status;
this.type = 'basic';
this.bodyUsed = false;
this.resp = resp;
this.rnfbRespInfo = info;
this.rnfbResp = resp;
}
rawResp() {
return Promise.resolve(this.rnfbResp);
}
arrayBuffer() {
log.verbose('to arrayBuffer', this.rnfbRespInfo);
this.bodyUsed = true;
return readArrayBuffer(this.rnfbResp, this.rnfbRespInfo);
}
text() {
log.verbose('to text', this.rnfbResp, this.rnfbRespInfo);
this.bodyUsed = true;
return readText(this.rnfbResp, this.rnfbRespInfo);
}
json() {
log.verbose('to json', this.rnfbResp, this.rnfbRespInfo);
this.bodyUsed = true;
return readJSON(this.rnfbResp, this.rnfbRespInfo);
}
blob() {
log.verbose('to blob', this.rnfbResp, this.rnfbRespInfo);
this.bodyUsed = true;
return readBlob(this.rnfbResp, this.rnfbRespInfo);
}
}
/**
* Get response data as array.
* @param {FetchBlobResponse} resp Response data object from RNFB fetch call.
* @param {ReactNativeBlobUtilResponseInfo} info Response informations.
* @return {Promise<Array>}
*/
function readArrayBuffer(resp, info): Promise<Array> {
switch (info.rnfbEncode) {
case 'path':
return resp.readFile('ascii');
break;
default:
let buffer = [];
let str = resp.text();
for (let i in str) {
buffer[i] = str.charCodeAt(i);
}
return Promise.resolve(buffer);
break;
}
}
/**
* Get response data as string.
* @param {FetchBlobResponse} resp Response data object from RNFB fetch call.
* @param {ReactNativeBlobUtilResponseInfo} info Response informations.
* @return {Promise<string>}
*/
function readText(resp, info): Promise<string> {
switch (info.rnfbEncode) {
case 'base64':
return Promise.resolve(resp.text());
break;
case 'path':
return resp.text();
break;
default:
return Promise.resolve(resp.text());
break;
}
}
/**
* Get response data as ReactNativeBlobUtil Blob polyfill object.
* @param {FetchBlobResponse} resp Response data object from RNFB fetch call.
* @param {ReactNativeBlobUtilResponseInfo} info Response informations.
* @return {Promise<Blob>}
*/
function readBlob(resp, info): Promise<Blob> {
log.verbose('readBlob', resp, info);
return resp.blob();
}
/**
* Get response data as JSON object.
* @param {FetchBlobResponse} resp Response data object from RNFB fetch call.
* @param {ReactNativeBlobUtilResponseInfo} info Response informations.
* @return {Promise<object>}
*/
function readJSON(resp, info): Promise<object> {
log.verbose('readJSON', resp, info);
switch (info.rnfbEncode) {
case 'base64':
return Promise.resolve(resp.json());
case 'path':
return resp.json();
default:
return Promise.resolve(resp.json());
}
}