Efficiently download and upload blobs in React Native
source link: https://reactnativeexample.com/efficiently-download-and-upload-blobs-in-react-native/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
Efficiently download and upload blobs in React Native
react-native-blob-courier
Use this library to efficiently download and upload blobs in React Native. The library was inspired by rn-fetch-blob, and aims to focus strictly on blob transfers.
Installation
Install using yarn
yarn add react-native-blob-courier
Or install using npm
npm install react-native-blob-courier
Link the library:
NB. Linking can be skipped when the project uses React Native 0.60 or greater, because autolinking will take care of it
react-native link react-native-blob-courier
If CocoaPods is used in the project, make sure to install the pod:
cd ios && pod install
Requirements
- Android >= 19
- iOS >= 9 Changing to iOS 10.x in July 2021
- Kotlin >= 1.4.x
Usage
The library provides both a fluent and a more concise interface. In the examples the concise approach is applied; fluent interface is demonstrated later in this document.
Straightforward down- and upload
import BlobCourier from 'react-native-blob-courier';
// ...
// Download a file
const request0 = {
filename: '5MB.zip',
method: 'GET',
mimeType: 'application/zip',
url: 'http://ipv4.download.thinkbroadband.com/5MB.zip',
};
const fetchedResult = await BlobCourier.fetchBlob(request0);
console.log(fetchedResult);
// {
// "data": {
// "absoluteFilePath": "/path/to/app/cache/5MB.zip",
// "response": {
// "code":200,
// "headers": {
// "some_header": "some_value",
// ...
// }
// },
// },
// "type":"Unmanaged"
// }
// ...
// Upload a file
const absoluteFilePath = fetchedResult.data.absoluteFilePath;
const request1 = {
absoluteFilePath,
method: 'POST',
mimeType: 'application/zip',
url: 'https://file.io',
};
const uploadResult = await BlobCourier.uploadBlob(request1);
console.log(uploadResult):
// {
// "response": {
// "code": {
// "data": "<some response>",
// "headers": {
// "some_header": "some_value",
// ...
// }
// }
// }
// Multipart file upload
const absoluteFilePath = fetchedResult.data.absoluteFilePath;
const request2 = {
method: 'POST',
parts: {
body: {
payload: 'some_value',
type: 'string',
},
file: {
payload: {
absoluteFilePath,
mimeType: 'application/zip',
},
type: 'file',
},
},
url: 'https://file.io',
};
const multipartUploadResult = await BlobCourier.uploadBlob(request1);
console.log(multipartUploadResult):
// {
// "response": {
// "code": {
// "data": "<some response>",
// "headers": {
// "some_header": "some_value",
// ...
// }
// }
// }
Transfer progress reporting
import BlobCourier from 'react-native-blob-courier';
// ...
// Download a file
const request0 = {
// ...
onProgress: ((e: BlobProgressEvent) => {
console.log(e)
// {
// "written": <some_number_of_bytes_written>,
// "total": <some_total_number_of_bytes>
// }
})
};
const fetchedResult = await BlobCourier.fetchBlob(request0);
// ...
// Upload a file
const request1 = {
// ...
onProgress: ((e: BlobProgressEvent) => {
console.log(e)
// {
// "written": <some_number_of_bytes_written>,
// "total": <some_total_number_of_bytes>
// }
})
};
const uploadResult = await BlobCourier.uploadBlob(request1)
// ...
// Set progress updater interval
const request2 = ...
const someResult =
await BlobCourier
.fetchBlob({
...request2,
progressIntervalMilliseconds: 1000,
});
Managed download on Android (not available on iOS)
import BlobCourier from 'react-native-blob-courier';
// ...
const request = {
android: {
useDownloadManager: true // <--- set useDownloadManager to "true"
},
filename: '5MB.zip',
method: 'GET',
mimeType: 'application/zip',
url: 'http://ipv4.download.thinkbroadband.com/5MB.zip',
};
const fetchResult = await BlobCourier.fetchBlob(request);
console.log(fetchedResult);
// {
// "data": {
// "result": "SUCCESS",
// "absoluteFilePath": "/path/to/app/cache/5MB.zip"
// },
// "type":"Managed"
// }
Multipart upload
Sometimes order of multipart fields matters, and Blob Courier respects the order in which parts are provided. There is a catch though: when object keys are regular strings they are kept in the order they were added unless the keys are strings containing numbers, e.g.:
Object.keys({
"b": "some_value1",
"c": "some_value2",
"a": "some_value3",
})
// ['b', 'c', 'a']
Object.keys({
"b": "some_value1",
"c": "some_value2",
"a": "some_value3",
"3": "some_value4",
"2": "some_value5",
"1": "some_value6",
});
// ['1', '2', '3', 'b', 'c', 'a']
The way to work around this, is to wrap all keys in a Symbol
, by using Symbol.for
. Do not use Symbol(<value>)
, this will not work, e.g.:
Object.getOwnPropertySymbols({
[Symbol.for("b")]: "some_value1",
[Symbol.for("c")]: "some_value2",
[Symbol.for("a")]: "some_value3",
[Symbol.for("3")]: "some_value4",
[Symbol.for("2")]: "some_value5",
[Symbol.for("1")]: "some_value6",
});
// [Symbol('b'), Symbol('c'), Symbol('a'), Symbol('3'), Symbol('2'), Symbol('1')]
Cancel request
import BlobCourier from 'react-native-blob-courier';
// ...
const abortController = new AbortController();
const { signal } = abortController;
const request0 = {
// ...
signal,
};
try {
BlobCourier.fetchBlob(request0);
abortController.abort();
} catch (e) {
if (e.code === ERROR_CANCELED_EXCEPTION) {
// ...
}
}
// ...
Fluent interface
Blob Courier provides a fluent interface, that both protects you from using impossible setting combinations and arguably improves readability.
const req0 = ...
const someResult =
await BlobCourier
.settings({
progressIntervalMilliseconds: 1000,
})
.onProgress((e: BlobProgressEvent) => {
// ...
})
.useDownloadManagerOnAndroid({
description: "Some file description",
enableNotification: true,
title: "Some title"
})
.fetchBlob(req0)
Available methods
fetchBlob(input: BlobFetchRequest)
Required
Field Type Descriptionfilename
string
The name the file will have on disk after fetch.
mimeType
string
What is the mime type of the blob being transferred?
url
string
From which url will the blob be fetched?
Optional
Field Type Description Defaultandroid
AndroidSettings
Settings to be used on Android
{ downloadManager: {}, target: 'cache', useDownloadManager: false }
headers
{ [key: string]: string }
Map of headers to send with the request
{}
ios
IOSSettings
Settings to be used on iOS
{ target: 'cache' }
headers
{ [key: string]: string }
Map of headers to send with the request
{}
method
string
Representing the HTTP method
GET
onProgress
(e: BlobProgressEvent) => void
Function handling progress updates
() => { }
signal
AbortSignal
Request cancellation manager
null
Response
Field Type Descriptiontype
"Managed" \| "Unmanaged"
Was the blob downloaded through Android Download Manager, or without?
data
BlobManagedData \| BlobUnmanagedData
Either managed or HTTP response data
uploadBlob(input: BlobUploadRequest)
Alias for:
const someResult =
await BlobCourier
// ...
.uploadParts({
headers,
method,
parts: {
file:
payload: {
absoluteFilePath,
filename,
mimeType,
},
type: 'file',
},
},
returnResponse,
url,
})
Required
Field Type DescriptionabsoluteFilePath
string
Path to the file to be uploaded
mimeType
string
Mime type of the blob being transferred
url
string
Url to upload the blob to
Optional
Field Type Description Defaultfilename
string
Map of headers to send with the request
<name part of 'absoluteFilePath'>
headers
{ [key: string]: string }
Map of headers to send with the request
{}
method
string
The HTTP method to be used in the request
"POST"
multipartName
string
Name for the file multipart
"file"
onProgress
(e: BlobProgressEvent) => void
Function handling progress updates
() => { }
returnResponse
boolean
Return the HTTP response body?
false
signal
AbortSignal
Request cancellation manager
null
uploadParts(input: BlobMultipartUploadRequest)
Required
Field Type Descriptionparts
{ [key: string]: BlobMultipart }
The parts to be sent
url
string
Url to upload the blob to
Optional
Field Type Description Defaultheaders
{ [key: string]: string }
Map of headers to send with the request
{}
method
string
The HTTP method to be used in the request
"POST"
onProgress
(e: BlobProgressEvent) => void
Function handling progress updates
() => { }
returnResponse
boolean
Return the HTTP response body?
false
signal
AbortSignal
Request cancellation manager
null
Response
Field Type Descriptionresponse
BlobUnmanagedHttpResponse
The HTTP response
AndroidDownloadManagerSettings
Field
Type
Description
description?
string
Description of the downloaded file
enableNotification?
boolean
Display notification when download completes
title?
string
Title to be displayed with the download
AndroidSettings
Field
Type
Description
downloadManager
AndroidDownloadManagerSettings
Settings to be used on download manager
target
"cache" \| "data"
Where will the file be stored?
useDownloadManager
boolean
Enable download manager on Android?
BlobManagedData
Field
Type
Description
absoluteFilePath
string
The absolute file path to where the file was stored
result
"SUCCESS" \| "FAILURE"
Was the request successful or did it fail?
BlobMultipart
Required
Field Type Descriptionpayload
BlobMultipartFormData \| BlobMultipartFormDataFile
Contains the payload of the part
type
"string" \| "file"
What is the type of the payload?
BlobMultipartFormData
Type of string | { [key:string] : any }
BlobMultipartFormDataFile
Required
Field Type DescriptionabsoluteFilePath
string
Path to the file to be uploaded
mimeType
string
Mime type of the blob being transferred
Optional
Field Type Description Defaultfilename
string
Map of headers to send with the request
<name part of 'absoluteFilePath'>
BlobProgressEvent
Field
Type
Description
written
number
Number of bytes processed
total
number
Total number of bytes to be processed
BlobUnmanagedData
Field
Type
Description
absoluteFilePath
string
The absolute file path to where the file was stored
response
BlobUnmanagedHttpResponse
HTTP response, including headers and status code
BlobUnmanagedHttpResponse
Field
Type
Description
code
number
HTTP status code
headers
{ [key: string]: string }
HTTP response headers
IOSSettings
Field
Type
Description
target
"cache" \| "data"
Where will the file be stored?
Example app
You can find an example of how to use the library in the example directory.
Android
Permissions
Android 5.0 and below (API level < 23)
Add the following line to AndroidManifest.xml
.
<manifest xmlns:android="http://schemas.android.com/apk/res/android" (...)>
+ <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
+ <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+ <uses-permission android:name="android.permission.DOWNLOAD_WITHOUT_NOTIFICATION" />
(...)
<application (...)>
<activity (...)>
<intent-filter>
(...)
+ <action android:name="android.intent.action.DOWNLOAD_COMPLETE"/>
</intent-filter>
(...)
Android 6.0+ (API level 23+)
Grant permissions using the PermissionAndroid API, like so:
const function App = () => {
// ...
React.useEffect(() => {
const requestPermissionAsync = async () => {
try {
await PermissionsAndroid.request(
PermissionsAndroid.PERMISSIONS.WRITE_EXTERNAL_STORAGE
);
// ...
} catch (err) {
console.error(err);
}
// ...
};
requestPermissionAsync();
}, []);
// ...
Add to Info.plist
of your app:
<key>NSAllowsArbitraryLoads</key>
<true/>
Using the integrated download manager for Android
This library allows you to use the integrated download manager on Android, this option is not available for iOS.
To enable the download manager, simply set the request's useDownloadManager
property of field android
to true
when passing it to fetchBlob
, or call the useDownloadManagerOnAndroid
method when using the fluent interface.
Shared directories
As this library is focussed on transferring files, it only supports storage to the app's cache and data directories. To move files from these app specific directories to other locations on the filesystem, use another library like @react-native-community/cameraroll, e.g.:
import BlobCourier from 'react-native-blob-courier';
import CameraRoll from '@react-native-community/cameraroll';
// ...
const request = {
filename: 'teh_cage640x360.png',
method: 'GET',
mimeType: 'image/png',
url: 'https://www.placecage.com/640/360',
};
const cageResult = await BlobCourier.fetchBlob(request)
const cageLocalPath = cageResult.data.absoluteFilePath
CameraRoll.save(cageLocalPath);
GitHub
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK