expo-file-system 18.0.2 → 18.0.4
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/CHANGELOG.md +12 -0
- package/android/build.gradle +2 -2
- package/android/src/main/java/expo/modules/filesystem/next/FileSystemFile.kt +7 -1
- package/android/src/main/java/expo/modules/filesystem/next/FileSystemFileHandle.kt +68 -0
- package/android/src/main/java/expo/modules/filesystem/next/FileSystemNextExceptions.kt +10 -0
- package/android/src/main/java/expo/modules/filesystem/next/FileSystemNextModule.kt +31 -0
- package/android/src/main/java/expo/modules/filesystem/next/FileSystemPath.kt +1 -1
- package/build/next/ExpoFileSystem.types.d.ts +18 -1
- package/build/next/ExpoFileSystem.types.d.ts.map +1 -1
- package/build/next/FileSystem.d.ts +3 -0
- package/build/next/FileSystem.d.ts.map +1 -1
- package/build/next/streams.d.ts +17 -0
- package/build/next/streams.d.ts.map +1 -0
- package/ios/Next/FileSystemFile.swift +10 -1
- package/ios/Next/FileSystemFileHandle.swift +53 -0
- package/ios/Next/FileSystemNextExceptions.swift +6 -0
- package/ios/Next/FileSystemNextModule.swift +32 -0
- package/package.json +5 -2
- package/src/next/ExpoFileSystem.types.ts +40 -1
- package/src/next/FileSystem.ts +11 -0
- package/src/next/streams.ts +64 -0
package/CHANGELOG.md
CHANGED
|
@@ -10,6 +10,18 @@
|
|
|
10
10
|
|
|
11
11
|
### 💡 Others
|
|
12
12
|
|
|
13
|
+
## 18.0.4 — 2024-11-19
|
|
14
|
+
|
|
15
|
+
### 🎉 New features
|
|
16
|
+
|
|
17
|
+
- [next] Added `.bytes()` and writing a `Uint8Array`. ([#33020](https://github.com/expo/expo/pull/33020) by [@aleqsio](https://github.com/aleqsio))
|
|
18
|
+
|
|
19
|
+
## 18.0.3 — 2024-11-13
|
|
20
|
+
|
|
21
|
+
### 🎉 New features
|
|
22
|
+
|
|
23
|
+
- [next] Add file handles. ([#31738](https://github.com/expo/expo/pull/31738) by [@aleqsio](https://github.com/aleqsio))
|
|
24
|
+
|
|
13
25
|
## 18.0.2 — 2024-11-11
|
|
14
26
|
|
|
15
27
|
_This version does not introduce any user-facing changes._
|
package/android/build.gradle
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
apply plugin: 'com.android.library'
|
|
2
2
|
|
|
3
3
|
group = 'host.exp.exponent'
|
|
4
|
-
version = '18.0.
|
|
4
|
+
version = '18.0.4'
|
|
5
5
|
|
|
6
6
|
def expoModulesCorePlugin = new File(project(":expo-modules-core").projectDir.absolutePath, "ExpoModulesCorePlugin.gradle")
|
|
7
7
|
apply from: expoModulesCorePlugin
|
|
@@ -14,7 +14,7 @@ android {
|
|
|
14
14
|
namespace "expo.modules.filesystem"
|
|
15
15
|
defaultConfig {
|
|
16
16
|
versionCode 30
|
|
17
|
-
versionName "18.0.
|
|
17
|
+
versionName "18.0.4"
|
|
18
18
|
}
|
|
19
19
|
}
|
|
20
20
|
|
|
@@ -53,7 +53,7 @@ class FileSystemFile(file: File) : FileSystemPath(file) {
|
|
|
53
53
|
create()
|
|
54
54
|
}
|
|
55
55
|
FileOutputStream(file).use {
|
|
56
|
-
it.write(content.toDirectBuffer()
|
|
56
|
+
it.channel.write(content.toDirectBuffer())
|
|
57
57
|
}
|
|
58
58
|
}
|
|
59
59
|
|
|
@@ -74,6 +74,12 @@ class FileSystemFile(file: File) : FileSystemPath(file) {
|
|
|
74
74
|
return Base64.encodeToString(file.readBytes(), Base64.NO_WRAP)
|
|
75
75
|
}
|
|
76
76
|
|
|
77
|
+
fun bytes(): ByteArray {
|
|
78
|
+
validateType()
|
|
79
|
+
validatePermission(Permission.READ)
|
|
80
|
+
return file.readBytes()
|
|
81
|
+
}
|
|
82
|
+
|
|
77
83
|
@OptIn(ExperimentalStdlibApi::class)
|
|
78
84
|
val md5: String get() {
|
|
79
85
|
validatePermission(Permission.READ)
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
package expo.modules.filesystem.next
|
|
2
|
+
|
|
3
|
+
import expo.modules.kotlin.sharedobjects.SharedRef
|
|
4
|
+
import java.io.RandomAccessFile
|
|
5
|
+
import java.nio.ByteBuffer
|
|
6
|
+
import java.nio.channels.FileChannel
|
|
7
|
+
class FileSystemFileHandle(file: FileSystemFile) : SharedRef<FileChannel>(RandomAccessFile(file.file, "rw").channel), AutoCloseable {
|
|
8
|
+
private val fileChannel: FileChannel = ref
|
|
9
|
+
|
|
10
|
+
private fun ensureIsOpen() {
|
|
11
|
+
if (!fileChannel.isOpen) {
|
|
12
|
+
throw UnableToReadHandleException("file handle is closed")
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
override fun sharedObjectDidRelease() {
|
|
17
|
+
close()
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
override fun close() {
|
|
21
|
+
fileChannel.close()
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
fun read(length: Int): ByteArray {
|
|
25
|
+
ensureIsOpen()
|
|
26
|
+
try {
|
|
27
|
+
val buffer = ByteBuffer.allocate(length.coerceAtMost((fileChannel.size() - fileChannel.position()).toInt()))
|
|
28
|
+
fileChannel.read(buffer)
|
|
29
|
+
return buffer.array()
|
|
30
|
+
} catch (e: Exception) {
|
|
31
|
+
throw UnableToReadHandleException(e.message ?: "unknown error")
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
fun write(data: ByteArray) {
|
|
36
|
+
ensureIsOpen()
|
|
37
|
+
try {
|
|
38
|
+
val buffer = ByteBuffer.wrap(data)
|
|
39
|
+
fileChannel.write(buffer)
|
|
40
|
+
} catch (e: Exception) {
|
|
41
|
+
throw UnableToWriteHandleException(e.message ?: "unknown error")
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
var offset: Long?
|
|
46
|
+
get() {
|
|
47
|
+
return try {
|
|
48
|
+
fileChannel.position()
|
|
49
|
+
} catch (e: Exception) {
|
|
50
|
+
null
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
set(value) {
|
|
54
|
+
if (value == null) {
|
|
55
|
+
return
|
|
56
|
+
}
|
|
57
|
+
fileChannel.position(value)
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
val size: Long?
|
|
61
|
+
get() {
|
|
62
|
+
return try {
|
|
63
|
+
fileChannel.size()
|
|
64
|
+
} catch (e: Exception) {
|
|
65
|
+
null
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
@@ -29,6 +29,16 @@ internal class InvalidPermissionException(permission: Permission) :
|
|
|
29
29
|
"Missing '${permission.name}' permission for accessing the file."
|
|
30
30
|
)
|
|
31
31
|
|
|
32
|
+
internal class UnableToReadHandleException(reason: String) :
|
|
33
|
+
CodedException(
|
|
34
|
+
"Unable to read from a file handle: '$reason'"
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
internal class UnableToWriteHandleException(reason: String) :
|
|
38
|
+
CodedException(
|
|
39
|
+
"Unable to write to a file handle: '$reason'"
|
|
40
|
+
)
|
|
41
|
+
|
|
32
42
|
internal class DestinationAlreadyExistsException :
|
|
33
43
|
CodedException(
|
|
34
44
|
"Destination already exists"
|
|
@@ -102,6 +102,10 @@ class FileSystemNextModule : Module() {
|
|
|
102
102
|
file.base64()
|
|
103
103
|
}
|
|
104
104
|
|
|
105
|
+
Function("bytes") { file: FileSystemFile ->
|
|
106
|
+
file.bytes()
|
|
107
|
+
}
|
|
108
|
+
|
|
105
109
|
Property("exists") { file: FileSystemFile ->
|
|
106
110
|
file.exists
|
|
107
111
|
}
|
|
@@ -133,6 +137,33 @@ class FileSystemNextModule : Module() {
|
|
|
133
137
|
null
|
|
134
138
|
}
|
|
135
139
|
}
|
|
140
|
+
|
|
141
|
+
Function("open") { file: FileSystemFile ->
|
|
142
|
+
FileSystemFileHandle(file)
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
Class(FileSystemFileHandle::class) {
|
|
147
|
+
Constructor { file: FileSystemFile ->
|
|
148
|
+
FileSystemFileHandle(file)
|
|
149
|
+
}
|
|
150
|
+
Function("readBytes") { fileHandle: FileSystemFileHandle, bytes: Int ->
|
|
151
|
+
fileHandle.read(bytes)
|
|
152
|
+
}
|
|
153
|
+
Function("writeBytes") { fileHandle: FileSystemFileHandle, data: ByteArray ->
|
|
154
|
+
fileHandle.write(data)
|
|
155
|
+
}
|
|
156
|
+
Function("close") { fileHandle: FileSystemFileHandle ->
|
|
157
|
+
fileHandle.close()
|
|
158
|
+
}
|
|
159
|
+
Property("offset") { fileHandle: FileSystemFileHandle ->
|
|
160
|
+
fileHandle.offset
|
|
161
|
+
}.set { fileHandle: FileSystemFileHandle, offset: Long ->
|
|
162
|
+
fileHandle.offset = offset
|
|
163
|
+
}
|
|
164
|
+
Property("size") { fileHandle: FileSystemFileHandle ->
|
|
165
|
+
fileHandle.size
|
|
166
|
+
}
|
|
136
167
|
}
|
|
137
168
|
|
|
138
169
|
Class(FileSystemDirectory::class) {
|
|
@@ -11,7 +11,7 @@ import kotlin.io.path.moveTo
|
|
|
11
11
|
// The Path class might be better, but `java.nio.file.Path` class is not available in API 23.
|
|
12
12
|
// The URL, URI classes seem like a less suitable choice.
|
|
13
13
|
// https://stackoverflow.com/questions/27845223/whats-the-difference-between-a-resource-uri-url-path-and-file-in-java
|
|
14
|
-
abstract class FileSystemPath(var file: File) : SharedObject() {
|
|
14
|
+
abstract class FileSystemPath(public var file: File) : SharedObject() {
|
|
15
15
|
fun delete() {
|
|
16
16
|
if (!file.exists()) {
|
|
17
17
|
throw UnableToDeleteException("path does not exist")
|
|
@@ -85,11 +85,16 @@ export declare class File {
|
|
|
85
85
|
* @returns The contents of the file as a base64 string.
|
|
86
86
|
*/
|
|
87
87
|
base64(): string;
|
|
88
|
+
/**
|
|
89
|
+
* Retrieves byte content of the entire file.
|
|
90
|
+
* @returns The contents of the file as a Uint8Array.
|
|
91
|
+
*/
|
|
92
|
+
bytes(): Uint8Array;
|
|
88
93
|
/**
|
|
89
94
|
* Writes content to the file.
|
|
90
95
|
* @param content - The content to write into the file.
|
|
91
96
|
*/
|
|
92
|
-
write(content: string): void;
|
|
97
|
+
write(content: string | Uint8Array): void;
|
|
93
98
|
/**
|
|
94
99
|
* Deletes a file.
|
|
95
100
|
*
|
|
@@ -115,6 +120,11 @@ export declare class File {
|
|
|
115
120
|
* Moves a directory. Updates the `uri` property that now points to the new location.
|
|
116
121
|
*/
|
|
117
122
|
move(destination: Directory | File): any;
|
|
123
|
+
/**
|
|
124
|
+
* Returns a FileHandle object that can be used to read and write data to the file.
|
|
125
|
+
* @throws Error if the file does not exist or cannot be opened.
|
|
126
|
+
*/
|
|
127
|
+
open(): FileHandle;
|
|
118
128
|
/**
|
|
119
129
|
* A static method that downloads a file from the network.
|
|
120
130
|
* @param url - The URL of the file to download.
|
|
@@ -135,4 +145,11 @@ export declare class File {
|
|
|
135
145
|
*/
|
|
136
146
|
md5: string | null;
|
|
137
147
|
}
|
|
148
|
+
export declare class FileHandle {
|
|
149
|
+
close(): void;
|
|
150
|
+
readBytes(length: number): Uint8Array;
|
|
151
|
+
writeBytes(bytes: Uint8Array): void;
|
|
152
|
+
offset: number | null;
|
|
153
|
+
size: number | null;
|
|
154
|
+
}
|
|
138
155
|
//# sourceMappingURL=ExpoFileSystem.types.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ExpoFileSystem.types.d.ts","sourceRoot":"","sources":["../../src/next/ExpoFileSystem.types.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,OAAO,OAAO,SAAS;IAC5B;;;;;;;OAOG;gBACS,GAAG,IAAI,EAAE,CAAC,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC,EAAE;IAElD;;OAEG;IACH,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IAErB;;;OAGG;IACH,YAAY,IAAI,IAAI;IAEpB;;;;OAIG;IACH,MAAM,IAAI,IAAI;IAEd;;;OAGG;IACH,MAAM,EAAE,OAAO,CAAC;IAEhB;;;;OAIG;IACH,MAAM,IAAI,IAAI;IAEd;;OAEG;IACH,IAAI,CAAC,WAAW,EAAE,SAAS,GAAG,IAAI;IAElC;;OAEG;IACH,IAAI,CAAC,WAAW,EAAE,SAAS,GAAG,IAAI;IAElC;;;;OAIG;IACH,aAAa,IAAI;QAAE,WAAW,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,EAAE;IAExD;;OAEG;IACH,IAAI,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,EAAE;CAC7B;AAED;;GAEG;AACH,MAAM,CAAC,OAAO,OAAO,IAAI;IACvB;;;;OAIG;gBACS,GAAG,IAAI,EAAE,CAAC,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC,EAAE;IAElD;;OAEG;IACH,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IAErB;;;OAGG;IACH,YAAY,IAAI,IAAI;IAEpB;;;OAGG;IACH,IAAI,IAAI,MAAM;IAEd;;;OAGG;IACH,MAAM,IAAI,MAAM;IAEhB;;;OAGG;IACH,KAAK,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;
|
|
1
|
+
{"version":3,"file":"ExpoFileSystem.types.d.ts","sourceRoot":"","sources":["../../src/next/ExpoFileSystem.types.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,OAAO,OAAO,SAAS;IAC5B;;;;;;;OAOG;gBACS,GAAG,IAAI,EAAE,CAAC,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC,EAAE;IAElD;;OAEG;IACH,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IAErB;;;OAGG;IACH,YAAY,IAAI,IAAI;IAEpB;;;;OAIG;IACH,MAAM,IAAI,IAAI;IAEd;;;OAGG;IACH,MAAM,EAAE,OAAO,CAAC;IAEhB;;;;OAIG;IACH,MAAM,IAAI,IAAI;IAEd;;OAEG;IACH,IAAI,CAAC,WAAW,EAAE,SAAS,GAAG,IAAI;IAElC;;OAEG;IACH,IAAI,CAAC,WAAW,EAAE,SAAS,GAAG,IAAI;IAElC;;;;OAIG;IACH,aAAa,IAAI;QAAE,WAAW,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,EAAE;IAExD;;OAEG;IACH,IAAI,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,EAAE;CAC7B;AAED;;GAEG;AACH,MAAM,CAAC,OAAO,OAAO,IAAI;IACvB;;;;OAIG;gBACS,GAAG,IAAI,EAAE,CAAC,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC,EAAE;IAElD;;OAEG;IACH,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IAErB;;;OAGG;IACH,YAAY,IAAI,IAAI;IAEpB;;;OAGG;IACH,IAAI,IAAI,MAAM;IAEd;;;OAGG;IACH,MAAM,IAAI,MAAM;IAEhB;;;OAGG;IACH,KAAK,IAAI,UAAU;IAEnB;;;OAGG;IACH,KAAK,CAAC,OAAO,EAAE,MAAM,GAAG,UAAU,GAAG,IAAI;IAEzC;;;;OAIG;IACH,MAAM,IAAI,IAAI;IAEd;;;OAGG;IACH,MAAM,EAAE,OAAO,CAAC;IAEhB;;;;OAIG;IACH,MAAM,IAAI,IAAI;IAEd;;OAEG;IACH,IAAI,CAAC,WAAW,EAAE,SAAS,GAAG,IAAI;IAElC;;OAEG;IACH,IAAI,CAAC,WAAW,EAAE,SAAS,GAAG,IAAI;IAElC;;;OAGG;IACH,IAAI,IAAI,UAAU;IAElB;;;;;;;;;OASG;IACH,MAAM,CAAC,iBAAiB,CAAC,GAAG,EAAE,MAAM,EAAE,WAAW,EAAE,SAAS,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;IAEnF;;OAEG;IACH,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAEpB;;OAEG;IACH,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;CACpB;AAED,MAAM,CAAC,OAAO,OAAO,UAAU;IAI7B,KAAK,IAAI,IAAI;IAKb,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,UAAU;IAKrC,UAAU,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI;IAMnC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IAItB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;CACrB"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { ReadableStream, WritableStream } from 'web-streams-polyfill';
|
|
1
2
|
import ExpoFileSystem from './ExpoFileSystem';
|
|
2
3
|
import { PathUtilities } from './pathUtilities';
|
|
3
4
|
export declare class Paths extends PathUtilities {
|
|
@@ -31,6 +32,8 @@ export declare class File extends ExpoFileSystem.FileSystemFile {
|
|
|
31
32
|
* File name. Includes the extension.
|
|
32
33
|
*/
|
|
33
34
|
get name(): string;
|
|
35
|
+
readableStream(): ReadableStream<Uint8Array>;
|
|
36
|
+
writableStream(): WritableStream<Uint8Array>;
|
|
34
37
|
}
|
|
35
38
|
/**
|
|
36
39
|
* Represents a directory on the filesystem.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FileSystem.d.ts","sourceRoot":"","sources":["../../src/next/FileSystem.ts"],"names":[],"mappings":"AAAA,OAAO,cAAc,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"FileSystem.d.ts","sourceRoot":"","sources":["../../src/next/FileSystem.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAEtE,OAAO,cAAc,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAGhD,qBAAa,KAAM,SAAQ,aAAa;IACtC;;OAEG;IACH,MAAM,KAAK,KAAK,cAEf;IAED;;OAEG;IACH,MAAM,KAAK,QAAQ,cAElB;IACD,MAAM,KAAK,qBAAqB,8BAO/B;CACF;AAED,qBAAa,IAAK,SAAQ,cAAc,CAAC,cAAc;IACrD;;;;;;;OAOG;gBACS,GAAG,IAAI,EAAE,CAAC,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC,EAAE;IAQlD,IAAI,eAAe,cAElB;IAED;;;OAGG;IACH,IAAI,SAAS,WAEZ;IAED;;OAEG;IACH,IAAI,IAAI,WAEP;IAED,cAAc;IAId,cAAc;CAGf;AAQD;;;;GAIG;AACH,qBAAa,SAAU,SAAQ,cAAc,CAAC,mBAAmB;IAC/D;;;;;;;OAOG;gBACS,GAAG,IAAI,EAAE,CAAC,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC,EAAE;IAQlD,IAAI,eAAe,cAElB;IAED;;;;OAIG;IACH,IAAI,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,EAAE;IAO5B;;OAEG;IACH,IAAI,IAAI,WAEP;CACF"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { FileHandle } from './ExpoFileSystem.types';
|
|
2
|
+
export declare class FileSystemReadableStreamSource implements UnderlyingByteSource {
|
|
3
|
+
handle: FileHandle;
|
|
4
|
+
size: number;
|
|
5
|
+
type: "bytes";
|
|
6
|
+
constructor(handle: any);
|
|
7
|
+
cancel(): void;
|
|
8
|
+
pull(controller: ReadableByteStreamController): void;
|
|
9
|
+
}
|
|
10
|
+
export declare class FileSystemWritableSink implements UnderlyingSink {
|
|
11
|
+
handle: FileHandle;
|
|
12
|
+
constructor(handle: any);
|
|
13
|
+
abort(): void;
|
|
14
|
+
close(): void;
|
|
15
|
+
write(chunk: Uint8Array): void;
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=streams.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"streams.d.ts","sourceRoot":"","sources":["../../src/next/streams.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAEzD,qBAAa,8BAA+B,YAAW,oBAAoB;IACzE,MAAM,EAAE,UAAU,CAAC;IACnB,IAAI,EAAE,MAAM,CAAQ;IACpB,IAAI,UAAoB;gBAEZ,MAAM,KAAA;IAIlB,MAAM;IAIN,IAAI,CAAC,UAAU,EAAE,4BAA4B;CA4B9C;AAED,qBAAa,sBAAuB,YAAW,cAAc;IAC3D,MAAM,EAAE,UAAU,CAAC;gBAEP,MAAM,KAAA;IAIlB,KAAK;IAIL,KAAK;IAIL,KAAK,CAAC,KAAK,EAAE,UAAU;CAGxB"}
|
|
@@ -73,8 +73,11 @@ internal final class FileSystemFile: FileSystemPath {
|
|
|
73
73
|
try content.write(to: url, atomically: false, encoding: .utf8) // TODO: better error handling
|
|
74
74
|
}
|
|
75
75
|
|
|
76
|
-
// TODO:
|
|
76
|
+
// TODO: blob support
|
|
77
77
|
func write(_ content: TypedArray) throws {
|
|
78
|
+
try validateType()
|
|
79
|
+
try validatePermission(.write)
|
|
80
|
+
try Data(bytes: content.rawPointer, count: content.byteLength).write(to: url)
|
|
78
81
|
}
|
|
79
82
|
|
|
80
83
|
func text() throws -> String {
|
|
@@ -83,6 +86,12 @@ internal final class FileSystemFile: FileSystemPath {
|
|
|
83
86
|
return try String(contentsOf: url)
|
|
84
87
|
}
|
|
85
88
|
|
|
89
|
+
func bytes() throws -> Data {
|
|
90
|
+
try validateType()
|
|
91
|
+
try validatePermission(.read)
|
|
92
|
+
return try Data(contentsOf: url)
|
|
93
|
+
}
|
|
94
|
+
|
|
86
95
|
func base64() throws -> String {
|
|
87
96
|
try validatePermission(.read)
|
|
88
97
|
return try Data(contentsOf: url).base64EncodedString()
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import Foundation
|
|
2
|
+
import ExpoModulesCore
|
|
3
|
+
|
|
4
|
+
internal final class FileSystemFileHandle: SharedRef<FileHandle> {
|
|
5
|
+
let file: FileSystemFile
|
|
6
|
+
let handle: FileHandle
|
|
7
|
+
|
|
8
|
+
init(file: FileSystemFile) throws {
|
|
9
|
+
self.file = file
|
|
10
|
+
handle = try FileHandle(forUpdating: file.url)
|
|
11
|
+
super.init(handle)
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
func read(_ length: Int) throws -> Data {
|
|
15
|
+
do {
|
|
16
|
+
let data = try handle.read(upToCount: length)
|
|
17
|
+
return data ?? Data()
|
|
18
|
+
} catch {
|
|
19
|
+
throw UnableToReadHandleException(error.localizedDescription)
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
func write(_ bytes: Data) throws {
|
|
24
|
+
try handle.write(contentsOf: bytes)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
func close() throws {
|
|
28
|
+
try handle.close()
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
var offset: UInt64? {
|
|
32
|
+
get {
|
|
33
|
+
try? handle.offset()
|
|
34
|
+
}
|
|
35
|
+
set(newOffset) {
|
|
36
|
+
guard let newOffset else {
|
|
37
|
+
return
|
|
38
|
+
}
|
|
39
|
+
handle.seek(toFileOffset: newOffset)
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
var size: UInt64? {
|
|
44
|
+
do {
|
|
45
|
+
let offset = try handle.offset()
|
|
46
|
+
let size = try handle.seekToEnd()
|
|
47
|
+
handle.seek(toFileOffset: offset)
|
|
48
|
+
return size
|
|
49
|
+
} catch {
|
|
50
|
+
return nil
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
@@ -49,6 +49,12 @@ internal class UnableToCreateFileException: GenericException<String> {
|
|
|
49
49
|
}
|
|
50
50
|
}
|
|
51
51
|
|
|
52
|
+
internal class UnableToReadHandleException: GenericException<String> {
|
|
53
|
+
override var reason: String {
|
|
54
|
+
"Unable to read from a file handle: \(param)"
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
52
58
|
internal class DestinationAlreadyExistsException: Exception {
|
|
53
59
|
override var reason: String {
|
|
54
60
|
"Destination already exists"
|
|
@@ -72,6 +72,14 @@ public final class FileSystemNextModule: Module {
|
|
|
72
72
|
return try file.base64()
|
|
73
73
|
}
|
|
74
74
|
|
|
75
|
+
Function("bytes") { file in
|
|
76
|
+
return try file.bytes()
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
Function("open") { file in
|
|
80
|
+
return try FileSystemFileHandle(file: file)
|
|
81
|
+
}
|
|
82
|
+
|
|
75
83
|
Function("write") { (file, content: Either<String, TypedArray>) in
|
|
76
84
|
if let content: String = content.get() {
|
|
77
85
|
try file.write(content)
|
|
@@ -114,6 +122,30 @@ public final class FileSystemNextModule: Module {
|
|
|
114
122
|
}
|
|
115
123
|
}
|
|
116
124
|
|
|
125
|
+
Class(FileSystemFileHandle.self) {
|
|
126
|
+
Function("readBytes") { (fileHandle, bytes: Int) in
|
|
127
|
+
try fileHandle.read(bytes)
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
Function("writeBytes") { (fileHandle, bytes: Data) in
|
|
131
|
+
try fileHandle.write(bytes)
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
Function("close") { fileHandle in
|
|
135
|
+
try fileHandle.close()
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
Property("offset") { fileHandle in
|
|
139
|
+
fileHandle.offset
|
|
140
|
+
}.set { (fileHandle, volume: UInt64) in
|
|
141
|
+
fileHandle.offset = volume
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
Property("size") { fileHandle in
|
|
145
|
+
fileHandle.size
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
117
149
|
Class(FileSystemDirectory.self) {
|
|
118
150
|
Constructor { (url: URL) in
|
|
119
151
|
return FileSystemDirectory(url: url.standardizedFileURL)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "expo-file-system",
|
|
3
|
-
"version": "18.0.
|
|
3
|
+
"version": "18.0.4",
|
|
4
4
|
"description": "Provides access to the local file system on the device.",
|
|
5
5
|
"main": "src/index.ts",
|
|
6
6
|
"types": "build/index.d.ts",
|
|
@@ -42,5 +42,8 @@
|
|
|
42
42
|
"expo": "*",
|
|
43
43
|
"react-native": "*"
|
|
44
44
|
},
|
|
45
|
-
"
|
|
45
|
+
"dependencies": {
|
|
46
|
+
"web-streams-polyfill": "^3.3.2"
|
|
47
|
+
},
|
|
48
|
+
"gitHead": "128718d43bac2eaed764b3551469b95400f2363e"
|
|
46
49
|
}
|
|
@@ -97,11 +97,17 @@ export declare class File {
|
|
|
97
97
|
*/
|
|
98
98
|
base64(): string;
|
|
99
99
|
|
|
100
|
+
/**
|
|
101
|
+
* Retrieves byte content of the entire file.
|
|
102
|
+
* @returns The contents of the file as a Uint8Array.
|
|
103
|
+
*/
|
|
104
|
+
bytes(): Uint8Array;
|
|
105
|
+
|
|
100
106
|
/**
|
|
101
107
|
* Writes content to the file.
|
|
102
108
|
* @param content - The content to write into the file.
|
|
103
109
|
*/
|
|
104
|
-
write(content: string): void;
|
|
110
|
+
write(content: string | Uint8Array): void;
|
|
105
111
|
|
|
106
112
|
/**
|
|
107
113
|
* Deletes a file.
|
|
@@ -133,6 +139,12 @@ export declare class File {
|
|
|
133
139
|
*/
|
|
134
140
|
move(destination: Directory | File);
|
|
135
141
|
|
|
142
|
+
/**
|
|
143
|
+
* Returns a FileHandle object that can be used to read and write data to the file.
|
|
144
|
+
* @throws Error if the file does not exist or cannot be opened.
|
|
145
|
+
*/
|
|
146
|
+
open(): FileHandle;
|
|
147
|
+
|
|
136
148
|
/**
|
|
137
149
|
* A static method that downloads a file from the network.
|
|
138
150
|
* @param url - The URL of the file to download.
|
|
@@ -155,3 +167,30 @@ export declare class File {
|
|
|
155
167
|
*/
|
|
156
168
|
md5: string | null;
|
|
157
169
|
}
|
|
170
|
+
|
|
171
|
+
export declare class FileHandle {
|
|
172
|
+
/*
|
|
173
|
+
* Closes the file handle. This allows the file to be deleted, moved or read by a different process. Subsequent calls to `readBytes` or `writeBytes` will throw an error.
|
|
174
|
+
*/
|
|
175
|
+
close(): void;
|
|
176
|
+
/*
|
|
177
|
+
* Reads the specified amount of bytes from the file at the current offset.
|
|
178
|
+
* @param length - The number of bytes to read.
|
|
179
|
+
*/
|
|
180
|
+
readBytes(length: number): Uint8Array;
|
|
181
|
+
/*
|
|
182
|
+
* Writes the specified bytes to the file at the current offset.
|
|
183
|
+
* @param bytes - A Uint8Array array containing bytes to write.
|
|
184
|
+
*/
|
|
185
|
+
writeBytes(bytes: Uint8Array): void;
|
|
186
|
+
/*
|
|
187
|
+
* A property that indicates the current byte offset in the file. Calling `readBytes` or `writeBytes` will read or write a specified amount of bytes starting from this offset. The offset is incremented by the number of bytes read or written.
|
|
188
|
+
* The offset can be set to any value within the file size. If the offset is set to a value greater than the file size, the next write operation will append data to the end of the file.
|
|
189
|
+
* Null if the file handle is closed.
|
|
190
|
+
*/
|
|
191
|
+
offset: number | null;
|
|
192
|
+
/*
|
|
193
|
+
* A size of the file in bytes or `null` if the file handle is closed.
|
|
194
|
+
*/
|
|
195
|
+
size: number | null;
|
|
196
|
+
}
|
package/src/next/FileSystem.ts
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
|
+
import { ReadableStream, WritableStream } from 'web-streams-polyfill';
|
|
2
|
+
|
|
1
3
|
import ExpoFileSystem from './ExpoFileSystem';
|
|
2
4
|
import { PathUtilities } from './pathUtilities';
|
|
5
|
+
import { FileSystemReadableStreamSource, FileSystemWritableSink } from './streams';
|
|
3
6
|
|
|
4
7
|
export class Paths extends PathUtilities {
|
|
5
8
|
/**
|
|
@@ -60,6 +63,14 @@ export class File extends ExpoFileSystem.FileSystemFile {
|
|
|
60
63
|
get name() {
|
|
61
64
|
return Paths.basename(this.uri);
|
|
62
65
|
}
|
|
66
|
+
|
|
67
|
+
readableStream() {
|
|
68
|
+
return new ReadableStream<Uint8Array>(new FileSystemReadableStreamSource(super.open()));
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
writableStream() {
|
|
72
|
+
return new WritableStream<Uint8Array>(new FileSystemWritableSink(super.open()));
|
|
73
|
+
}
|
|
63
74
|
}
|
|
64
75
|
|
|
65
76
|
// Cannot use `static` keyword in class declaration because of a runtime error.
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import type { FileHandle } from './ExpoFileSystem.types';
|
|
2
|
+
|
|
3
|
+
export class FileSystemReadableStreamSource implements UnderlyingByteSource {
|
|
4
|
+
handle: FileHandle;
|
|
5
|
+
size: number = 1024;
|
|
6
|
+
type = 'bytes' as const;
|
|
7
|
+
|
|
8
|
+
constructor(handle) {
|
|
9
|
+
this.handle = handle;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
cancel() {
|
|
13
|
+
this.handle.close();
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
pull(controller: ReadableByteStreamController) {
|
|
17
|
+
const theView = controller.byobRequest?.view;
|
|
18
|
+
if (!theView) {
|
|
19
|
+
const bytes = this.handle.readBytes(this.size);
|
|
20
|
+
if (bytes.length === 0) {
|
|
21
|
+
controller.close();
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
controller.enqueue(bytes);
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// TODO: Optimize by adding a native method that can write into a TypedArray at a given offset.
|
|
29
|
+
const bytes = this.handle.readBytes(theView.byteLength - theView.byteOffset);
|
|
30
|
+
if (bytes.length === 0) {
|
|
31
|
+
controller.close();
|
|
32
|
+
controller.byobRequest.respond(0);
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
if (theView instanceof Uint8Array) {
|
|
36
|
+
theView.set(bytes, theView.byteOffset);
|
|
37
|
+
} else {
|
|
38
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
39
|
+
theView[i + theView.byteOffset] = bytes[i];
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
controller.byobRequest.respond(bytes.length);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export class FileSystemWritableSink implements UnderlyingSink {
|
|
47
|
+
handle: FileHandle;
|
|
48
|
+
|
|
49
|
+
constructor(handle) {
|
|
50
|
+
this.handle = handle;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
abort() {
|
|
54
|
+
this.close();
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
close() {
|
|
58
|
+
this.handle.close();
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
write(chunk: Uint8Array) {
|
|
62
|
+
this.handle.writeBytes(chunk);
|
|
63
|
+
}
|
|
64
|
+
}
|