@symbiote-native/file-system 0.1.0
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/LICENSE +21 -0
- package/README.md +262 -0
- package/build/angular/index.d.ts +1 -0
- package/build/angular/index.js +1 -0
- package/build/core/file-system.d.ts +169 -0
- package/build/core/file-system.js +404 -0
- package/build/core/index.d.ts +2 -0
- package/build/core/index.js +2 -0
- package/build/core/native-module.d.ts +32 -0
- package/build/core/native-module.js +2 -0
- package/build/core/types.d.ts +104 -0
- package/build/core/types.js +15 -0
- package/build/next/directory.d.ts +36 -0
- package/build/next/directory.js +59 -0
- package/build/next/file.d.ts +71 -0
- package/build/next/file.js +207 -0
- package/build/next/index.d.ts +5 -0
- package/build/next/index.js +5 -0
- package/build/next/native-module.d.ts +109 -0
- package/build/next/native-module.js +2 -0
- package/build/next/network-tasks.d.ts +118 -0
- package/build/next/network-tasks.js +333 -0
- package/build/next/node-path.d.ts +16 -0
- package/build/next/node-path.js +353 -0
- package/build/next/path-utilities.d.ts +42 -0
- package/build/next/path-utilities.js +110 -0
- package/build/next/paths.d.ts +22 -0
- package/build/next/paths.js +43 -0
- package/build/next/streams.d.ts +25 -0
- package/build/next/streams.js +55 -0
- package/build/next/types.d.ts +152 -0
- package/build/next/types.js +19 -0
- package/build/next/url-utilities.d.ts +3 -0
- package/build/next/url-utilities.js +62 -0
- package/build/next/watcher.d.ts +19 -0
- package/build/next/watcher.js +64 -0
- package/build-ngc/angular/index.d.ts +1 -0
- package/build-ngc/angular/index.js +2 -0
- package/build-ngc/angular/index.js.map +1 -0
- package/build-ngc/next/directory.d.ts +36 -0
- package/build-ngc/next/directory.js +60 -0
- package/build-ngc/next/directory.js.map +1 -0
- package/build-ngc/next/file.d.ts +71 -0
- package/build-ngc/next/file.js +208 -0
- package/build-ngc/next/file.js.map +1 -0
- package/build-ngc/next/index.d.ts +5 -0
- package/build-ngc/next/index.js +6 -0
- package/build-ngc/next/index.js.map +1 -0
- package/build-ngc/next/native-module.d.ts +109 -0
- package/build-ngc/next/native-module.js +3 -0
- package/build-ngc/next/native-module.js.map +1 -0
- package/build-ngc/next/network-tasks.d.ts +118 -0
- package/build-ngc/next/network-tasks.js +334 -0
- package/build-ngc/next/network-tasks.js.map +1 -0
- package/build-ngc/next/node-path.d.ts +16 -0
- package/build-ngc/next/node-path.js +354 -0
- package/build-ngc/next/node-path.js.map +1 -0
- package/build-ngc/next/path-utilities.d.ts +42 -0
- package/build-ngc/next/path-utilities.js +111 -0
- package/build-ngc/next/path-utilities.js.map +1 -0
- package/build-ngc/next/paths.d.ts +22 -0
- package/build-ngc/next/paths.js +44 -0
- package/build-ngc/next/paths.js.map +1 -0
- package/build-ngc/next/streams.d.ts +25 -0
- package/build-ngc/next/streams.js +56 -0
- package/build-ngc/next/streams.js.map +1 -0
- package/build-ngc/next/types.d.ts +152 -0
- package/build-ngc/next/types.js +20 -0
- package/build-ngc/next/types.js.map +1 -0
- package/build-ngc/next/url-utilities.d.ts +3 -0
- package/build-ngc/next/url-utilities.js +63 -0
- package/build-ngc/next/url-utilities.js.map +1 -0
- package/build-ngc/next/watcher.d.ts +19 -0
- package/build-ngc/next/watcher.js +65 -0
- package/build-ngc/next/watcher.js.map +1 -0
- package/native-link.json +17 -0
- package/package.json +154 -0
- package/src/angular/index.ts +1 -0
- package/src/core/file-system.ts +634 -0
- package/src/core/index.ts +2 -0
- package/src/core/native-module.ts +99 -0
- package/src/core/types.ts +135 -0
- package/src/next/directory.ts +83 -0
- package/src/next/file.ts +318 -0
- package/src/next/index.ts +5 -0
- package/src/next/native-module.ts +188 -0
- package/src/next/network-tasks.ts +456 -0
- package/src/next/node-path.ts +362 -0
- package/src/next/path-utilities.ts +144 -0
- package/src/next/paths.ts +52 -0
- package/src/next/streams-ambient.d.ts +34 -0
- package/src/next/streams.ts +81 -0
- package/src/next/types.ts +194 -0
- package/src/next/url-utilities.ts +70 -0
- package/src/next/watcher.ts +106 -0
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import {
|
|
2
|
+
requireOptionalNativeModule,
|
|
3
|
+
type EventSubscription,
|
|
4
|
+
} from 'expo-modules-core';
|
|
5
|
+
|
|
6
|
+
import type {
|
|
7
|
+
IFileSystemDeletingOptions,
|
|
8
|
+
IFileSystemDownloadOptions,
|
|
9
|
+
IFileSystemDownloadResult,
|
|
10
|
+
IFileSystemFileInfo,
|
|
11
|
+
IFileSystemInfoOptions,
|
|
12
|
+
IFileSystemMakeDirectoryOptions,
|
|
13
|
+
IFileSystemRelocatingOptions,
|
|
14
|
+
IFileSystemRequestDirectoryPermissionsResult,
|
|
15
|
+
IFileSystemUploadOptions,
|
|
16
|
+
IFileSystemUploadResult,
|
|
17
|
+
IFileSystemWritingOptions,
|
|
18
|
+
} from './types';
|
|
19
|
+
|
|
20
|
+
export type INativeFileSystemModule = {
|
|
21
|
+
documentDirectory: string | null;
|
|
22
|
+
cacheDirectory: string | null;
|
|
23
|
+
bundleDirectory: string | null;
|
|
24
|
+
|
|
25
|
+
getInfoAsync?: (
|
|
26
|
+
fileUri: string,
|
|
27
|
+
options: IFileSystemInfoOptions,
|
|
28
|
+
) => Promise<IFileSystemFileInfo>;
|
|
29
|
+
readAsStringAsync?: (
|
|
30
|
+
fileUri: string,
|
|
31
|
+
options: Record<string, unknown>,
|
|
32
|
+
) => Promise<string>;
|
|
33
|
+
getContentUriAsync?: (fileUri: string) => Promise<string>;
|
|
34
|
+
writeAsStringAsync?: (
|
|
35
|
+
fileUri: string,
|
|
36
|
+
contents: string,
|
|
37
|
+
options: IFileSystemWritingOptions,
|
|
38
|
+
) => Promise<void>;
|
|
39
|
+
deleteAsync?: (
|
|
40
|
+
fileUri: string,
|
|
41
|
+
options: IFileSystemDeletingOptions,
|
|
42
|
+
) => Promise<void>;
|
|
43
|
+
moveAsync?: (options: IFileSystemRelocatingOptions) => Promise<void>;
|
|
44
|
+
copyAsync?: (options: IFileSystemRelocatingOptions) => Promise<void>;
|
|
45
|
+
makeDirectoryAsync?: (
|
|
46
|
+
fileUri: string,
|
|
47
|
+
options: IFileSystemMakeDirectoryOptions,
|
|
48
|
+
) => Promise<void>;
|
|
49
|
+
readDirectoryAsync?: (fileUri: string) => Promise<string[]>;
|
|
50
|
+
getFreeDiskStorageAsync?: () => Promise<number>;
|
|
51
|
+
getTotalDiskCapacityAsync?: () => Promise<number>;
|
|
52
|
+
downloadAsync?: (
|
|
53
|
+
uri: string,
|
|
54
|
+
fileUri: string,
|
|
55
|
+
options: IFileSystemDownloadOptions,
|
|
56
|
+
) => Promise<IFileSystemDownloadResult>;
|
|
57
|
+
uploadAsync?: (
|
|
58
|
+
url: string,
|
|
59
|
+
fileUri: string,
|
|
60
|
+
options: IFileSystemUploadOptions,
|
|
61
|
+
) => Promise<IFileSystemUploadResult>;
|
|
62
|
+
networkTaskCancelAsync?: (uuid: string) => Promise<void>;
|
|
63
|
+
addListener?: (
|
|
64
|
+
eventName: string,
|
|
65
|
+
callback: (event: unknown) => void,
|
|
66
|
+
) => EventSubscription;
|
|
67
|
+
uploadTaskStartAsync?: (
|
|
68
|
+
url: string,
|
|
69
|
+
fileUri: string,
|
|
70
|
+
uuid: string,
|
|
71
|
+
options: IFileSystemUploadOptions,
|
|
72
|
+
) => Promise<IFileSystemUploadResult | undefined | null>;
|
|
73
|
+
downloadResumableStartAsync?: (
|
|
74
|
+
url: string,
|
|
75
|
+
fileUri: string,
|
|
76
|
+
uuid: string,
|
|
77
|
+
options: IFileSystemDownloadOptions,
|
|
78
|
+
resumeData?: string,
|
|
79
|
+
) => Promise<IFileSystemDownloadResult | undefined>;
|
|
80
|
+
downloadResumablePauseAsync?: (
|
|
81
|
+
uuid: string,
|
|
82
|
+
) => Promise<{ resumeData?: string } | undefined>;
|
|
83
|
+
requestDirectoryPermissionsAsync?: (
|
|
84
|
+
initialFileUrl: string | null,
|
|
85
|
+
) => Promise<IFileSystemRequestDirectoryPermissionsResult>;
|
|
86
|
+
readSAFDirectoryAsync?: (dirUri: string) => Promise<string[]>;
|
|
87
|
+
makeSAFDirectoryAsync?: (
|
|
88
|
+
parentUri: string,
|
|
89
|
+
dirName: string,
|
|
90
|
+
) => Promise<string>;
|
|
91
|
+
createSAFFileAsync?: (
|
|
92
|
+
parentUri: string,
|
|
93
|
+
fileName: string,
|
|
94
|
+
mimeType: string,
|
|
95
|
+
) => Promise<string>;
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
export const expoFileSystem =
|
|
99
|
+
requireOptionalNativeModule<INativeFileSystemModule>('ExponentFileSystem');
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
export enum FileSystemSessionType {
|
|
2
|
+
BACKGROUND = 0,
|
|
3
|
+
FOREGROUND = 1,
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
export enum FileSystemUploadType {
|
|
7
|
+
BINARY_CONTENT = 0,
|
|
8
|
+
MULTIPART = 1,
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export type IFileSystemDownloadOptions = {
|
|
12
|
+
md5?: boolean;
|
|
13
|
+
cache?: boolean;
|
|
14
|
+
headers?: Record<string, string>;
|
|
15
|
+
sessionType?: FileSystemSessionType;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export type IFileSystemHttpResult = {
|
|
19
|
+
headers: Record<string, string>;
|
|
20
|
+
status: number;
|
|
21
|
+
mimeType: string | null;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export type IFileSystemDownloadResult = IFileSystemHttpResult & {
|
|
25
|
+
uri: string;
|
|
26
|
+
md5?: string;
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
export type IFileSystemAcceptedUploadHttpMethod = 'POST' | 'PUT' | 'PATCH';
|
|
30
|
+
|
|
31
|
+
export type IFileSystemUploadOptionsBinary = {
|
|
32
|
+
uploadType?: FileSystemUploadType;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
export type IFileSystemUploadOptionsMultipart = {
|
|
36
|
+
uploadType: FileSystemUploadType;
|
|
37
|
+
fieldName?: string;
|
|
38
|
+
mimeType?: string;
|
|
39
|
+
parameters?: Record<string, string>;
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
export type IFileSystemUploadOptions = (
|
|
43
|
+
IFileSystemUploadOptionsBinary | IFileSystemUploadOptionsMultipart
|
|
44
|
+
) & {
|
|
45
|
+
headers?: Record<string, string>;
|
|
46
|
+
httpMethod?: IFileSystemAcceptedUploadHttpMethod;
|
|
47
|
+
sessionType?: FileSystemSessionType;
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
export type IFileSystemUploadResult = IFileSystemHttpResult & {
|
|
51
|
+
body: string;
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
export type IFileSystemNetworkTaskProgressCallback<
|
|
55
|
+
T extends IFileSystemDownloadProgressData | IFileSystemUploadProgressData,
|
|
56
|
+
> = (data: T) => void;
|
|
57
|
+
|
|
58
|
+
export type IFileSystemDownloadProgressData = {
|
|
59
|
+
totalBytesWritten: number;
|
|
60
|
+
totalBytesExpectedToWrite: number;
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
export type IFileSystemUploadProgressData = {
|
|
64
|
+
totalBytesSent: number;
|
|
65
|
+
totalBytesExpectedToSend: number;
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
export type IFileSystemDownloadPauseState = {
|
|
69
|
+
url: string;
|
|
70
|
+
fileUri: string;
|
|
71
|
+
options: IFileSystemDownloadOptions;
|
|
72
|
+
resumeData?: string;
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
export type IFileSystemFileInfo =
|
|
76
|
+
| {
|
|
77
|
+
exists: true;
|
|
78
|
+
uri: string;
|
|
79
|
+
size: number;
|
|
80
|
+
isDirectory: boolean;
|
|
81
|
+
modificationTime: number;
|
|
82
|
+
md5?: string;
|
|
83
|
+
}
|
|
84
|
+
| {
|
|
85
|
+
exists: false;
|
|
86
|
+
uri: string;
|
|
87
|
+
isDirectory: false;
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
export enum FileSystemEncodingType {
|
|
91
|
+
UTF8 = 'utf8',
|
|
92
|
+
Base64 = 'base64',
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export type IFileSystemReadingOptions = {
|
|
96
|
+
encoding?: FileSystemEncodingType | 'utf8' | 'base64';
|
|
97
|
+
position?: number;
|
|
98
|
+
length?: number;
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
export type IFileSystemWritingOptions = {
|
|
102
|
+
encoding?: FileSystemEncodingType | 'utf8' | 'base64';
|
|
103
|
+
append?: boolean;
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
export type IFileSystemDeletingOptions = {
|
|
107
|
+
idempotent?: boolean;
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
export type IFileSystemInfoOptions = {
|
|
111
|
+
md5?: boolean;
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
export type IFileSystemRelocatingOptions = {
|
|
115
|
+
from: string;
|
|
116
|
+
to: string;
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
export type IFileSystemMakeDirectoryOptions = {
|
|
120
|
+
intermediates?: boolean;
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
export type IFileSystemProgressEvent<T> = {
|
|
124
|
+
uuid: string;
|
|
125
|
+
data: T;
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
export type IFileSystemRequestDirectoryPermissionsResult =
|
|
129
|
+
| {
|
|
130
|
+
granted: false;
|
|
131
|
+
}
|
|
132
|
+
| {
|
|
133
|
+
granted: true;
|
|
134
|
+
directoryUri: string;
|
|
135
|
+
};
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { File } from './file';
|
|
2
|
+
import { expoFileSystemNext } from './native-module';
|
|
3
|
+
import { Paths } from './paths';
|
|
4
|
+
import { FileSystemWatcher } from './watcher';
|
|
5
|
+
import type {
|
|
6
|
+
IFileSystemWatchEvent,
|
|
7
|
+
IFileSystemWatchOptions,
|
|
8
|
+
IFileSystemWatchSubscription,
|
|
9
|
+
} from './types';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Represents a directory on the filesystem.
|
|
13
|
+
*
|
|
14
|
+
* A `Directory` instance can be created for any path, and does not need to exist on the
|
|
15
|
+
* filesystem during creation.
|
|
16
|
+
*/
|
|
17
|
+
export class Directory extends expoFileSystemNext.FileSystemDirectory {
|
|
18
|
+
static pickDirectoryAsync: (initialUri?: string) => Promise<Directory>;
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Creates an instance of a directory. It can be created for any path, and does not need to
|
|
22
|
+
* exist on the filesystem during creation.
|
|
23
|
+
*/
|
|
24
|
+
constructor(...uris: (string | File | Directory)[]) {
|
|
25
|
+
super(Paths.join(...uris));
|
|
26
|
+
this.validatePath();
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/** Directory containing this directory. */
|
|
30
|
+
get parentDirectory() {
|
|
31
|
+
return new Directory(Paths.join(this.uri, '..'));
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Lists the contents of a directory. Calling this method if the parent directory does not
|
|
36
|
+
* exist will throw an error.
|
|
37
|
+
*/
|
|
38
|
+
override list(): (Directory | File)[] {
|
|
39
|
+
return super
|
|
40
|
+
.listAsRecords()
|
|
41
|
+
.map(({ isDirectory, uri }) =>
|
|
42
|
+
isDirectory ? new Directory(uri) : new File(uri),
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/** Directory name. */
|
|
47
|
+
get name() {
|
|
48
|
+
return Paths.basename(this.uri);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
override createFile(name: string, mimeType: string | null): File {
|
|
52
|
+
return new File(super.createFile(name, mimeType).uri);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
override createDirectory(name: string): Directory {
|
|
56
|
+
return new Directory(super.createDirectory(name).uri);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Watches this directory for changes to its contents or the directory itself. On iOS,
|
|
61
|
+
* child changes are surfaced as a coarse-grained `modified` event on the directory itself, so
|
|
62
|
+
* filtering for child-level `created`, `deleted`, or `renamed` events is not reliable. The
|
|
63
|
+
* watcher automatically stops when the directory is deleted or renamed. To stop watching
|
|
64
|
+
* manually, call `remove()` on the returned subscription.
|
|
65
|
+
*/
|
|
66
|
+
watch(
|
|
67
|
+
callback: (event: IFileSystemWatchEvent<File | Directory>) => void,
|
|
68
|
+
options?: IFileSystemWatchOptions,
|
|
69
|
+
): IFileSystemWatchSubscription {
|
|
70
|
+
return new FileSystemWatcher<File | Directory>(
|
|
71
|
+
this.uri,
|
|
72
|
+
callback,
|
|
73
|
+
options,
|
|
74
|
+
(uri, isDirectory) => (isDirectory ? new Directory(uri) : new File(uri)),
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
Directory.pickDirectoryAsync = async function (initialUri?: string) {
|
|
80
|
+
const directory = (await expoFileSystemNext.pickDirectoryAsync(initialUri))
|
|
81
|
+
.uri;
|
|
82
|
+
return new Directory(directory);
|
|
83
|
+
};
|
package/src/next/file.ts
ADDED
|
@@ -0,0 +1,318 @@
|
|
|
1
|
+
import { uuid, type EventSubscription } from 'expo-modules-core';
|
|
2
|
+
|
|
3
|
+
import { Directory } from './directory';
|
|
4
|
+
import { expoFileSystemNext } from './native-module';
|
|
5
|
+
import { DownloadTask, UploadTask } from './network-tasks';
|
|
6
|
+
import { Paths } from './paths';
|
|
7
|
+
import {
|
|
8
|
+
FileSystemReadableStreamSource,
|
|
9
|
+
FileSystemWritableSink,
|
|
10
|
+
} from './streams';
|
|
11
|
+
import { FileSystemWatcher } from './watcher';
|
|
12
|
+
import {
|
|
13
|
+
FileMode,
|
|
14
|
+
type IFileSystemDownloadOptions,
|
|
15
|
+
type IFileSystemDownloadProgress,
|
|
16
|
+
type IFileSystemDownloadTaskOptions,
|
|
17
|
+
type IFileSystemUploadOptions,
|
|
18
|
+
type IFileSystemUploadResult,
|
|
19
|
+
type IFileSystemWatchEvent,
|
|
20
|
+
type IFileSystemWatchOptions,
|
|
21
|
+
type IFileSystemWatchSubscription,
|
|
22
|
+
type IPickFileOptions,
|
|
23
|
+
type IPickMultipleFilesOptions,
|
|
24
|
+
type IPickMultipleFilesResult,
|
|
25
|
+
type IPickSingleFileOptions,
|
|
26
|
+
type IPickSingleFileResult,
|
|
27
|
+
} from './types';
|
|
28
|
+
|
|
29
|
+
function createAbortError(reason?: unknown): Error {
|
|
30
|
+
const message =
|
|
31
|
+
typeof reason === 'string' ? reason : 'The operation was aborted.';
|
|
32
|
+
const error = new Error(message);
|
|
33
|
+
error.name = 'AbortError';
|
|
34
|
+
return error;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function parsePickFileOptions(
|
|
38
|
+
initialUriOrOptions?: string | IPickFileOptions,
|
|
39
|
+
mimeType?: string,
|
|
40
|
+
): { options: IPickFileOptions; usingOldAPI: boolean } {
|
|
41
|
+
if (typeof initialUriOrOptions === 'object') {
|
|
42
|
+
return { options: initialUriOrOptions, usingOldAPI: false };
|
|
43
|
+
}
|
|
44
|
+
return {
|
|
45
|
+
options: {
|
|
46
|
+
initialUri: initialUriOrOptions,
|
|
47
|
+
mimeTypes: mimeType,
|
|
48
|
+
multipleFiles: false,
|
|
49
|
+
},
|
|
50
|
+
usingOldAPI:
|
|
51
|
+
mimeType !== undefined || typeof initialUriOrOptions === 'string',
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Represents a file on the filesystem.
|
|
57
|
+
*
|
|
58
|
+
* A `File` instance can be created for any path, and does not need to exist on the filesystem
|
|
59
|
+
* during creation. The constructor accepts an array of strings that are joined to create the
|
|
60
|
+
* file URI. The first argument can also be a `Directory` instance (like `Paths.cache`) or a
|
|
61
|
+
* `File` instance (which creates a new reference to the same file).
|
|
62
|
+
*/
|
|
63
|
+
export class File extends expoFileSystemNext.FileSystemFile {
|
|
64
|
+
/**
|
|
65
|
+
* Downloads a file from the network.
|
|
66
|
+
*
|
|
67
|
+
* On Android, the response body streams directly into the target file. If the download fails
|
|
68
|
+
* after it starts, a partially written file may remain at the destination. On iOS, the
|
|
69
|
+
* download first completes in a temporary location and the file is moved into place only
|
|
70
|
+
* after success, so no file is left behind when the request fails.
|
|
71
|
+
*/
|
|
72
|
+
static downloadFileAsync: (
|
|
73
|
+
url: string,
|
|
74
|
+
destination: Directory | File,
|
|
75
|
+
options?: IFileSystemDownloadOptions,
|
|
76
|
+
) => Promise<File>;
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Opens the system file picker for selecting a single file.
|
|
80
|
+
*/
|
|
81
|
+
static pickFileAsync(
|
|
82
|
+
options?: IPickSingleFileOptions,
|
|
83
|
+
): Promise<IPickSingleFileResult>;
|
|
84
|
+
/**
|
|
85
|
+
* Opens the system file picker for selecting multiple files.
|
|
86
|
+
*/
|
|
87
|
+
static pickFileAsync(
|
|
88
|
+
options?: IPickMultipleFilesOptions,
|
|
89
|
+
): Promise<IPickMultipleFilesResult>;
|
|
90
|
+
/**
|
|
91
|
+
* @deprecated Use `pickFileAsync({initialUri, mimeTypes: mimeType})` instead.
|
|
92
|
+
*/
|
|
93
|
+
static pickFileAsync(
|
|
94
|
+
initialUri?: string,
|
|
95
|
+
mimeType?: string,
|
|
96
|
+
): Promise<File | File[]>;
|
|
97
|
+
static async pickFileAsync(
|
|
98
|
+
initialUriOrOptions?: string | IPickFileOptions,
|
|
99
|
+
mimeType?: string,
|
|
100
|
+
): Promise<File | File[] | IPickSingleFileResult | IPickMultipleFilesResult> {
|
|
101
|
+
const { options, usingOldAPI } = parsePickFileOptions(
|
|
102
|
+
initialUriOrOptions,
|
|
103
|
+
mimeType,
|
|
104
|
+
);
|
|
105
|
+
try {
|
|
106
|
+
if (options.multipleFiles) {
|
|
107
|
+
const files = await expoFileSystemNext.pickFileAsync(options);
|
|
108
|
+
return {
|
|
109
|
+
result: files.map(file => new File(file.uri)),
|
|
110
|
+
canceled: false,
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
const file = await expoFileSystemNext.pickFileAsync(options);
|
|
114
|
+
if (usingOldAPI) {
|
|
115
|
+
return new File(file.uri);
|
|
116
|
+
}
|
|
117
|
+
return {
|
|
118
|
+
result: new File(file.uri),
|
|
119
|
+
canceled: false,
|
|
120
|
+
};
|
|
121
|
+
} catch (error) {
|
|
122
|
+
if (usingOldAPI) {
|
|
123
|
+
throw error;
|
|
124
|
+
}
|
|
125
|
+
return {
|
|
126
|
+
result: null,
|
|
127
|
+
canceled: true,
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Creates an instance of a file. It can be created for any path, and does not need to exist on
|
|
134
|
+
* the filesystem during creation.
|
|
135
|
+
*/
|
|
136
|
+
constructor(...uris: (string | File | Directory)[]) {
|
|
137
|
+
super(Paths.join(...uris));
|
|
138
|
+
this.validatePath();
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/** Directory containing the file. */
|
|
142
|
+
get parentDirectory() {
|
|
143
|
+
return new Directory(Paths.dirname(this.uri));
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/** File extension, e.g. `.png`. */
|
|
147
|
+
get extension() {
|
|
148
|
+
return Paths.extname(this.uri);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/** File name. Includes the extension. */
|
|
152
|
+
get name() {
|
|
153
|
+
return Paths.basename(this.uri);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
readableStream() {
|
|
157
|
+
return new ReadableStream(
|
|
158
|
+
new FileSystemReadableStreamSource(super.open(FileMode.ReadOnly)),
|
|
159
|
+
);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
writableStream() {
|
|
163
|
+
return new WritableStream<Uint8Array>(
|
|
164
|
+
new FileSystemWritableSink(super.open(FileMode.WriteOnly)),
|
|
165
|
+
);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
async arrayBuffer(): Promise<ArrayBuffer> {
|
|
169
|
+
const bytes = await this.bytes();
|
|
170
|
+
return bytes.buffer;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
async json(): Promise<unknown> {
|
|
174
|
+
return JSON.parse(await this.text());
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
async formData(): ReturnType<Response['formData']> {
|
|
178
|
+
return new Response(await this.arrayBuffer(), {
|
|
179
|
+
headers: this.type ? { 'Content-Type': this.type } : undefined,
|
|
180
|
+
}).formData();
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
stream(): ReadableStream<Uint8Array<ArrayBuffer>> {
|
|
184
|
+
return this.readableStream();
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
slice(start?: number, end?: number, contentType?: string): Blob {
|
|
188
|
+
// React Native's own ambient `Blob` declaration (react-native/src/types/globals.d.ts) is
|
|
189
|
+
// narrower than its runtime: `blobParts` is typed as `(Blob | string)[]` only (the real
|
|
190
|
+
// native implementation accepts raw bytes), and `BlobOptions` requires both `type` and
|
|
191
|
+
// `lastModified` even though `lastModified` is a `File`-only concept the Web `Blob` spec
|
|
192
|
+
// doesn't have at all. Cast at this one call — RN's declared contract, not our logic, is
|
|
193
|
+
// what's incomplete; `lastModified` is supplied only to satisfy the type and is not read by
|
|
194
|
+
// any real `Blob` implementation.
|
|
195
|
+
const bytes = this.bytesSync().slice(start, end) as unknown as string;
|
|
196
|
+
return new Blob([bytes], {
|
|
197
|
+
type: contentType ?? '',
|
|
198
|
+
lastModified: Date.now(),
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Uploads this file to a server and starts the request immediately.
|
|
204
|
+
*/
|
|
205
|
+
upload(
|
|
206
|
+
url: string,
|
|
207
|
+
options?: IFileSystemUploadOptions,
|
|
208
|
+
): Promise<IFileSystemUploadResult> {
|
|
209
|
+
return new UploadTask(this, url, options).uploadAsync();
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Creates an upload task for this file without starting it.
|
|
214
|
+
*/
|
|
215
|
+
createUploadTask(
|
|
216
|
+
url: string,
|
|
217
|
+
options?: IFileSystemUploadOptions,
|
|
218
|
+
): UploadTask {
|
|
219
|
+
return new UploadTask(this, url, options);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* Creates a download task without starting it.
|
|
224
|
+
*/
|
|
225
|
+
static createDownloadTask(
|
|
226
|
+
url: string,
|
|
227
|
+
destination: File | Directory,
|
|
228
|
+
options?: IFileSystemDownloadTaskOptions,
|
|
229
|
+
): DownloadTask {
|
|
230
|
+
return new DownloadTask(url, destination, options);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* Watches this file for changes on the filesystem. The watcher automatically stops when the
|
|
235
|
+
* file is deleted or renamed. To stop watching manually, call `remove()` on the returned
|
|
236
|
+
* subscription.
|
|
237
|
+
*/
|
|
238
|
+
watch(
|
|
239
|
+
callback: (event: IFileSystemWatchEvent<File>) => void,
|
|
240
|
+
options?: IFileSystemWatchOptions,
|
|
241
|
+
): IFileSystemWatchSubscription {
|
|
242
|
+
return new FileSystemWatcher<File>(
|
|
243
|
+
this.uri,
|
|
244
|
+
callback,
|
|
245
|
+
options,
|
|
246
|
+
uri => new File(uri),
|
|
247
|
+
);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
// Cannot use `static` keyword in class declaration because of a runtime error (matches upstream's
|
|
252
|
+
// own comment — assigning after the class body avoids whatever native interop issue upstream ran
|
|
253
|
+
// into with `SharedObject`-extending classes).
|
|
254
|
+
File.downloadFileAsync = async function downloadFileAsync(
|
|
255
|
+
url: string,
|
|
256
|
+
to: File | Directory,
|
|
257
|
+
options?: IFileSystemDownloadOptions,
|
|
258
|
+
) {
|
|
259
|
+
const needsUuid = options?.onProgress || options?.signal;
|
|
260
|
+
const downloadUuid = needsUuid ? uuid.v4() : undefined;
|
|
261
|
+
|
|
262
|
+
let subscription: EventSubscription | undefined;
|
|
263
|
+
let abortHandler: (() => void) | undefined;
|
|
264
|
+
let lastProgress: IFileSystemDownloadProgress | undefined;
|
|
265
|
+
|
|
266
|
+
try {
|
|
267
|
+
if (options?.signal?.aborted) {
|
|
268
|
+
throw createAbortError(options.signal.reason);
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
if (downloadUuid && options?.onProgress) {
|
|
272
|
+
subscription = expoFileSystemNext.addListener(
|
|
273
|
+
'downloadProgress',
|
|
274
|
+
event => {
|
|
275
|
+
if (event.uuid === downloadUuid) {
|
|
276
|
+
lastProgress = event.data;
|
|
277
|
+
options.onProgress!(lastProgress);
|
|
278
|
+
}
|
|
279
|
+
},
|
|
280
|
+
);
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
if (downloadUuid && options?.signal) {
|
|
284
|
+
abortHandler = () => {
|
|
285
|
+
expoFileSystemNext.cancelDownloadAsync(downloadUuid);
|
|
286
|
+
};
|
|
287
|
+
options.signal.addEventListener('abort', abortHandler, { once: true });
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
const outputURI = await expoFileSystemNext.downloadFileAsync(
|
|
291
|
+
url,
|
|
292
|
+
to,
|
|
293
|
+
options,
|
|
294
|
+
downloadUuid,
|
|
295
|
+
);
|
|
296
|
+
const file = new File(outputURI);
|
|
297
|
+
const fileSize = file.size ?? 0;
|
|
298
|
+
if (
|
|
299
|
+
options?.onProgress &&
|
|
300
|
+
fileSize > 0 &&
|
|
301
|
+
(lastProgress?.bytesWritten !== fileSize ||
|
|
302
|
+
lastProgress?.totalBytes !== fileSize)
|
|
303
|
+
) {
|
|
304
|
+
options.onProgress({ bytesWritten: fileSize, totalBytes: fileSize });
|
|
305
|
+
}
|
|
306
|
+
return file;
|
|
307
|
+
} catch (error) {
|
|
308
|
+
if (options?.signal?.aborted) {
|
|
309
|
+
throw createAbortError(options.signal.reason);
|
|
310
|
+
}
|
|
311
|
+
throw error;
|
|
312
|
+
} finally {
|
|
313
|
+
subscription?.remove();
|
|
314
|
+
if (abortHandler && options?.signal) {
|
|
315
|
+
options.signal.removeEventListener('abort', abortHandler);
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
};
|