@capacitor/filesystem 7.0.2-nightly-20250526T150552.0 → 7.1.0-dev.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/CapacitorFilesystem.podspec +4 -3
- package/Package.swift +10 -4
- package/README.md +149 -78
- package/android/build.gradle +12 -22
- package/android/src/main/kotlin/com/capacitorjs/plugins/filesystem/FilesystemErrors.kt +101 -0
- package/android/src/main/kotlin/com/capacitorjs/plugins/filesystem/FilesystemMethodOptions.kt +129 -0
- package/android/src/main/kotlin/com/capacitorjs/plugins/filesystem/FilesystemMethodResults.kt +65 -0
- package/android/src/main/kotlin/com/capacitorjs/plugins/filesystem/FilesystemPlugin.kt +412 -0
- package/android/src/main/kotlin/com/capacitorjs/plugins/filesystem/LegacyFilesystemImplementation.kt +169 -0
- package/android/src/main/kotlin/com/capacitorjs/plugins/filesystem/PluginResultExtensions.kt +25 -0
- package/dist/docs.json +227 -145
- package/dist/esm/definitions.d.ts +102 -64
- package/dist/esm/definitions.js +25 -3
- package/dist/esm/definitions.js.map +1 -1
- package/dist/esm/index.js +3 -1
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/web.d.ts +3 -1
- package/dist/esm/web.js +10 -10
- package/dist/esm/web.js.map +1 -1
- package/dist/plugin.cjs.js +40 -14
- package/dist/plugin.cjs.js.map +1 -1
- package/dist/plugin.js +41 -16
- package/dist/plugin.js.map +1 -1
- package/ios/Sources/FilesystemPlugin/CAPPluginCall+Accelerators.swift +73 -0
- package/ios/Sources/FilesystemPlugin/FilesystemConstants.swift +61 -0
- package/ios/Sources/FilesystemPlugin/FilesystemError.swift +57 -0
- package/ios/Sources/FilesystemPlugin/FilesystemLocationResolver.swift +39 -0
- package/ios/Sources/FilesystemPlugin/FilesystemOperation.swift +24 -0
- package/ios/Sources/FilesystemPlugin/FilesystemOperationExecutor.swift +116 -0
- package/ios/Sources/FilesystemPlugin/FilesystemPlugin.swift +103 -264
- package/ios/Sources/FilesystemPlugin/IONFileStructures+Converters.swift +60 -0
- package/ios/Sources/FilesystemPlugin/{Filesystem.swift → LegacyFilesystemImplementation.swift} +18 -179
- package/package.json +28 -24
- package/LICENSE +0 -23
- package/android/src/main/java/com/capacitorjs/plugins/filesystem/Filesystem.java +0 -414
- package/android/src/main/java/com/capacitorjs/plugins/filesystem/FilesystemPlugin.java +0 -551
- package/android/src/main/java/com/capacitorjs/plugins/filesystem/exceptions/CopyFailedException.java +0 -16
- package/android/src/main/java/com/capacitorjs/plugins/filesystem/exceptions/DirectoryExistsException.java +0 -16
- package/android/src/main/java/com/capacitorjs/plugins/filesystem/exceptions/DirectoryNotFoundException.java +0 -16
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import Capacitor
|
|
2
|
+
import Foundation
|
|
3
|
+
import IONFilesystemLib
|
|
4
|
+
import Combine
|
|
5
|
+
|
|
6
|
+
class FilesystemOperationExecutor {
|
|
7
|
+
let service: FileService
|
|
8
|
+
private var cancellables = Set<AnyCancellable>()
|
|
9
|
+
|
|
10
|
+
init(service: FileService) {
|
|
11
|
+
self.service = service
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
func execute(_ operation: FilesystemOperation, _ call: CAPPluginCall) {
|
|
15
|
+
do {
|
|
16
|
+
var resultData: PluginCallResultData?
|
|
17
|
+
|
|
18
|
+
switch operation {
|
|
19
|
+
case .readFile(let url, let encoding):
|
|
20
|
+
let data = try service.readEntireFile(atURL: url, withEncoding: encoding).textValue
|
|
21
|
+
resultData = [Constants.ResultDataKey.data: data]
|
|
22
|
+
case .readFileInChunks(let url, let encoding, let chunkSize):
|
|
23
|
+
try processFileInChunks(at: url, withEncoding: encoding, chunkSize: chunkSize, for: operation, call)
|
|
24
|
+
return
|
|
25
|
+
case .write(let url, let encodingMapper, let recursive):
|
|
26
|
+
try service.saveFile(atURL: url, withEncodingAndData: encodingMapper, includeIntermediateDirectories: recursive)
|
|
27
|
+
resultData = [Constants.ResultDataKey.uri: url.absoluteString]
|
|
28
|
+
case .append(let url, let encodingMapper, let recursive):
|
|
29
|
+
try service.appendData(encodingMapper, atURL: url, includeIntermediateDirectories: recursive)
|
|
30
|
+
case .delete(let url):
|
|
31
|
+
try service.deleteFile(atURL: url)
|
|
32
|
+
case .mkdir(let url, let recursive):
|
|
33
|
+
try service.createDirectory(atURL: url, includeIntermediateDirectories: recursive)
|
|
34
|
+
case .rmdir(let url, let recursive):
|
|
35
|
+
try service.removeDirectory(atURL: url, includeIntermediateDirectories: recursive)
|
|
36
|
+
case .readdir(let url):
|
|
37
|
+
let directoryAttributes = try service.listDirectory(atURL: url)
|
|
38
|
+
.map { try fetchItemAttributesJSObject(using: service, atURL: $0) }
|
|
39
|
+
resultData = [Constants.ResultDataKey.files: directoryAttributes]
|
|
40
|
+
case .stat(let url):
|
|
41
|
+
resultData = try fetchItemAttributesJSObject(using: service, atURL: url)
|
|
42
|
+
case .getUri(let url):
|
|
43
|
+
resultData = [Constants.ResultDataKey.uri: url.absoluteString]
|
|
44
|
+
case .rename(let source, let destination):
|
|
45
|
+
try service.renameItem(fromURL: source, toURL: destination)
|
|
46
|
+
case .copy(let source, let destination):
|
|
47
|
+
try service.copyItem(fromURL: source, toURL: destination)
|
|
48
|
+
resultData = [Constants.ResultDataKey.uri: destination.absoluteString]
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
call.handleSuccess(resultData)
|
|
52
|
+
} catch {
|
|
53
|
+
call.handleError(mapError(error, for: operation))
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
private extension FilesystemOperationExecutor {
|
|
59
|
+
func processFileInChunks(at url: URL, withEncoding encoding: IONFILEEncoding, chunkSize: Int, for operation: FilesystemOperation, _ call: CAPPluginCall) throws {
|
|
60
|
+
let chunkSizeToUse = chunkSizeToUse(basedOn: chunkSize, and: encoding)
|
|
61
|
+
try service.readFileInChunks(atURL: url, withEncoding: encoding, andChunkSize: chunkSizeToUse)
|
|
62
|
+
.sink(receiveCompletion: { completion in
|
|
63
|
+
switch completion {
|
|
64
|
+
case .finished:
|
|
65
|
+
call.handleSuccess([Constants.ResultDataKey.data: Constants.ConfigurationValue.endOfFile])
|
|
66
|
+
case .failure(let error):
|
|
67
|
+
call.handleError(self.mapError(error, for: operation))
|
|
68
|
+
}
|
|
69
|
+
}, receiveValue: {
|
|
70
|
+
call.handleSuccess([Constants.ResultDataKey.data: $0.textValue], true)
|
|
71
|
+
})
|
|
72
|
+
.store(in: &cancellables)
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
private func chunkSizeToUse(basedOn chunkSize: Int, and encoding: IONFILEEncoding) -> Int {
|
|
76
|
+
// When dealing with byte buffers, we need chunk size that are multiples of 3
|
|
77
|
+
// We're treating byte buffers as base64 data, and size multiple of 3 makes it so that chunks can be concatenated
|
|
78
|
+
encoding == .byteBuffer ? chunkSize - chunkSize % 3 + 3 : chunkSize
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
func mapError(_ error: Error, for operation: FilesystemOperation) -> FilesystemError {
|
|
82
|
+
var path = ""
|
|
83
|
+
var method: IONFileMethod = IONFileMethod.getUri
|
|
84
|
+
switch operation {
|
|
85
|
+
case .readFile(let url, _): path = url.absoluteString; method = .readFile
|
|
86
|
+
case .readFileInChunks(let url, _, _): path = url.absoluteString; method = .readFileInChunks
|
|
87
|
+
case .write(let url, _, _): path = url.absoluteString; method = .writeFile
|
|
88
|
+
case .append(let url, _, _): path = url.absoluteString; method = .appendFile
|
|
89
|
+
case .delete(let url): path = url.absoluteString; method = .deleteFile
|
|
90
|
+
case .mkdir(let url, _): path = url.absoluteString; method = .mkdir
|
|
91
|
+
case .rmdir(let url, _): path = url.absoluteString; method = .rmdir
|
|
92
|
+
case .readdir(let url): path = url.absoluteString; method = .readdir
|
|
93
|
+
case .stat(let url): path = url.absoluteString; method = .stat
|
|
94
|
+
case .getUri(let url): return FilesystemError.invalidPath(url.absoluteString)
|
|
95
|
+
case .rename(let sourceUrl, _): path = sourceUrl.absoluteString; method = .rename
|
|
96
|
+
case .copy(let sourceUrl, _): path = sourceUrl.absoluteString; method = .copy
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return mapError(error, withPath: path, andMethod: method)
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
private func mapError(_ error: Error, withPath path: String, andMethod method: IONFileMethod) -> FilesystemError {
|
|
103
|
+
return switch error {
|
|
104
|
+
case IONFILEDirectoryManagerError.notEmpty: .cannotDeleteChildren
|
|
105
|
+
case IONFILEDirectoryManagerError.alreadyExists: .directoryAlreadyExists(path)
|
|
106
|
+
case IONFILEFileManagerError.missingParentFolder: .parentDirectoryMissing
|
|
107
|
+
case IONFILEFileManagerError.fileNotFound: .fileNotFound(method: method, path)
|
|
108
|
+
default: .operationFailed(method: method, error)
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
func fetchItemAttributesJSObject(using service: FileService, atURL url: URL) throws -> IONFILEItemAttributeModel.JSResult {
|
|
113
|
+
let attributes = try service.getItemAttributes(atURL: url)
|
|
114
|
+
return attributes.toJSResult(with: url)
|
|
115
|
+
}
|
|
116
|
+
}
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import Foundation
|
|
2
2
|
import Capacitor
|
|
3
|
+
import IONFilesystemLib
|
|
4
|
+
|
|
5
|
+
typealias FileService = any IONFILEDirectoryManager & IONFILEFileManager
|
|
3
6
|
|
|
4
7
|
/**
|
|
5
8
|
* Please read the Capacitor iOS Plugin Development Guide
|
|
@@ -11,6 +14,7 @@ public class FilesystemPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
11
14
|
public let jsName = "Filesystem"
|
|
12
15
|
public let pluginMethods: [CAPPluginMethod] = [
|
|
13
16
|
CAPPluginMethod(name: "readFile", returnType: CAPPluginReturnPromise),
|
|
17
|
+
CAPPluginMethod(name: "readFileInChunks", returnType: CAPPluginReturnCallback),
|
|
14
18
|
CAPPluginMethod(name: "writeFile", returnType: CAPPluginReturnPromise),
|
|
15
19
|
CAPPluginMethod(name: "appendFile", returnType: CAPPluginReturnPromise),
|
|
16
20
|
CAPPluginMethod(name: "deleteFile", returnType: CAPPluginReturnPromise),
|
|
@@ -25,31 +29,48 @@ public class FilesystemPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
25
29
|
CAPPluginMethod(name: "requestPermissions", returnType: CAPPluginReturnPromise),
|
|
26
30
|
CAPPluginMethod(name: "downloadFile", returnType: CAPPluginReturnPromise)
|
|
27
31
|
]
|
|
28
|
-
private let implementation = Filesystem()
|
|
29
32
|
|
|
33
|
+
private let legacyImplementation = LegacyFilesystemImplementation()
|
|
34
|
+
|
|
35
|
+
private var fileService: FileService?
|
|
36
|
+
|
|
37
|
+
override public func load() {
|
|
38
|
+
self.fileService = IONFILEManager()
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
func getService() -> Result<FileService, FilesystemError> {
|
|
42
|
+
if fileService == nil { load() }
|
|
43
|
+
return fileService.map(Result.success) ?? .failure(.bridgeNotInitialised)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
@objc override public func checkPermissions(_ call: CAPPluginCall) {
|
|
47
|
+
call.handlePermissionSuccess()
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
@objc override public func requestPermissions(_ call: CAPPluginCall) {
|
|
51
|
+
call.handlePermissionSuccess()
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// MARK: - Public API Methods
|
|
56
|
+
private extension FilesystemPlugin {
|
|
30
57
|
/**
|
|
31
58
|
* Read a file from the filesystem.
|
|
32
59
|
*/
|
|
33
60
|
@objc func readFile(_ call: CAPPluginCall) {
|
|
34
|
-
let encoding = call.
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
handleError(call, "path must be provided and must be a string.")
|
|
38
|
-
return
|
|
61
|
+
let encoding = call.getEncoding(Constants.MethodParameter.encoding)
|
|
62
|
+
performSinglePathOperation(call) {
|
|
63
|
+
.readFile(url: $0, encoding: encoding)
|
|
39
64
|
}
|
|
40
|
-
|
|
65
|
+
}
|
|
41
66
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
67
|
+
@objc func readFileInChunks(_ call: CAPPluginCall) {
|
|
68
|
+
let encoding = call.getEncoding(Constants.MethodParameter.encoding)
|
|
69
|
+
guard let chunkSize = call.getInt(Constants.MethodParameter.chunkSize) else {
|
|
70
|
+
return call.handleError(.invalidInput(method: call.getIONFileMethod()))
|
|
45
71
|
}
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
call.resolve([
|
|
49
|
-
"data": data
|
|
50
|
-
])
|
|
51
|
-
} catch let error as NSError {
|
|
52
|
-
handleError(call, error.localizedDescription, error)
|
|
72
|
+
performSinglePathOperation(call) {
|
|
73
|
+
.readFileInChunks(url: $0, encoding: encoding, chunkSize: chunkSize)
|
|
53
74
|
}
|
|
54
75
|
}
|
|
55
76
|
|
|
@@ -57,33 +78,13 @@ public class FilesystemPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
57
78
|
* Write a file to the filesystem.
|
|
58
79
|
*/
|
|
59
80
|
@objc func writeFile(_ call: CAPPluginCall) {
|
|
60
|
-
let
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
guard let file = call.getString("path") else {
|
|
64
|
-
handleError(call, "path must be provided and must be a string.")
|
|
65
|
-
return
|
|
81
|
+
guard let encodingMapper = call.getEncodingMapper() else {
|
|
82
|
+
return call.handleError(.invalidInput(method: call.getIONFileMethod()))
|
|
66
83
|
}
|
|
84
|
+
let recursive = call.getBool(Constants.MethodParameter.recursive, false)
|
|
67
85
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
return
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
let directory = call.getString("directory")
|
|
74
|
-
|
|
75
|
-
guard let fileUrl = implementation.getFileUrl(at: file, in: directory) else {
|
|
76
|
-
handleError(call, "Invalid path")
|
|
77
|
-
return
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
do {
|
|
81
|
-
let path = try implementation.writeFile(at: fileUrl, with: data, recursive: recursive, with: encoding)
|
|
82
|
-
call.resolve([
|
|
83
|
-
"uri": path
|
|
84
|
-
])
|
|
85
|
-
} catch let error as NSError {
|
|
86
|
-
handleError(call, error.localizedDescription, error)
|
|
86
|
+
performSinglePathOperation(call) {
|
|
87
|
+
.write(url: $0, encodingMapper: encodingMapper, recursive: recursive)
|
|
87
88
|
}
|
|
88
89
|
}
|
|
89
90
|
|
|
@@ -91,29 +92,13 @@ public class FilesystemPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
91
92
|
* Append to a file.
|
|
92
93
|
*/
|
|
93
94
|
@objc func appendFile(_ call: CAPPluginCall) {
|
|
94
|
-
let
|
|
95
|
-
|
|
96
|
-
guard let file = call.getString("path") else {
|
|
97
|
-
handleError(call, "path must be provided and must be a string.")
|
|
98
|
-
return
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
guard let data = call.getString("data") else {
|
|
102
|
-
handleError(call, "Data must be provided and must be a string.")
|
|
103
|
-
return
|
|
95
|
+
guard let encodingMapper = call.getEncodingMapper() else {
|
|
96
|
+
return call.handleError(.invalidInput(method: call.getIONFileMethod()))
|
|
104
97
|
}
|
|
98
|
+
let recursive = call.getBool(Constants.MethodParameter.recursive, false)
|
|
105
99
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
handleError(call, "Invalid path")
|
|
109
|
-
return
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
do {
|
|
113
|
-
try implementation.appendFile(at: fileUrl, with: data, recursive: false, with: encoding)
|
|
114
|
-
call.resolve()
|
|
115
|
-
} catch let error as NSError {
|
|
116
|
-
handleError(call, error.localizedDescription, error)
|
|
100
|
+
performSinglePathOperation(call) {
|
|
101
|
+
.append(url: $0, encodingMapper: encodingMapper, recursive: recursive)
|
|
117
102
|
}
|
|
118
103
|
}
|
|
119
104
|
|
|
@@ -121,22 +106,8 @@ public class FilesystemPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
121
106
|
* Delete a file.
|
|
122
107
|
*/
|
|
123
108
|
@objc func deleteFile(_ call: CAPPluginCall) {
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
return
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
let directory = call.getString("directory")
|
|
130
|
-
guard let fileUrl = implementation.getFileUrl(at: file, in: directory) else {
|
|
131
|
-
handleError(call, "Invalid path")
|
|
132
|
-
return
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
do {
|
|
136
|
-
try implementation.deleteFile(at: fileUrl)
|
|
137
|
-
call.resolve()
|
|
138
|
-
} catch let error as NSError {
|
|
139
|
-
handleError(call, error.localizedDescription, error)
|
|
109
|
+
performSinglePathOperation(call) {
|
|
110
|
+
.delete(url: $0)
|
|
140
111
|
}
|
|
141
112
|
}
|
|
142
113
|
|
|
@@ -144,23 +115,10 @@ public class FilesystemPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
144
115
|
* Make a new directory, optionally creating parent folders first.
|
|
145
116
|
*/
|
|
146
117
|
@objc func mkdir(_ call: CAPPluginCall) {
|
|
147
|
-
|
|
148
|
-
handleError(call, "path must be provided and must be a string.")
|
|
149
|
-
return
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
let recursive = call.getBool("recursive") ?? false
|
|
153
|
-
let directory = call.getString("directory")
|
|
154
|
-
guard let fileUrl = implementation.getFileUrl(at: path, in: directory) else {
|
|
155
|
-
handleError(call, "Invalid path")
|
|
156
|
-
return
|
|
157
|
-
}
|
|
118
|
+
let recursive = call.getBool(Constants.MethodParameter.recursive, false)
|
|
158
119
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
call.resolve()
|
|
162
|
-
} catch let error as NSError {
|
|
163
|
-
handleError(call, error.localizedDescription, error)
|
|
120
|
+
performSinglePathOperation(call) {
|
|
121
|
+
.mkdir(url: $0, recursive: recursive)
|
|
164
122
|
}
|
|
165
123
|
}
|
|
166
124
|
|
|
@@ -168,24 +126,10 @@ public class FilesystemPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
168
126
|
* Remove a directory.
|
|
169
127
|
*/
|
|
170
128
|
@objc func rmdir(_ call: CAPPluginCall) {
|
|
171
|
-
|
|
172
|
-
handleError(call, "path must be provided and must be a string.")
|
|
173
|
-
return
|
|
174
|
-
}
|
|
129
|
+
let recursive = call.getBool(Constants.MethodParameter.recursive, false)
|
|
175
130
|
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
handleError(call, "Invalid path")
|
|
179
|
-
return
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
let recursive = call.getBool("recursive") ?? false
|
|
183
|
-
|
|
184
|
-
do {
|
|
185
|
-
try implementation.rmdir(at: fileUrl, recursive: recursive)
|
|
186
|
-
call.resolve()
|
|
187
|
-
} catch let error as NSError {
|
|
188
|
-
handleError(call, error.localizedDescription, error)
|
|
131
|
+
performSinglePathOperation(call) {
|
|
132
|
+
.rmdir(url: $0, recursive: recursive)
|
|
189
133
|
}
|
|
190
134
|
}
|
|
191
135
|
|
|
@@ -193,130 +137,29 @@ public class FilesystemPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
193
137
|
* Read the contents of a directory.
|
|
194
138
|
*/
|
|
195
139
|
@objc func readdir(_ call: CAPPluginCall) {
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
return
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
let directory = call.getString("directory")
|
|
202
|
-
guard let fileUrl = implementation.getFileUrl(at: path, in: directory) else {
|
|
203
|
-
handleError(call, "Invalid path")
|
|
204
|
-
return
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
do {
|
|
208
|
-
let directoryContents = try implementation.readdir(at: fileUrl)
|
|
209
|
-
let directoryContent = try directoryContents.map {(url: URL) -> [String: Any] in
|
|
210
|
-
let attr = try implementation.stat(at: url)
|
|
211
|
-
var ctime: UInt64 = 0
|
|
212
|
-
var mtime: UInt64 = 0
|
|
213
|
-
|
|
214
|
-
if let ctimeSeconds = (attr[.creationDate] as? Date)?.timeIntervalSince1970 {
|
|
215
|
-
ctime = UInt64((ctimeSeconds * 1000).rounded())
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
if let mtimeSeconds = (attr[.modificationDate] as? Date)?.timeIntervalSince1970 {
|
|
219
|
-
mtime = UInt64((mtimeSeconds * 1000).rounded())
|
|
220
|
-
}
|
|
221
|
-
return [
|
|
222
|
-
"name": url.lastPathComponent,
|
|
223
|
-
"type": implementation.getType(from: attr),
|
|
224
|
-
"size": attr[.size] as? UInt64 ?? 0,
|
|
225
|
-
"ctime": ctime,
|
|
226
|
-
"mtime": mtime,
|
|
227
|
-
"uri": url.absoluteString
|
|
228
|
-
]
|
|
229
|
-
}
|
|
230
|
-
call.resolve([
|
|
231
|
-
"files": directoryContent
|
|
232
|
-
])
|
|
233
|
-
} catch {
|
|
234
|
-
handleError(call, error.localizedDescription, error)
|
|
140
|
+
performSinglePathOperation(call) {
|
|
141
|
+
.readdir(url: $0)
|
|
235
142
|
}
|
|
236
143
|
}
|
|
237
144
|
|
|
238
145
|
@objc func stat(_ call: CAPPluginCall) {
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
return
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
let directory = call.getString("directory")
|
|
245
|
-
guard let fileUrl = implementation.getFileUrl(at: path, in: directory) else {
|
|
246
|
-
handleError(call, "Invalid path")
|
|
247
|
-
return
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
do {
|
|
251
|
-
let attr = try implementation.stat(at: fileUrl)
|
|
252
|
-
|
|
253
|
-
var ctime: UInt64 = 0
|
|
254
|
-
var mtime: UInt64 = 0
|
|
255
|
-
|
|
256
|
-
if let ctimeSeconds = (attr[.creationDate] as? Date)?.timeIntervalSince1970 {
|
|
257
|
-
ctime = UInt64((ctimeSeconds * 1000).rounded())
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
if let mtimeSeconds = (attr[.modificationDate] as? Date)?.timeIntervalSince1970 {
|
|
261
|
-
mtime = UInt64((mtimeSeconds * 1000).rounded())
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
call.resolve([
|
|
265
|
-
"type": implementation.getType(from: attr),
|
|
266
|
-
"size": attr[.size] as? UInt64 ?? 0,
|
|
267
|
-
"ctime": ctime,
|
|
268
|
-
"mtime": mtime,
|
|
269
|
-
"uri": fileUrl.absoluteString
|
|
270
|
-
])
|
|
271
|
-
} catch {
|
|
272
|
-
handleError(call, error.localizedDescription, error)
|
|
146
|
+
performSinglePathOperation(call) {
|
|
147
|
+
.stat(url: $0)
|
|
273
148
|
}
|
|
274
149
|
}
|
|
275
150
|
|
|
276
151
|
@objc func getUri(_ call: CAPPluginCall) {
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
return
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
let directory = call.getString("directory")
|
|
283
|
-
guard let fileUrl = implementation.getFileUrl(at: path, in: directory) else {
|
|
284
|
-
handleError(call, "Invalid path")
|
|
285
|
-
return
|
|
152
|
+
performSinglePathOperation(call) {
|
|
153
|
+
.getUri(url: $0)
|
|
286
154
|
}
|
|
287
|
-
|
|
288
|
-
call.resolve([
|
|
289
|
-
"uri": fileUrl.absoluteString
|
|
290
|
-
])
|
|
291
|
-
|
|
292
155
|
}
|
|
293
156
|
|
|
294
157
|
/**
|
|
295
158
|
* Rename a file or directory.
|
|
296
159
|
*/
|
|
297
160
|
@objc func rename(_ call: CAPPluginCall) {
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
return
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
let directory = call.getString("directory")
|
|
304
|
-
let toDirectory = call.getString("toDirectory") ?? directory
|
|
305
|
-
|
|
306
|
-
guard let fromUrl = implementation.getFileUrl(at: from, in: directory) else {
|
|
307
|
-
handleError(call, "Invalid from path")
|
|
308
|
-
return
|
|
309
|
-
}
|
|
310
|
-
|
|
311
|
-
guard let toUrl = implementation.getFileUrl(at: to, in: toDirectory) else {
|
|
312
|
-
handleError(call, "Invalid to path")
|
|
313
|
-
return
|
|
314
|
-
}
|
|
315
|
-
do {
|
|
316
|
-
try implementation.rename(at: fromUrl, to: toUrl)
|
|
317
|
-
call.resolve()
|
|
318
|
-
} catch let error as NSError {
|
|
319
|
-
handleError(call, error.localizedDescription, error)
|
|
161
|
+
performDualPathOperation(call) {
|
|
162
|
+
.rename(source: $0, destination: $1)
|
|
320
163
|
}
|
|
321
164
|
}
|
|
322
165
|
|
|
@@ -324,48 +167,18 @@ public class FilesystemPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
324
167
|
* Copy a file or directory.
|
|
325
168
|
*/
|
|
326
169
|
@objc func copy(_ call: CAPPluginCall) {
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
return
|
|
170
|
+
performDualPathOperation(call) {
|
|
171
|
+
.copy(source: $0, destination: $1)
|
|
330
172
|
}
|
|
331
|
-
|
|
332
|
-
let directory = call.getString("directory")
|
|
333
|
-
let toDirectory = call.getString("toDirectory") ?? directory
|
|
334
|
-
|
|
335
|
-
guard let fromUrl = implementation.getFileUrl(at: from, in: directory) else {
|
|
336
|
-
handleError(call, "Invalid from path")
|
|
337
|
-
return
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
guard let toUrl = implementation.getFileUrl(at: to, in: toDirectory) else {
|
|
341
|
-
handleError(call, "Invalid to path")
|
|
342
|
-
return
|
|
343
|
-
}
|
|
344
|
-
do {
|
|
345
|
-
try implementation.copy(at: fromUrl, to: toUrl)
|
|
346
|
-
call.resolve([
|
|
347
|
-
"uri": toUrl.absoluteString
|
|
348
|
-
])
|
|
349
|
-
} catch let error as NSError {
|
|
350
|
-
handleError(call, error.localizedDescription, error)
|
|
351
|
-
}
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
@objc override public func checkPermissions(_ call: CAPPluginCall) {
|
|
355
|
-
call.resolve([
|
|
356
|
-
"publicStorage": "granted"
|
|
357
|
-
])
|
|
358
|
-
}
|
|
359
|
-
|
|
360
|
-
@objc override public func requestPermissions(_ call: CAPPluginCall) {
|
|
361
|
-
call.resolve([
|
|
362
|
-
"publicStorage": "granted"
|
|
363
|
-
])
|
|
364
173
|
}
|
|
365
174
|
|
|
175
|
+
/**
|
|
176
|
+
* [DEPRECATED] Download a file
|
|
177
|
+
*/
|
|
178
|
+
@available(*, deprecated, message: "Use @capacitor/file-transfer plugin instead.")
|
|
366
179
|
@objc func downloadFile(_ call: CAPPluginCall) {
|
|
367
180
|
guard let url = call.getString("url") else { return call.reject("Must provide a URL") }
|
|
368
|
-
let progressEmitter:
|
|
181
|
+
let progressEmitter: LegacyFilesystemImplementation.ProgressEmitter = { bytes, contentLength in
|
|
369
182
|
self.notifyListeners("progress", data: [
|
|
370
183
|
"url": url,
|
|
371
184
|
"bytes": bytes,
|
|
@@ -374,17 +187,43 @@ public class FilesystemPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
374
187
|
}
|
|
375
188
|
|
|
376
189
|
do {
|
|
377
|
-
try
|
|
190
|
+
try legacyImplementation.downloadFile(call: call, emitter: progressEmitter, config: bridge?.config)
|
|
378
191
|
} catch let error {
|
|
379
192
|
call.reject(error.localizedDescription)
|
|
380
193
|
}
|
|
381
194
|
}
|
|
195
|
+
}
|
|
382
196
|
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
197
|
+
// MARK: - Operation Execution
|
|
198
|
+
private extension FilesystemPlugin {
|
|
199
|
+
func performSinglePathOperation(_ call: CAPPluginCall, operationBuilder: (URL) -> FilesystemOperation) {
|
|
200
|
+
executeOperation(call) { service in
|
|
201
|
+
FilesystemLocationResolver(service: service)
|
|
202
|
+
.resolveSinglePath(from: call)
|
|
203
|
+
.map { operationBuilder($0) }
|
|
204
|
+
}
|
|
388
205
|
}
|
|
389
206
|
|
|
207
|
+
func performDualPathOperation(_ call: CAPPluginCall, operationBuilder: (URL, URL) -> FilesystemOperation) {
|
|
208
|
+
executeOperation(call) { service in
|
|
209
|
+
FilesystemLocationResolver(service: service)
|
|
210
|
+
.resolveDualPaths(from: call)
|
|
211
|
+
.map { operationBuilder($0.source, $0.destination) }
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
func executeOperation(_ call: CAPPluginCall, operationProvider: (FileService) -> Result<FilesystemOperation, FilesystemError>) {
|
|
216
|
+
switch getService() {
|
|
217
|
+
case .success(let service):
|
|
218
|
+
switch operationProvider(service) {
|
|
219
|
+
case .success(let operation):
|
|
220
|
+
let executor = FilesystemOperationExecutor(service: service)
|
|
221
|
+
executor.execute(operation, call)
|
|
222
|
+
case .failure(let error):
|
|
223
|
+
call.handleError(error)
|
|
224
|
+
}
|
|
225
|
+
case .failure(let error):
|
|
226
|
+
call.handleError(error)
|
|
227
|
+
}
|
|
228
|
+
}
|
|
390
229
|
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import Foundation
|
|
2
|
+
import IONFilesystemLib
|
|
3
|
+
|
|
4
|
+
extension IONFILEStringEncoding {
|
|
5
|
+
static func create(from text: String) -> Self {
|
|
6
|
+
switch text {
|
|
7
|
+
case Constants.StringEncodingValue.ascii: .ascii
|
|
8
|
+
case Constants.StringEncodingValue.utf16: .utf16
|
|
9
|
+
case Constants.StringEncodingValue.utf8: .utf8
|
|
10
|
+
default: .utf8
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
extension IONFILEDirectoryType {
|
|
16
|
+
static func create(from text: String) -> Self? {
|
|
17
|
+
switch text {
|
|
18
|
+
case Constants.DirectoryTypeValue.cache: .cache
|
|
19
|
+
case Constants.DirectoryTypeValue.data, Constants.DirectoryTypeValue.documents, Constants.DirectoryTypeValue.external, Constants.DirectoryTypeValue.externalCache, Constants.DirectoryTypeValue.externalStorage: .document
|
|
20
|
+
case Constants.DirectoryTypeValue.library: .library
|
|
21
|
+
case Constants.DirectoryTypeValue.libraryNoCloud: .notSyncedLibrary
|
|
22
|
+
case Constants.DirectoryTypeValue.temporary: .temporary
|
|
23
|
+
default: nil
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
extension IONFILEEncodingValueMapper {
|
|
29
|
+
var textValue: String {
|
|
30
|
+
switch self {
|
|
31
|
+
case .byteBuffer(let data): data.base64EncodedString()
|
|
32
|
+
case .string(_, let text): text
|
|
33
|
+
@unknown default: ""
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
extension IONFILEItemAttributeModel {
|
|
39
|
+
typealias JSResult = [String: Any]
|
|
40
|
+
func toJSResult(with url: URL) -> JSResult {
|
|
41
|
+
[
|
|
42
|
+
Constants.ItemAttributeJSONKey.name: url.lastPathComponent,
|
|
43
|
+
Constants.ItemAttributeJSONKey.type: type.description,
|
|
44
|
+
Constants.ItemAttributeJSONKey.size: size,
|
|
45
|
+
Constants.ItemAttributeJSONKey.ctime: UInt64(creationDateTimestamp.rounded()),
|
|
46
|
+
Constants.ItemAttributeJSONKey.mtime: UInt64(modificationDateTimestamp.rounded()),
|
|
47
|
+
Constants.ItemAttributeJSONKey.uri: url.absoluteString
|
|
48
|
+
]
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
extension IONFILEItemType {
|
|
53
|
+
var description: String {
|
|
54
|
+
switch self {
|
|
55
|
+
case .directory: Constants.FileItemTypeValue.directory
|
|
56
|
+
case .file: Constants.FileItemTypeValue.file
|
|
57
|
+
@unknown default: Constants.FileItemTypeValue.fallback
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|