@umituz/react-native-filesystem 2.1.19 → 2.1.21
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/package.json
CHANGED
|
@@ -1,72 +1,48 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Download Service
|
|
3
|
-
* Single Responsibility: Handle file download operations
|
|
4
3
|
*/
|
|
5
4
|
|
|
6
5
|
import { File, Paths, Directory } from "expo-file-system";
|
|
7
6
|
import type { FileOperationResult } from "../../domain/entities/File";
|
|
8
7
|
import { FileUtils } from "../../domain/entities/File";
|
|
8
|
+
import { SUPPORTED_DOWNLOAD_EXTENSIONS, DEFAULT_DOWNLOAD_EXTENSION } from "./download.constants";
|
|
9
9
|
import type { DownloadProgressCallback, DownloadWithProgressResult } from "./download.types";
|
|
10
10
|
|
|
11
|
-
const hashUrl = (url: string)
|
|
11
|
+
const hashUrl = (url: string) => {
|
|
12
12
|
let hash = 0;
|
|
13
13
|
for (let i = 0; i < url.length; i++) hash = ((hash << 5) - hash + url.charCodeAt(i)) | 0;
|
|
14
14
|
return Math.abs(hash).toString(36);
|
|
15
15
|
};
|
|
16
16
|
|
|
17
|
-
const getExt = (url: string)
|
|
18
|
-
const
|
|
19
|
-
|
|
17
|
+
const getExt = (url: string) => {
|
|
18
|
+
const parts = url.split("?")[0]?.split(".") || [];
|
|
19
|
+
const ext = parts.length > 1 ? parts.pop()?.toLowerCase() || DEFAULT_DOWNLOAD_EXTENSION : DEFAULT_DOWNLOAD_EXTENSION;
|
|
20
|
+
return (SUPPORTED_DOWNLOAD_EXTENSIONS as readonly string[]).includes(ext) ? ext : DEFAULT_DOWNLOAD_EXTENSION;
|
|
20
21
|
};
|
|
21
22
|
|
|
22
|
-
const getCacheUri = (url: string, dir: string)
|
|
23
|
-
return FileUtils.joinPaths(dir, `cached_${hashUrl(url)}.${getExt(url)}`);
|
|
24
|
-
};
|
|
25
|
-
|
|
26
|
-
interface DownloadError extends Error {
|
|
27
|
-
message: string;
|
|
28
|
-
}
|
|
23
|
+
const getCacheUri = (url: string, dir: string) => FileUtils.joinPaths(dir, `cached_${hashUrl(url)}.${getExt(url)}`);
|
|
29
24
|
|
|
30
25
|
export async function downloadFile(url: string, dest?: string): Promise<FileOperationResult> {
|
|
31
26
|
try {
|
|
32
|
-
const destination = dest
|
|
33
|
-
? new File(dest)
|
|
34
|
-
: new File(Paths.document, FileUtils.generateUniqueFilename("download"));
|
|
27
|
+
const destination = dest ? new File(dest) : new File(Paths.document, FileUtils.generateUniqueFilename("download"));
|
|
35
28
|
const res = await File.downloadFileAsync(url, destination, { idempotent: true });
|
|
36
29
|
return { success: true, uri: res.uri };
|
|
37
|
-
} catch (
|
|
38
|
-
const downloadError = error as DownloadError;
|
|
39
|
-
return { success: false, error: downloadError.message || "Unknown error" };
|
|
40
|
-
}
|
|
30
|
+
} catch (e: any) { return { success: false, error: e.message }; }
|
|
41
31
|
}
|
|
42
32
|
|
|
43
|
-
export async function downloadFileWithProgress(
|
|
44
|
-
url: string,
|
|
45
|
-
cacheDir: string,
|
|
46
|
-
onProgress?: DownloadProgressCallback,
|
|
47
|
-
): Promise<DownloadWithProgressResult> {
|
|
33
|
+
export async function downloadFileWithProgress(url: string, cacheDir: string, onProgress?: DownloadProgressCallback): Promise<DownloadWithProgressResult> {
|
|
48
34
|
try {
|
|
49
35
|
const dir = new Directory(cacheDir);
|
|
50
|
-
if (!dir.exists) {
|
|
51
|
-
dir.create({ intermediates: true, idempotent: true });
|
|
52
|
-
}
|
|
36
|
+
if (!dir.exists) dir.create({ intermediates: true, idempotent: true });
|
|
53
37
|
|
|
54
38
|
const destUri = getCacheUri(url, cacheDir);
|
|
55
|
-
|
|
56
|
-
if (cachedFile.exists) {
|
|
57
|
-
return { success: true, uri: destUri, fromCache: true };
|
|
58
|
-
}
|
|
39
|
+
if (new File(destUri).exists) return { success: true, uri: destUri, fromCache: true };
|
|
59
40
|
|
|
60
41
|
const response = await fetch(url);
|
|
61
|
-
if (!response.ok) {
|
|
62
|
-
|
|
63
|
-
}
|
|
64
|
-
|
|
42
|
+
if (!response.ok) throw new Error(`HTTP ${response.status}`);
|
|
43
|
+
|
|
65
44
|
const totalBytes = parseInt(response.headers.get("content-length") || "0", 10);
|
|
66
|
-
if (!response.body) {
|
|
67
|
-
const downloadResult = await downloadFile(url, destUri);
|
|
68
|
-
return { ...downloadResult, fromCache: false };
|
|
69
|
-
}
|
|
45
|
+
if (!response.body) return { ...(await downloadFile(url, destUri)), fromCache: false };
|
|
70
46
|
|
|
71
47
|
const reader = response.body.getReader();
|
|
72
48
|
const chunks: Uint8Array[] = [];
|
|
@@ -77,39 +53,22 @@ export async function downloadFileWithProgress(
|
|
|
77
53
|
if (done) break;
|
|
78
54
|
chunks.push(value);
|
|
79
55
|
received += value.length;
|
|
80
|
-
onProgress?.({
|
|
81
|
-
totalBytesWritten: received,
|
|
82
|
-
totalBytesExpectedToWrite: totalBytes || received,
|
|
83
|
-
});
|
|
56
|
+
onProgress?.({ totalBytesWritten: received, totalBytesExpectedToWrite: totalBytes || received });
|
|
84
57
|
}
|
|
85
58
|
|
|
86
59
|
const all = new Uint8Array(received);
|
|
87
60
|
let pos = 0;
|
|
88
|
-
for (const
|
|
89
|
-
all.set(chunk, pos);
|
|
90
|
-
pos += chunk.length;
|
|
91
|
-
}
|
|
61
|
+
for (const c of chunks) { all.set(c, pos); pos += c.length; }
|
|
92
62
|
new File(destUri).write(all);
|
|
93
63
|
|
|
94
64
|
return { success: true, uri: destUri, fromCache: false };
|
|
95
|
-
} catch (
|
|
96
|
-
const downloadError = error as DownloadError;
|
|
97
|
-
return { success: false, error: downloadError.message || "Unknown error" };
|
|
98
|
-
}
|
|
65
|
+
} catch (e: any) { return { success: false, error: e.message }; }
|
|
99
66
|
}
|
|
100
67
|
|
|
101
|
-
export const isUrlCached = (url: string, dir: string)
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
return isUrlCached(url, dir) ? getCacheUri(url, dir) : null;
|
|
107
|
-
};
|
|
108
|
-
|
|
109
|
-
export const deleteCachedFile = (url: string, dir: string): boolean => {
|
|
110
|
-
const file = new File(getCacheUri(url, dir));
|
|
111
|
-
if (file.exists) {
|
|
112
|
-
file.delete();
|
|
113
|
-
}
|
|
68
|
+
export const isUrlCached = (url: string, dir: string) => new File(getCacheUri(url, dir)).exists;
|
|
69
|
+
export const getCachedFileUri = (url: string, dir: string) => isUrlCached(url, dir) ? getCacheUri(url, dir) : null;
|
|
70
|
+
export const deleteCachedFile = (url: string, dir: string) => {
|
|
71
|
+
const f = new File(getCacheUri(url, dir));
|
|
72
|
+
if (f.exists) f.delete();
|
|
114
73
|
return true;
|
|
115
74
|
};
|
|
@@ -11,8 +11,7 @@ export function blobToBase64(blob: Blob): Promise<string> {
|
|
|
11
11
|
const reader = new FileReader();
|
|
12
12
|
reader.onloadend = () => {
|
|
13
13
|
const result = reader.result as string;
|
|
14
|
-
|
|
15
|
-
const base64 = result.includes(",") ? result.split(",")[1] : result;
|
|
14
|
+
const base64 = result.includes(",") ? result.split(",")[1] ?? result : result;
|
|
16
15
|
resolve(base64);
|
|
17
16
|
};
|
|
18
17
|
reader.onerror = reject;
|