rn-file-toolkit 1.0.1
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/FileToolkit.podspec +22 -0
- package/LICENSE +20 -0
- package/README.md +522 -0
- package/android/build.gradle +61 -0
- package/android/src/main/AndroidManifest.xml +13 -0
- package/android/src/main/java/com/filetoolkit/FileToolkitModule.kt +1204 -0
- package/android/src/main/java/com/filetoolkit/FileToolkitPackage.kt +31 -0
- package/android/src/main/res/xml/file_provider_paths.xml +14 -0
- package/app.plugin.js +49 -0
- package/ios/FileToolkit.h +6 -0
- package/ios/FileToolkit.mm +1468 -0
- package/lib/commonjs/NativeFileToolkit.js +9 -0
- package/lib/commonjs/NativeFileToolkit.js.map +1 -0
- package/lib/commonjs/index.js +941 -0
- package/lib/commonjs/index.js.map +1 -0
- package/lib/commonjs/package.json +1 -0
- package/lib/module/NativeFileToolkit.js +5 -0
- package/lib/module/NativeFileToolkit.js.map +1 -0
- package/lib/module/index.js +905 -0
- package/lib/module/index.js.map +1 -0
- package/lib/module/package.json +1 -0
- package/lib/typescript/commonjs/package.json +1 -0
- package/lib/typescript/commonjs/src/NativeFileToolkit.d.ts +29 -0
- package/lib/typescript/commonjs/src/NativeFileToolkit.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/index.d.ts +635 -0
- package/lib/typescript/commonjs/src/index.d.ts.map +1 -0
- package/lib/typescript/module/package.json +1 -0
- package/lib/typescript/module/src/NativeFileToolkit.d.ts +29 -0
- package/lib/typescript/module/src/NativeFileToolkit.d.ts.map +1 -0
- package/lib/typescript/module/src/index.d.ts +635 -0
- package/lib/typescript/module/src/index.d.ts.map +1 -0
- package/package.json +232 -0
- package/src/NativeFileToolkit.ts +29 -0
- package/src/index.tsx +1293 -0
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
require "json"
|
|
2
|
+
|
|
3
|
+
package = JSON.parse(File.read(File.join(__dir__, "package.json")))
|
|
4
|
+
|
|
5
|
+
Pod::Spec.new do |s|
|
|
6
|
+
s.name = "FileToolkit"
|
|
7
|
+
s.version = package["version"]
|
|
8
|
+
s.summary = package["description"]
|
|
9
|
+
s.homepage = package["homepage"]
|
|
10
|
+
s.license = package["license"]
|
|
11
|
+
s.authors = package["author"]
|
|
12
|
+
|
|
13
|
+
s.platforms = { :ios => min_ios_version_supported }
|
|
14
|
+
s.source = { :git => "https://github.com/chavan-labs/rn-file-toolkit.git", :tag => "#{s.version}" }
|
|
15
|
+
|
|
16
|
+
s.source_files = "ios/**/*.{h,m,mm,swift,cpp}"
|
|
17
|
+
s.private_header_files = "ios/**/*.h"
|
|
18
|
+
|
|
19
|
+
s.libraries = "z"
|
|
20
|
+
|
|
21
|
+
install_modules_dependencies(s)
|
|
22
|
+
end
|
package/LICENSE
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Developer
|
|
4
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
5
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
6
|
+
in the Software without restriction, including without limitation the rights
|
|
7
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
8
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
9
|
+
furnished to do so, subject to the following conditions:
|
|
10
|
+
|
|
11
|
+
The above copyright notice and this permission notice shall be included in all
|
|
12
|
+
copies or substantial portions of the Software.
|
|
13
|
+
|
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
15
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
16
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
17
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
18
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
19
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
20
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,522 @@
|
|
|
1
|
+
# rn-file-toolkit
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/rn-file-toolkit)
|
|
4
|
+
[](https://www.npmjs.com/package/rn-file-toolkit)
|
|
5
|
+
[](https://github.com/chavan-labs/rn-file-toolkit/blob/main/LICENSE)
|
|
6
|
+
[](https://www.typescriptlang.org/)
|
|
7
|
+
[](https://github.com/chavan-labs/rn-file-toolkit)
|
|
8
|
+
|
|
9
|
+
The easiest way to download **and manage files** in React Native — with background support, pause/resume, upload, queueing, and built-in filesystem APIs.
|
|
10
|
+
|
|
11
|
+
> 100% pure native code (Kotlin + Swift). Zero third-party dependencies.
|
|
12
|
+
|
|
13
|
+
**Keywords:** react native download, react native file download, react native background download, react native download manager, react native file upload, react native download progress, react native pause resume download, react native cache manager, react native turbo module, expo download, iOS URLSession, Android DownloadManager, react native checksum validation, base64 converter, url to base64, data uri, share file, open file, file sharing, native share, document viewer, download queue, concurrent downloads, queue concurrency, priority queue, batch download, max concurrent downloads, react native filesystem, read file, write file, copy file, move file, stat file, file exists, useDownload hook, download speed, download eta, download progress bytes
|
|
14
|
+
|
|
15
|
+
⭐ **Star this repo if you found it useful** — it helps others discover the project!
|
|
16
|
+
|
|
17
|
+
## Problems?
|
|
18
|
+
|
|
19
|
+
Most React Native file download solutions have one or more of these problems:
|
|
20
|
+
|
|
21
|
+
- **Complexity** — require configuring native modules, background tasks, and notification channels separately
|
|
22
|
+
- **Dependencies** — rely on heavy third-party libraries that bloat your app size
|
|
23
|
+
- **Limited features** — lack pause/resume, checksum validation, or proper background support
|
|
24
|
+
- **No queue** — spawn 20 downloads and you'll crash or saturate the network; libraries like `react-native-blob-util` have no queue at all
|
|
25
|
+
- **Split libraries** — downloading is in one package, filesystem operations in another (read/write/copy/stat/exists), adding extra dependency and complexity
|
|
26
|
+
- **Poor DX** — complicated APIs that require managing multiple IDs, listeners, and cleanup logic
|
|
27
|
+
|
|
28
|
+
**rn-file-toolkit** was built to solve these issues. It provides a simple, unified API while leveraging platform-native download managers (URLSession on iOS, DownloadManager on Android) for reliable, battery-efficient downloads. Everything works out of the box — background downloads, progress tracking, pause/resume — without wrestling with native configuration.
|
|
29
|
+
|
|
30
|
+
## ✨ Features
|
|
31
|
+
|
|
32
|
+
- **`useDownload()` hook** — drop-in React hook with built-in state: `status`, `progress`, `result`, `pause`, `resume`, `cancel` — no competitors have this
|
|
33
|
+
- **Rich progress info** — `onProgress` gives you `percent`, `bytesDownloaded`, `totalBytes`, `speedBps`, and `etaSeconds` — not just a plain number
|
|
34
|
+
- **Zip & Unzip** — compress files/directories and extract ZIP archives natively with **zero third-party dependencies** (`java.util.zip` on Android, system `zlib` on iOS)
|
|
35
|
+
- **Download with progress** — clean `0 → 100` progress natively, no UI freezing
|
|
36
|
+
- **Background downloads** — survive app suspension (iOS background URLSession + Android DownloadManager)
|
|
37
|
+
- **Download Queue** — built-in concurrency-limited queue with `maxConcurrent` control and `high`/`normal` priority
|
|
38
|
+
- **Pause & Resume** — resume mid-download using HTTP Range requests
|
|
39
|
+
- **Auto-Retry** — automatic retry on network errors with exponential backoff
|
|
40
|
+
- **Cancel** — cancel any active download, partial files are cleaned up automatically
|
|
41
|
+
- **Re-attach** — reconnect to background downloads after app restart
|
|
42
|
+
- **Custom Headers** — support for Authorization tokens and custom metadata
|
|
43
|
+
- **Custom Destinations** — save to `downloads`, `cache`, or `documents` folders
|
|
44
|
+
- **Multipart Upload** — simple, native file uploading
|
|
45
|
+
- **Checksum Validation** — verify file integrity (MD5, SHA1, SHA256) after download
|
|
46
|
+
- **Base64 & Data URI Support** — convert base64 strings and data URIs to files natively
|
|
47
|
+
- **URL to Base64** — convert remote URLs (images, videos, gifs) to base64 strings
|
|
48
|
+
- **Share Files** — share files with other apps using native share dialog
|
|
49
|
+
- **Open Files** — open files with default apps or app chooser
|
|
50
|
+
- **Filesystem API (`fs`)** — `exists`, `stat`, `readFile`, `writeFile`, `copyFile`, `moveFile`, `mkdir`, `ls`, `deleteFile`
|
|
51
|
+
- **Expo Support** — includes a config plugin for zero-config integration
|
|
52
|
+
- **TurboModules** — built on the React Native New Architecture
|
|
53
|
+
- **File management** — list, delete individual files, or clear all downloads
|
|
54
|
+
|
|
55
|
+
---
|
|
56
|
+
|
|
57
|
+
## Installation
|
|
58
|
+
|
|
59
|
+
```sh
|
|
60
|
+
npm install rn-file-toolkit
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
---
|
|
64
|
+
|
|
65
|
+
## API
|
|
66
|
+
|
|
67
|
+
### `download(options)`
|
|
68
|
+
|
|
69
|
+
```javascript
|
|
70
|
+
import { download } from 'rn-file-toolkit';
|
|
71
|
+
|
|
72
|
+
const result = await download({
|
|
73
|
+
url: 'https://example.com/file.pdf',
|
|
74
|
+
fileName: 'my_file.pdf',
|
|
75
|
+
headers: { Authorization: 'Bearer <token>' },
|
|
76
|
+
destination: 'documents', // 'downloads' | 'cache' | 'documents'
|
|
77
|
+
checksum: {
|
|
78
|
+
hash: 'd41d8cd98f00b204e9800998ecf8427e',
|
|
79
|
+
algorithm: 'md5',
|
|
80
|
+
},
|
|
81
|
+
onProgress: ({
|
|
82
|
+
percent,
|
|
83
|
+
bytesDownloaded,
|
|
84
|
+
totalBytes,
|
|
85
|
+
speedBps,
|
|
86
|
+
etaSeconds,
|
|
87
|
+
}) => {
|
|
88
|
+
console.log(
|
|
89
|
+
`${percent.toFixed(1)}% — ${(speedBps / 1024).toFixed(
|
|
90
|
+
1
|
|
91
|
+
)} KB/s — ETA ${etaSeconds.toFixed(0)}s`
|
|
92
|
+
);
|
|
93
|
+
},
|
|
94
|
+
// Auto-retry on network failure (optional)
|
|
95
|
+
retry: {
|
|
96
|
+
attempts: 3, // max retry attempts
|
|
97
|
+
delay: 1000, // base delay in ms; doubles each attempt: 1s → 2s → 4s (capped at 30s)
|
|
98
|
+
onRetry: (attempt, error) => console.log(`Retry #${attempt}: ${error}`),
|
|
99
|
+
},
|
|
100
|
+
});
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
> **Retry behaviour:**
|
|
104
|
+
>
|
|
105
|
+
> - Only retries on **network errors** (timeouts, connection drops, socket errors).
|
|
106
|
+
> - Server errors (`4xx`/`5xx`) and checksum mismatches are **not** retried.
|
|
107
|
+
> - Delay doubles each attempt (exponential backoff), capped at 30 seconds.
|
|
108
|
+
> - Works on both iOS and Android for foreground downloads.
|
|
109
|
+
|
|
110
|
+
---
|
|
111
|
+
|
|
112
|
+
### `setQueueOptions(options)` · `getQueueStatus()` · Queue-aware `download()`
|
|
113
|
+
|
|
114
|
+
Avoid crashing your app (or saturating the network) when kicking off many simultaneous downloads. The built-in queue lets you cap concurrency and prioritise individual items — all in pure JavaScript, with zero native changes required.
|
|
115
|
+
|
|
116
|
+
```javascript
|
|
117
|
+
import { download, setQueueOptions, getQueueStatus } from 'rn-file-toolkit';
|
|
118
|
+
|
|
119
|
+
// 1. Configure the global queue (call once, e.g. at app startup)
|
|
120
|
+
setQueueOptions({ maxConcurrent: 3 }); // default is 3
|
|
121
|
+
|
|
122
|
+
// 2. Enqueue downloads — at most 3 will run at the same time
|
|
123
|
+
const promises = urls.map((url) =>
|
|
124
|
+
download({
|
|
125
|
+
url,
|
|
126
|
+
destination: 'documents',
|
|
127
|
+
queue: true, // join the managed queue
|
|
128
|
+
priority: 'normal', // 'high' | 'normal' (default)
|
|
129
|
+
onProgress: (p) => console.log(`${url}: ${p.percent}%`),
|
|
130
|
+
})
|
|
131
|
+
);
|
|
132
|
+
|
|
133
|
+
const results = await Promise.all(promises);
|
|
134
|
+
|
|
135
|
+
// High-priority item jumps ahead of all 'normal' pending items
|
|
136
|
+
download({
|
|
137
|
+
url: 'https://example.com/urgent.pdf',
|
|
138
|
+
queue: true,
|
|
139
|
+
priority: 'high',
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
// 3. Inspect queue state at any time
|
|
143
|
+
const { active, pending, maxConcurrent } = getQueueStatus();
|
|
144
|
+
console.log(`Running: ${active} Waiting: ${pending} Limit: ${maxConcurrent}`);
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
> **Queue behaviour:**
|
|
148
|
+
>
|
|
149
|
+
> - `queue: false` (default) — download starts immediately, bypassing the queue entirely. All existing behaviour is unchanged.
|
|
150
|
+
> - `queue: true` — the download is placed in the JS queue. It starts as soon as an active slot is free.
|
|
151
|
+
> - `priority: 'high'` — item is inserted at the **front** of the pending list, so it runs before any `'normal'` items.
|
|
152
|
+
> - `priority: 'normal'` (default) — item is appended to the **back**.
|
|
153
|
+
> - `setQueueOptions` can be called at any time; if `maxConcurrent` is increased, idle slots are filled immediately.
|
|
154
|
+
> - All other `DownloadOptions` (`onProgress`, `retry`, `checksum`, `headers`, etc.) work exactly the same inside the queue.
|
|
155
|
+
|
|
156
|
+
---
|
|
157
|
+
|
|
158
|
+
### `useDownload()` hook
|
|
159
|
+
|
|
160
|
+
The easiest way to manage a download inside a React component. Get status, rich progress (with speed & ETA), and full controls — all with zero boilerplate.
|
|
161
|
+
|
|
162
|
+
```tsx
|
|
163
|
+
import { useDownload } from 'rn-file-toolkit';
|
|
164
|
+
|
|
165
|
+
function DownloadScreen() {
|
|
166
|
+
const { start, pause, resume, cancel, status, progress, result } =
|
|
167
|
+
useDownload();
|
|
168
|
+
|
|
169
|
+
return (
|
|
170
|
+
<View>
|
|
171
|
+
<Button
|
|
172
|
+
title="Download"
|
|
173
|
+
onPress={() =>
|
|
174
|
+
start({
|
|
175
|
+
url: 'https://example.com/video.mp4',
|
|
176
|
+
destination: 'documents',
|
|
177
|
+
})
|
|
178
|
+
}
|
|
179
|
+
/>
|
|
180
|
+
|
|
181
|
+
{status === 'downloading' && progress && (
|
|
182
|
+
<View>
|
|
183
|
+
<Text>{progress.percent.toFixed(1)}%</Text>
|
|
184
|
+
<Text>Speed: {(progress.speedBps / 1024).toFixed(1)} KB/s</Text>
|
|
185
|
+
<Text>ETA: {progress.etaSeconds.toFixed(0)}s</Text>
|
|
186
|
+
<Text>
|
|
187
|
+
{(progress.bytesDownloaded / 1024 / 1024).toFixed(1)} MB
|
|
188
|
+
{' / '}
|
|
189
|
+
{(progress.totalBytes / 1024 / 1024).toFixed(1)} MB
|
|
190
|
+
</Text>
|
|
191
|
+
<Button title="Pause" onPress={pause} />
|
|
192
|
+
<Button title="Cancel" onPress={cancel} />
|
|
193
|
+
</View>
|
|
194
|
+
)}
|
|
195
|
+
|
|
196
|
+
{status === 'paused' && <Button title="Resume" onPress={resume} />}
|
|
197
|
+
|
|
198
|
+
{status === 'done' && result?.success && (
|
|
199
|
+
<Text>✅ Saved to: {result.filePath}</Text>
|
|
200
|
+
)}
|
|
201
|
+
|
|
202
|
+
{status === 'error' && <Text>❌ {result?.error}</Text>}
|
|
203
|
+
</View>
|
|
204
|
+
);
|
|
205
|
+
}
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
| Property | Type | Description |
|
|
209
|
+
| ---------------- | ---------------------------------------------------------- | ------------------------------- |
|
|
210
|
+
| `start(options)` | `(options: DownloadOptions) => Promise<DownloadResult>` | Start a download |
|
|
211
|
+
| `pause()` | `() => Promise<void>` | Pause the active download |
|
|
212
|
+
| `resume()` | `() => Promise<void>` | Resume a paused download |
|
|
213
|
+
| `cancel()` | `() => Promise<void>` | Cancel and discard the download |
|
|
214
|
+
| `status` | `'idle' \| 'downloading' \| 'paused' \| 'done' \| 'error'` | Current state |
|
|
215
|
+
| `progress` | `ProgressInfo \| null` | Live progress info (see below) |
|
|
216
|
+
| `result` | `DownloadResult \| null` | Final result once complete |
|
|
217
|
+
| `downloadId` | `string \| null` | Active download ID |
|
|
218
|
+
|
|
219
|
+
---
|
|
220
|
+
|
|
221
|
+
### `upload(options)`
|
|
222
|
+
|
|
223
|
+
```javascript
|
|
224
|
+
import { upload } from 'rn-file-toolkit';
|
|
225
|
+
|
|
226
|
+
const result = await upload({
|
|
227
|
+
url: 'https://example.com/api/upload',
|
|
228
|
+
filePath: '/path/to/my_image.jpg',
|
|
229
|
+
fieldName: 'avatar', // default: 'file'
|
|
230
|
+
parameters: {
|
|
231
|
+
userId: '123',
|
|
232
|
+
},
|
|
233
|
+
headers: { 'X-Custom-Header': 'value' },
|
|
234
|
+
onProgress: (p) => console.log(`Uploading: ${p}%`),
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
if (result.success) {
|
|
238
|
+
console.log('Response:', result.data);
|
|
239
|
+
}
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
---
|
|
243
|
+
|
|
244
|
+
### `saveBase64AsFile(options)`
|
|
245
|
+
|
|
246
|
+
Save a base64 string or data URI as a file. Perfect for handling base64 images, documents, or any binary data.
|
|
247
|
+
|
|
248
|
+
```javascript
|
|
249
|
+
import { saveBase64AsFile } from 'rn-file-toolkit';
|
|
250
|
+
|
|
251
|
+
// From data URI (auto-detects file extension)
|
|
252
|
+
const result = await saveBase64AsFile({
|
|
253
|
+
base64Data: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUg...',
|
|
254
|
+
fileName: 'photo.png',
|
|
255
|
+
destination: 'documents', // 'downloads' | 'cache' | 'documents'
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
// From plain base64 string
|
|
259
|
+
const result = await saveBase64AsFile({
|
|
260
|
+
base64Data: 'SGVsbG8gV29ybGQ=',
|
|
261
|
+
fileName: 'hello.txt',
|
|
262
|
+
destination: 'cache',
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
if (result.success) {
|
|
266
|
+
console.log('File saved at:', result.filePath);
|
|
267
|
+
}
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
---
|
|
271
|
+
|
|
272
|
+
### `urlToBase64(options)`
|
|
273
|
+
|
|
274
|
+
Convert a web URL (image, video, gif, etc.) to base64 string. Perfect for converting remote media to base64 for local processing.
|
|
275
|
+
|
|
276
|
+
```javascript
|
|
277
|
+
import { urlToBase64 } from 'rn-file-toolkit';
|
|
278
|
+
|
|
279
|
+
const result = await urlToBase64({
|
|
280
|
+
url: 'https://example.com/photo.jpg',
|
|
281
|
+
headers: { Authorization: 'Bearer <token>' }, // optional
|
|
282
|
+
});
|
|
283
|
+
|
|
284
|
+
if (result.success) {
|
|
285
|
+
console.log('Base64:', result.base64);
|
|
286
|
+
console.log('Data URI:', result.dataUri);
|
|
287
|
+
console.log('MIME Type:', result.mimeType); // e.g., 'image/jpeg'
|
|
288
|
+
|
|
289
|
+
// Use in Image component
|
|
290
|
+
// <Image source={{ uri: result.dataUri }} />
|
|
291
|
+
}
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
---
|
|
295
|
+
|
|
296
|
+
### `shareFile(options)`
|
|
297
|
+
|
|
298
|
+
Share a file with other apps using the native share dialog.
|
|
299
|
+
|
|
300
|
+
```javascript
|
|
301
|
+
import { shareFile } from 'rn-file-toolkit';
|
|
302
|
+
|
|
303
|
+
const result = await shareFile({
|
|
304
|
+
filePath: '/path/to/document.pdf',
|
|
305
|
+
title: 'Share Document', // Android only
|
|
306
|
+
subject: 'Check this out', // Android only
|
|
307
|
+
});
|
|
308
|
+
|
|
309
|
+
if (result.success) {
|
|
310
|
+
console.log('File shared successfully');
|
|
311
|
+
}
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
---
|
|
315
|
+
|
|
316
|
+
### `openFile(options)`
|
|
317
|
+
|
|
318
|
+
Open a file with the default app or app chooser.
|
|
319
|
+
|
|
320
|
+
```javascript
|
|
321
|
+
import { openFile } from 'rn-file-toolkit';
|
|
322
|
+
|
|
323
|
+
const result = await openFile({
|
|
324
|
+
filePath: '/path/to/document.pdf',
|
|
325
|
+
mimeType: 'application/pdf', // optional, auto-detected if not provided
|
|
326
|
+
});
|
|
327
|
+
|
|
328
|
+
if (result.success) {
|
|
329
|
+
console.log('File opened');
|
|
330
|
+
}
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
---
|
|
334
|
+
|
|
335
|
+
### `unzip(sourcePath, destDir)` · `zip(sourcePath, destPath)`
|
|
336
|
+
|
|
337
|
+
Compress and extract ZIP archives natively — **no third-party library required**.
|
|
338
|
+
|
|
339
|
+
- **Android** uses `java.util.zip` (built into the Android SDK, every API level).
|
|
340
|
+
- **iOS** uses `zlib` (system framework, linked via `s.libraries = 'z'` in the podspec — ships on every iPhone/iPad).
|
|
341
|
+
|
|
342
|
+
```javascript
|
|
343
|
+
import { download, unzip, zip } from 'rn-file-toolkit';
|
|
344
|
+
|
|
345
|
+
// ── Unzip a downloaded archive ──────────────────────────────────────────────
|
|
346
|
+
const dl = await download({
|
|
347
|
+
url: 'https://example.com/assets.zip',
|
|
348
|
+
destination: 'cache',
|
|
349
|
+
});
|
|
350
|
+
|
|
351
|
+
if (dl.success) {
|
|
352
|
+
const result = await unzip(
|
|
353
|
+
dl.filePath, // absolute path to the .zip file
|
|
354
|
+
'/path/to/output-folder' // destination directory (created if it doesn't exist)
|
|
355
|
+
);
|
|
356
|
+
|
|
357
|
+
if (result.success) {
|
|
358
|
+
console.log('Destination:', result.destDir);
|
|
359
|
+
console.log('Extracted files:', result.files);
|
|
360
|
+
// result.files → ['/path/to/output-folder/image.png', ...]
|
|
361
|
+
} else {
|
|
362
|
+
console.log('Error:', result.error);
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
// ── Zip a single file ───────────────────────────────────────────────────────
|
|
367
|
+
const zipResult = await zip(
|
|
368
|
+
'/path/to/document.pdf', // source file
|
|
369
|
+
'/path/to/document.zip' // output archive path
|
|
370
|
+
);
|
|
371
|
+
|
|
372
|
+
if (zipResult.success) {
|
|
373
|
+
console.log('Archive created at:', zipResult.zipPath);
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
// ── Zip an entire directory ─────────────────────────────────────────────────
|
|
377
|
+
const dirResult = await zip(
|
|
378
|
+
'/path/to/my-folder', // source directory
|
|
379
|
+
'/path/to/my-folder.zip' // output archive path
|
|
380
|
+
);
|
|
381
|
+
```
|
|
382
|
+
|
|
383
|
+
> **Notes:**
|
|
384
|
+
>
|
|
385
|
+
> - `unzip` auto-creates the destination directory including any intermediate folders.
|
|
386
|
+
> - `unzip` on Android protects against [zip-slip attacks](https://snyk.io/research/zip-slip-vulnerability) by validating each entry path.
|
|
387
|
+
> - `zip` supports both single files and entire directory trees (recursive).
|
|
388
|
+
> - Both functions run on a background thread and never block the JS thread.
|
|
389
|
+
> - Supported compression methods: **Deflate** (method 8) and **Store** (method 0) — the two methods used by virtually every ZIP file.
|
|
390
|
+
|
|
391
|
+
---
|
|
392
|
+
|
|
393
|
+
### Pause / Resume / Cancel
|
|
394
|
+
|
|
395
|
+
```javascript
|
|
396
|
+
import { pauseDownload, resumeDownload, cancelDownload } from 'rn-file-toolkit';
|
|
397
|
+
|
|
398
|
+
await pauseDownload(downloadId);
|
|
399
|
+
await resumeDownload(downloadId);
|
|
400
|
+
await cancelDownload(downloadId);
|
|
401
|
+
```
|
|
402
|
+
|
|
403
|
+
---
|
|
404
|
+
|
|
405
|
+
### Cache Management
|
|
406
|
+
|
|
407
|
+
```javascript
|
|
408
|
+
import { getCachedFiles, deleteFile, clearCache } from 'rn-file-toolkit';
|
|
409
|
+
|
|
410
|
+
// List all files in the cache/documents folders
|
|
411
|
+
const { files } = await getCachedFiles();
|
|
412
|
+
|
|
413
|
+
// Delete a specific file
|
|
414
|
+
await deleteFile('/path/to/file.pdf');
|
|
415
|
+
|
|
416
|
+
// Clear all managed files
|
|
417
|
+
await clearCache();
|
|
418
|
+
```
|
|
419
|
+
|
|
420
|
+
---
|
|
421
|
+
|
|
422
|
+
### `fs` (filesystem namespace)
|
|
423
|
+
|
|
424
|
+
Use built-in file system helpers without adding another dependency.
|
|
425
|
+
|
|
426
|
+
```javascript
|
|
427
|
+
import { fs } from 'rn-file-toolkit';
|
|
428
|
+
|
|
429
|
+
await fs.exists('/path/to/file.pdf'); // → true/false
|
|
430
|
+
|
|
431
|
+
await fs.stat('/path/to/file.pdf');
|
|
432
|
+
// → { path, name, size, modified, isDir }
|
|
433
|
+
|
|
434
|
+
await fs.readFile('/path/to/file.txt'); // utf8 by default
|
|
435
|
+
await fs.readFile('/path/to/file.bin', 'base64');
|
|
436
|
+
|
|
437
|
+
await fs.writeFile('/path/to/file.txt', 'hello');
|
|
438
|
+
await fs.writeFile('/path/to/file.bin', 'SGVsbG8=', 'base64');
|
|
439
|
+
|
|
440
|
+
await fs.copyFile('/src/file.pdf', '/dst/file.pdf');
|
|
441
|
+
await fs.moveFile('/src/file.pdf', '/dst/file.pdf');
|
|
442
|
+
await fs.deleteFile('/path/to/file.pdf');
|
|
443
|
+
|
|
444
|
+
await fs.mkdir('/path/to/folder');
|
|
445
|
+
await fs.ls('/path/to/folder'); // → string[]
|
|
446
|
+
```
|
|
447
|
+
|
|
448
|
+
> `readFile` / `writeFile` support encodings: `'utf8'` (default) and `'base64'`.
|
|
449
|
+
|
|
450
|
+
---
|
|
451
|
+
|
|
452
|
+
## Use Cases
|
|
453
|
+
|
|
454
|
+
### Download from URLs
|
|
455
|
+
|
|
456
|
+
Perfect for downloading files from remote servers with progress tracking, background support, and resume capability.
|
|
457
|
+
|
|
458
|
+
### Convert URLs to Base64
|
|
459
|
+
|
|
460
|
+
Convert remote media (images, videos, gifs) to base64 strings for:
|
|
461
|
+
|
|
462
|
+
- Display in Image components without downloading to disk
|
|
463
|
+
- Inline embedding in HTML/emails
|
|
464
|
+
- Upload to APIs requiring base64 format
|
|
465
|
+
- Cross-platform data transfer
|
|
466
|
+
|
|
467
|
+
### Save Base64/Data URIs
|
|
468
|
+
|
|
469
|
+
Convert base64-encoded data (like images from canvas, camera, or API responses) directly to files:
|
|
470
|
+
|
|
471
|
+
- Canvas-generated images (`canvas.toDataURL()`)
|
|
472
|
+
- Camera/photo library base64 outputs
|
|
473
|
+
- API responses with base64-encoded files
|
|
474
|
+
- Email attachments in base64 format
|
|
475
|
+
|
|
476
|
+
---
|
|
477
|
+
|
|
478
|
+
## Type Reference
|
|
479
|
+
|
|
480
|
+
| Type | Fields |
|
|
481
|
+
| -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
482
|
+
| `DownloadOptions` | `url`, `fileName?`, `background?`, `headers?`, `destination?`, `notificationTitle?`, `notificationDescription?`, `checksum?`, `onProgress?`, `retry?`, `queue?`, `priority?` |
|
|
483
|
+
| `ProgressInfo` | `percent`, `bytesDownloaded`, `totalBytes`, `speedBps`, `etaSeconds` |
|
|
484
|
+
| `RetryOptions` | `attempts`, `delay?`, `onRetry?` |
|
|
485
|
+
| `QueueOptions` | `maxConcurrent?` |
|
|
486
|
+
| `QueueStatus` | `active`, `pending`, `maxConcurrent` |
|
|
487
|
+
| `DownloadStatus` | `'idle' \| 'downloading' \| 'paused' \| 'done' \| 'error'` |
|
|
488
|
+
| `UseDownloadReturn` | `start`, `pause`, `resume`, `cancel`, `status`, `progress`, `result`, `downloadId` |
|
|
489
|
+
| `FsEncoding` | `'utf8' \| 'base64'` |
|
|
490
|
+
| `FsStat` | `path`, `name`, `size`, `modified`, `isDir` |
|
|
491
|
+
| `FsApi` | `exists`, `stat`, `readFile`, `writeFile`, `copyFile`, `moveFile`, `deleteFile`, `mkdir`, `ls` |
|
|
492
|
+
| `UploadOptions` | `url`, `filePath`, `fieldName?`, `headers?`, `parameters?`, `onProgress?` |
|
|
493
|
+
| `SaveBase64Options` | `base64Data`, `fileName?`, `destination?` |
|
|
494
|
+
| `UrlToBase64Options` | `url`, `headers?` |
|
|
495
|
+
| `ShareFileOptions` | `filePath`, `title?`, `subject?` |
|
|
496
|
+
| `OpenFileOptions` | `filePath`, `mimeType?` |
|
|
497
|
+
| `DownloadResult` | `success`, `filePath?`, `downloadId?`, `error?` |
|
|
498
|
+
| `UploadResult` | `success`, `status?`, `data?`, `error?` |
|
|
499
|
+
| `UnzipResult` | `success`, `destDir?`, `files?`, `error?` |
|
|
500
|
+
| `ZipResult` | `success`, `zipPath?`, `error?` |
|
|
501
|
+
| `SaveBase64Result` | `success`, `filePath?`, `error?` |
|
|
502
|
+
| `UrlToBase64Result` | `success`, `base64?`, `mimeType?`, `dataUri?`, `error?` |
|
|
503
|
+
| `ShareFileResult` | `success`, `completed?`, `error?` |
|
|
504
|
+
| `OpenFileResult` | `success`, `error?` |
|
|
505
|
+
| `Checksum` | `hash`, `algorithm: 'md5' \| 'sha1' \| 'sha256'` |
|
|
506
|
+
|
|
507
|
+
---
|
|
508
|
+
|
|
509
|
+
## Articles & Resources
|
|
510
|
+
|
|
511
|
+
- [**I Got Tired of React Native File Downloads Being a Mess — So I Built rn-file-toolkit**](https://medium.com/@chavanrohit413/i-got-tired-of-react-native-file-downloads-being-a-mess-so-i-built-rn-file-toolkit-29b7a8b5c743) — Deep dive into the problems with existing solutions and how rn-file-toolkit solves them
|
|
512
|
+
|
|
513
|
+
---
|
|
514
|
+
|
|
515
|
+
## Links
|
|
516
|
+
|
|
517
|
+
- [GitHub](https://github.com/chavan-labs/rn-file-toolkit)
|
|
518
|
+
- [npm](https://www.npmjs.com/package/rn-file-toolkit)
|
|
519
|
+
|
|
520
|
+
---
|
|
521
|
+
|
|
522
|
+
_Made natively for the community 🤝 by Rohit Chavan_
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
def FileToolkitConfig = [
|
|
2
|
+
kotlinVersion: "2.0.21",
|
|
3
|
+
minSdkVersion: 24,
|
|
4
|
+
compileSdkVersion: 35,
|
|
5
|
+
targetSdkVersion: 35
|
|
6
|
+
]
|
|
7
|
+
|
|
8
|
+
def getExtOrDefault(prop) {
|
|
9
|
+
return rootProject.ext.has(prop) ? rootProject.ext.get(prop) : FileToolkitConfig[prop]
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
buildscript {
|
|
13
|
+
repositories {
|
|
14
|
+
google()
|
|
15
|
+
mavenCentral()
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
dependencies {
|
|
19
|
+
classpath "com.android.tools.build:gradle:8.7.2"
|
|
20
|
+
// noinspection DifferentKotlinGradleVersion
|
|
21
|
+
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:2.0.21"
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
apply plugin: "com.android.library"
|
|
26
|
+
apply plugin: "kotlin-android"
|
|
27
|
+
apply plugin: "com.facebook.react"
|
|
28
|
+
|
|
29
|
+
android {
|
|
30
|
+
namespace "com.filetoolkit"
|
|
31
|
+
|
|
32
|
+
compileSdk getExtOrDefault("compileSdkVersion")
|
|
33
|
+
|
|
34
|
+
defaultConfig {
|
|
35
|
+
minSdk getExtOrDefault("minSdkVersion")
|
|
36
|
+
targetSdk getExtOrDefault("targetSdkVersion")
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
buildFeatures {
|
|
40
|
+
buildConfig true
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
buildTypes {
|
|
44
|
+
release {
|
|
45
|
+
minifyEnabled false
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
lint {
|
|
50
|
+
disable "GradleCompatible"
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
compileOptions {
|
|
54
|
+
sourceCompatibility JavaVersion.VERSION_1_8
|
|
55
|
+
targetCompatibility JavaVersion.VERSION_1_8
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
dependencies {
|
|
60
|
+
implementation "com.facebook.react:react-android"
|
|
61
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
|
2
|
+
<application>
|
|
3
|
+
<provider
|
|
4
|
+
android:name="androidx.core.content.FileProvider"
|
|
5
|
+
android:authorities="${applicationId}.fileprovider"
|
|
6
|
+
android:exported="false"
|
|
7
|
+
android:grantUriPermissions="true">
|
|
8
|
+
<meta-data
|
|
9
|
+
android:name="android.support.FILE_PROVIDER_PATHS"
|
|
10
|
+
android:resource="@xml/file_provider_paths" />
|
|
11
|
+
</provider>
|
|
12
|
+
</application>
|
|
13
|
+
</manifest>
|