{"id":4732,"url":"https://github.com/edeckers/react-native-blob-courier","last_synced_at":"2025-04-09T10:08:34.219Z","repository":{"id":37309875,"uuid":"300395789","full_name":"edeckers/react-native-blob-courier","owner":"edeckers","description":"Use this library to efficiently download and upload blobs in React Native.","archived":false,"fork":false,"pushed_at":"2024-04-13T01:18:49.000Z","size":3246,"stargazers_count":127,"open_issues_count":17,"forks_count":10,"subscribers_count":3,"default_branch":"develop","last_synced_at":"2024-04-14T00:42:31.190Z","etag":null,"topics":["android","blob","courier","download","fetch","files","hacktoberfest","ios","kotlin","push","react-native","send","swift","transfer","typescript","upload","upload-blobs"],"latest_commit_sha":null,"homepage":"","language":"Kotlin","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mpl-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/edeckers.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null}},"created_at":"2020-10-01T19:12:39.000Z","updated_at":"2024-04-17T02:45:46.114Z","dependencies_parsed_at":"2022-07-12T16:17:31.077Z","dependency_job_id":"b105c83e-9060-44c8-8883-72c4992b05b3","html_url":"https://github.com/edeckers/react-native-blob-courier","commit_stats":{"total_commits":215,"total_committers":7,"mean_commits":"30.714285714285715","dds":"0.19069767441860463","last_synced_commit":"671fe4c82adbd0292d0cc63da67d9e8f3651c729"},"previous_names":[],"tags_count":51,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/edeckers%2Freact-native-blob-courier","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/edeckers%2Freact-native-blob-courier/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/edeckers%2Freact-native-blob-courier/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/edeckers%2Freact-native-blob-courier/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/edeckers","download_url":"https://codeload.github.com/edeckers/react-native-blob-courier/tar.gz/refs/heads/develop","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":246777832,"owners_count":20832032,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["android","blob","courier","download","fetch","files","hacktoberfest","ios","kotlin","push","react-native","send","swift","transfer","typescript","upload","upload-blobs"],"created_at":"2024-01-05T20:17:21.637Z","updated_at":"2025-04-02T08:09:44.763Z","avatar_url":"https://github.com/edeckers.png","language":"Kotlin","funding_links":[],"categories":["Components","Kotlin"],"sub_categories":["Backend"],"readme":"# react-native-blob-courier\n\n[![License: MPL 2.0](https://img.shields.io/badge/License-MPL%202.0-brightgreen.svg)](https://opensource.org/licenses/MPL-2.0)\n[![Build](https://github.com/edeckers/react-native-blob-courier/actions/workflows/ci-build-and-test.yml/badge.svg)](https://github.com/edeckers/react-native-blob-courier/actions)\n\nUse this library to efficiently download and upload data in React Native. The library was inspired by [rn-fetch-blob](https://github.com/joltup/rn-fetch-blob), and is focused strictly on http file transfers.\n\n## Installation\n\nInstall using _yarn_\n\n```sh\nyarn add react-native-blob-courier\n```\n\nOr install using _npm_\n\n```sh\nnpm install react-native-blob-courier\n```\n\n## Requirements\n\n- Android \u003e= 24\n- Android Gradle Plugin \u003e= 7.5.1\n- iOS \u003e= 13.0\n- JDK \u003e= 11\n- React Native \u003e= 0.69.x\n\n_Note: you may have success with earlier versions of React Native but these are neither tested nor supported._\n\n## Usage\n\nThe 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.\n\n### Straightforward down- and upload\n\n```tsx\nimport BlobCourier from 'react-native-blob-courier';\n\n// ...\n\n// Download a file\nconst request0 = {\n  filename: '5MB.zip',\n  method: 'GET',\n  mimeType: 'application/zip',\n  url: 'http://ipv4.download.thinkbroadband.com/5MB.zip',\n};\n\nconst fetchedResult = await BlobCourier.fetchBlob(request0);\nconsole.log(fetchedResult);\n// {\n//   \"data\": {\n//     \"absoluteFilePath\": \"/path/to/app/cache/5MB.zip\",\n//     \"response\": {\n//       \"code\":200,\n//       \"headers\": {\n//         \"some_header\": \"some_value\",\n//         ...\n//       }\n//     },\n//   },\n//   \"type\":\"Unmanaged\"\n// }\n\n// ...\n\n// Upload a file\nconst absoluteFilePath = fetchedResult.data.absoluteFilePath;\n\nconst request1 = {\n  absoluteFilePath,\n  method: 'POST',\n  mimeType: 'application/zip',\n  url: 'https://file.io',\n};\n\nconst uploadResult = await BlobCourier.uploadBlob(request1);\n\nconsole.log(uploadResult):\n// {\n//   \"response\": {\n//     \"code\": {\n//     \"data\": \"\u003csome response\u003e\",\n//     \"headers\": {\n//       \"some_header\": \"some_value\",\n//        ...\n//      }\n//   }\n// }\n\n// Multipart file upload\nconst absoluteFilePath = fetchedResult.data.absoluteFilePath;\n\nconst request2 = {\n  method: 'POST',\n  parts: {\n    body: {\n      payload: 'some_value',\n      type: 'string',\n    },\n    file: {\n      payload: {\n        absoluteFilePath,\n        mimeType: 'application/zip',\n      },\n      type: 'file',\n    },\n  },\n  url: 'https://file.io',\n};\n\nconst multipartUploadResult = await BlobCourier.uploadBlob(request1);\n\nconsole.log(multipartUploadResult):\n// {\n//   \"response\": {\n//     \"code\": {\n//     \"data\": \"\u003csome response\u003e\",\n//     \"headers\": {\n//       \"some_header\": \"some_value\",\n//        ...\n//      }\n//   }\n// }\n```\n\n### Transfer progress reporting\n\n```tsx\nimport BlobCourier from 'react-native-blob-courier';\n\n// ...\n\n// Download a file\nconst request0 = {\n  // ...\n  onProgress: ((e: BlobProgressEvent) =\u003e {\n    console.log(e)\n    // {\n    //  \"written\": \u003csome_number_of_bytes_written\u003e,\n    //  \"total\": \u003csome_total_number_of_bytes\u003e\n    // }\n  })\n};\n\nconst fetchedResult = await BlobCourier.fetchBlob(request0);\n\n// ...\n\n// Upload a file\nconst request1 = {\n  // ...\n  onProgress: ((e: BlobProgressEvent) =\u003e {\n    console.log(e)\n    // {\n    //  \"written\": \u003csome_number_of_bytes_written\u003e,\n    //  \"total\": \u003csome_total_number_of_bytes\u003e\n    // }\n  })\n};\n\nconst uploadResult = await BlobCourier.uploadBlob(request1)\n\n// ...\n\n// Set progress updater interval\nconst request2 = ...\n\nconst someResult =\n  await BlobCourier\n    .fetchBlob({\n      ...request2,\n      progressIntervalMilliseconds: 1000,\n    });\n```\n\n### Managed download on Android (not available on iOS)\n\n```tsx\nimport BlobCourier from 'react-native-blob-courier';\n\n// ...\n\nconst request = {\n  android: {\n    useDownloadManager: true // \u003c--- set useDownloadManager to \"true\"\n  },\n  filename: '5MB.zip',\n  method: 'GET',\n  mimeType: 'application/zip',\n  url: 'http://ipv4.download.thinkbroadband.com/5MB.zip',\n};\n\nconst fetchResult = await BlobCourier.fetchBlob(request);\n\nconsole.log(fetchedResult);\n// {\n//   \"data\": {\n//     \"result\": \"SUCCESS\",\n//     \"absoluteFilePath\": \"/path/to/app/cache/5MB.zip\"\n//   },\n//   \"type\":\"Managed\"\n// }\n```\n\n## Multipart upload\n\nSometimes order of multipart fields matters, and Blob Courier respects the order in which parts are provided. There is a catch though: due to how JavaScript works, when object keys are regular strings they are kept in the order they were added _unless_ the keys are strings containing numbers, e.g.:\n\n```tsx\nObject.keys({\n  \"b\": \"some_value1\",\n  \"c\": \"some_value2\",\n  \"a\": \"some_value3\",\n})\n\n// ['b', 'c', 'a']\n\nObject.keys({\n  \"b\": \"some_value1\",\n  \"c\": \"some_value2\",\n  \"a\": \"some_value3\",\n  \"3\": \"some_value4\",\n  \"2\": \"some_value5\",\n  \"1\": \"some_value6\",\n});\n\n// ['1', '2', '3', 'b', 'c', 'a']\n```\n\nThe way to work around this, is to wrap _all_ keys in a `Symbol`, by using `Symbol.for`. Do not use `Symbol(\u003cvalue\u003e)`, this will not work, e.g.:\n\n```tsx\nObject.getOwnPropertySymbols({\n  [Symbol.for(\"b\")]: \"some_value1\",\n  [Symbol.for(\"c\")]: \"some_value2\",\n  [Symbol.for(\"a\")]: \"some_value3\",\n  [Symbol.for(\"3\")]: \"some_value4\",\n  [Symbol.for(\"2\")]: \"some_value5\",\n  [Symbol.for(\"1\")]: \"some_value6\",\n});\n\n// [Symbol('b'), Symbol('c'), Symbol('a'), Symbol('3'), Symbol('2'), Symbol('1')]\n\n```\n\n### Cancel request\n\n```tsx\nimport BlobCourier from 'react-native-blob-courier';\n\n// ...\n\nconst abortController = new AbortController();\n\nconst { signal } = abortController;\n\nconst request0 = {\n  // ...\n  signal,\n};\n\ntry {\n  BlobCourier.fetchBlob(request0);\n\n  abortController.abort();\n} catch (e) {\n  if (e.code === ERROR_CANCELED_EXCEPTION) {\n    // ...\n  }\n}\n\n// ...\n```\n\n## Fluent interface\n\nBlob Courier provides a fluent interface, that both protects you from using impossible setting combinations and arguably improves readability.\n\n```tsx\nconst req0 = ...\n\nconst someResult =\n  await BlobCourier\n    .settings({\n      progressIntervalMilliseconds: 1000,\n    })\n    .onProgress((e: BlobProgressEvent) =\u003e {\n      // ...\n    })\n    .useDownloadManagerOnAndroid({\n      description: \"Some file description\",\n      enableNotification: true,\n      title: \"Some title\"\n    })\n    .fetchBlob(req0)\n```\n\n## Available methods\n\n### `fetchBlob(input: BlobFetchRequest)`\n\nRequired\n\n| **Field**  | **Type** | **Description**                                                     |\n| ---------- | -------- | ------------------------------------------------------------------- |\n| `filename` | `string` | The name the file will have on disk after fetch.                    |\n| `mimeType` | `string` | What is the mime type of the blob being transferred?                |\n| `url`      | `string` | From which url will the blob be fetched?                            |\n\nOptional\n\n| **Field**    | **Type**                         | **Description**                           | **Default** |\n| ------------ | -------------------------------- | ----------------------------------------- | ---------------------------------------------------- |\n| `android`    | `AndroidSettings`                | Settings to be used on Android            | `{ downloadManager: {}, target: 'cache', useDownloadManager: false }` |\n| `ios`        | `IOSSettings`                    | Settings to be used on iOS                | `{ target: 'cache' }`                                |\n| `headers`    | `{ [key: string]: string }`      | Map of headers to send with the request   | `{}`                                                 |\n| `method`     | `string`                         | Representing the HTTP method              | `GET`                                                |\n| `onProgress` | `(e: BlobProgressEvent) =\u003e void` | Function handling progress updates        | `() =\u003e { }`                                          |\n| `signal`     | `AbortSignal`                    | Request cancellation manager              | `null`                                               |\n\nResponse\n\n| **Field** | **Type**                               | **Description**                                                       |\n| --------- | -------------------------------------- | --------------------------------------------------------------------- |\n| `type`    | `\"Managed\" \\| \"Unmanaged\"`             | Was the blob downloaded through Android Download Manager, or without? |\n| `data`    | `BlobManagedData \\| BlobUnmanagedData` | Either managed or HTTP response data                                  |\n\n### `uploadBlob(input: BlobUploadRequest)`\n\nAlias for:\n\n```tsx\nconst someResult =\n  await BlobCourier\n   // ...\n   .uploadParts({\n     headers,\n     method,\n     parts: {\n       file:\n         payload: {\n           absoluteFilePath,\n           filename,\n           mimeType,\n         },\n         type: 'file',\n       },\n     },\n     returnResponse,\n     url,\n   })\n```\n\nRequired\n\n| **Field**          | **Type** | **Description**                                          |\n| ------------------ | -------- | -------------------------------------------------------- |\n| `absoluteFilePath` | `string` | Path to the file to be uploaded                          |\n| `mimeType`         | `string` | Mime type of the blob being transferred                  |\n| `url`              | `string` | Url to upload the blob to                                |\n\nOptional\n\n| **Field**        | **Type**                         | **Description**                           | **Default**                         |\n| ---------------- | -------------------------------- | ----------------------------------------- | ----------------------------------- |\n| `filename`       | `string`                         | Name of the file on disk                  | `\u003cname part of 'absoluteFilePath'\u003e` |\n| `headers`        | `{ [key: string]: string }`      | Map of headers to send with the request   | `{}`                                |\n| `method`         | `string`                         | The HTTP method to be used in the request | `\"POST\"`                            |\n| `multipartName`  | `string`                         | Name for the file multipart               | `\"file\"`                            |\n| `onProgress`     | `(e: BlobProgressEvent) =\u003e void` | Function handling progress updates        | `() =\u003e { }`                         |\n| `returnResponse` | `boolean`                        | Return the HTTP response body?            | `false`                             |\n| `signal`         | `AbortSignal`                    | Request cancellation manager              | `null`                              |\n\n### `uploadParts(input: BlobMultipartUploadRequest)`\n\nRequired\n\n| **Field**          | **Type**                           | **Description**           |\n| ------------------ | ---------------------------------- | --------------------------|\n| `parts`            | `{ [key: string]: BlobMultipart }` | The parts to be sent      |\n| `url`              | `string`                           | Url to upload the blob to |\n\nOptional\n\n| **Field**        | **Type**                         | **Description**                           | **Default** |\n| ---------------- | -------------------------------- | ----------------------------------------- | ----------- |\n| `headers`        | `{ [key: string]: string }`      | Map of headers to send with the request   | `{}`        |\n| `method`         | `string`                         | The HTTP method to be used in the request | `\"POST\"`    |\n| `onProgress`     | `(e: BlobProgressEvent) =\u003e void` | Function handling progress updates        | `() =\u003e { }` |\n| `returnResponse` | `boolean`                        | Return the HTTP response body?            | `false`     |\n| `signal`         | `AbortSignal`                    | Request cancellation manager              | `null`      |\n\nResponse\n\n| **Field** | **Type**                     | **Description**   |\n| --------- | ---------------------------- | ----------------- |\n| `response` | `BlobUnmanagedHttpResponse` | The HTTP response |\n\n#### `AndroidDownloadManagerSettings`\n\n| **Field**             | **Type**  | **Description**                              |\n| --------------------- | ----------| -------------------------------------------- |\n| `description?`        | `string`  | Description of the downloaded file           |\n| `enableNotification?` | `boolean` | Display notification when download completes |\n| `title?`              | `string`  | Title to be displayed with the download      |\n\n#### `AndroidSettings`\n\n| **Field**            | **Type**                         | **Description**                         |\n| -------------------- | -------------------------------- | --------------------------------------- |\n| `downloadManager`    | `AndroidDownloadManagerSettings` | Settings to be used on download manager |\n| `target`             | `\"cache\" \\| \"data\"`              | Where will the file be stored?          |\n| `useDownloadManager` | `boolean`                        | Enable download manager on Android?     |\n\n#### `BlobManagedData`\n\n| **Field**          | **Type**                 | **Description**                                     |\n| ------------------ | ------------------------ | --------------------------------------------------- |\n| `absoluteFilePath` | `string`                 | The absolute file path to where the file was stored |\n| `result`           | `\"SUCCESS\" \\| \"FAILURE\"` | Was the request successful or did it fail?          |\n\n#### `BlobMultipart`\n\nRequired\n\n| **Field**  | **Type**                                             | **Description**                  |\n| ---------- | ---------------------------------------------------- | -------------------------------- |\n| `payload`  | `BlobMultipartFormData \\| BlobMultipartFormDataFile` | Contains the payload of the part |\n| `type`     | `\"string\" \\| \"file\"`                                 | What is the type of the payload? |\n\n#### `BlobMultipartFormData`\n\nType of `string | { [key:string] : any }`\n\n#### `BlobMultipartFormDataFile`\n\nRequired\n\n| **Field**          | **Type** | **Description**                                          |\n| ------------------ | -------- | -------------------------------------------------------- |\n| `absoluteFilePath` | `string` | Path to the file to be uploaded                          |\n| `mimeType`         | `string` | Mime type of the blob being transferred                  |\n\nOptional\n\n| **Field**        | **Type** | **Description**                           | **Default**                         |\n| ---------------- | ---------| ----------------------------------------- | ----------------------------------- |\n| `filename`       | `string` | Name of the file on disk                  | `\u003cname part of 'absoluteFilePath'\u003e` |\n\n#### `BlobProgressEvent`\n\n| **Field** | **Type** | **Description**                       |\n| --------- | ---------| ------------------------------------- |\n| `written` | `number` | Number of bytes processed             |\n| `total`   | `number` | Total number of bytes to be processed |\n\n#### `BlobUnmanagedData`\n\n| **Field**          | **Type**                    | **Description**                                     |\n| ------------------ | --------------------------- | --------------------------------------------------- |\n| `absoluteFilePath` | `string`                    | The absolute file path to where the file was stored |\n| `response`         | `BlobUnmanagedHttpResponse` | HTTP response, including headers and status code    |\n\n#### `BlobUnmanagedHttpResponse`\n\n| **Field** | **Type**                    | **Description**       |\n| --------- | --------------------------- | --------------------- |\n| `code`    | `number`                    | HTTP status code      |\n| `headers` | `{ [key: string]: string }` | HTTP response headers |\n\n#### `IOSSettings`\n\n| **Field**            | **Type**                         | **Description**                         |\n| -------------------- | -------------------------------- | --------------------------------------- |\n| `target`             | `\"cache\" \\| \"data\"`              | Where will the file be stored?          |\n\n## Example app\n\nYou can find an example of how to use the library in the [example](example) directory.\n\n## Android\n\n### Permissions\n\n### Android 5.1 and below (API level \u003c 23)\n\nAdd the following line to `AndroidManifest.xml`.\n\n```diff\n\u003cmanifest xmlns:android=\"http://schemas.android.com/apk/res/android\" (...)\u003e\n\n+   \u003cuses-permission android:name=\"android.permission.READ_EXTERNAL_STORAGE\" /\u003e\n+   \u003cuses-permission android:name=\"android.permission.WRITE_EXTERNAL_STORAGE\" /\u003e\n+   \u003cuses-permission android:name=\"android.permission.DOWNLOAD_WITHOUT_NOTIFICATION\" /\u003e\n    (...)\n    \u003capplication (...)\u003e\n      \u003cactivity (...)\u003e\n        \u003cintent-filter\u003e\n          (...)\n+          \u003caction android:name=\"android.intent.action.DOWNLOAD_COMPLETE\"/\u003e\n        \u003c/intent-filter\u003e\n    (...)\n```\n\n### Android 6.0+ (API level 23+)\n\nGrant permissions using the [PermissionAndroid API](https://facebook.github.io/react-native/docs/permissionsandroid.html), like so:\n\n```tsx\nconst function App = () =\u003e {\n\n  // ...\n\n  React.useEffect(() =\u003e {\n    const requestPermissionAsync = async () =\u003e {\n      try {\n        await PermissionsAndroid.request(\n          PermissionsAndroid.PERMISSIONS.WRITE_EXTERNAL_STORAGE\n        );\n\n        // ...\n      } catch (err) {\n        console.error(err);\n      }\n\n      // ...\n    };\n\n    requestPermissionAsync();\n  }, []);\n\n  // ...\n```\n\n## iOS\n\nAdd to `Info.plist` of your app:\n\n```xml\n\u003ckey\u003eNSAllowsArbitraryLoads\u003c/key\u003e\n\u003ctrue/\u003e\n```\n\n## Using the integrated download manager for Android\n\nThis library allows you to use the integrated download manager on Android, this option is not available for iOS.\n\nTo 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.\n\n## Shared directories\n\nAs this library is focused 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](https://github.com/react-native-community/react-native-cameraroll), e.g.:\n\n```tsx\nimport BlobCourier from 'react-native-blob-courier';\nimport CameraRoll from '@react-native-community/cameraroll';\n\n// ...\n\nconst request = {\n  filename: 'teh_cage640x360.png',\n  method: 'GET',\n  mimeType: 'image/png',\n  url: 'https://www.placecage.com/640/360',\n};\n\nconst cageResult = await BlobCourier.fetchBlob(request)\n\nconst cageLocalPath = cageResult.data.absoluteFilePath\n\nCameraRoll.save(cageLocalPath);\n```\n\n## Contributing\n\nSee the [contributing guide](CONTRIBUTING.md) to learn how to contribute to the  repository and the development workflow.\n\n## Code of Conduct\n\n[Contributor Code of Conduct](CODE_OF_CONDUCT.md). By participating in this project you agree to abide by its terms.\n\n## License\n\nMPL-2.0\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fedeckers%2Freact-native-blob-courier","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fedeckers%2Freact-native-blob-courier","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fedeckers%2Freact-native-blob-courier/lists"}