@react-native-documents/picker 10.1.3 → 10.1.5

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.
@@ -3,6 +3,8 @@ package com.reactnativedocumentpicker
3
3
  import android.content.ContentResolver
4
4
  import android.content.Context
5
5
  import android.net.Uri
6
+ import android.os.Build
7
+ import android.os.FileUtils
6
8
  import com.facebook.react.bridge.Arguments
7
9
  import com.facebook.react.bridge.ReactApplicationContext
8
10
  import com.facebook.react.bridge.ReactContext
@@ -18,7 +20,7 @@ import java.io.FileNotFoundException
18
20
  import java.io.FileOutputStream
19
21
  import java.io.IOException
20
22
  import java.io.InputStream
21
- import java.nio.channels.Channels
23
+ import java.io.OutputStream
22
24
  import java.util.UUID
23
25
 
24
26
  class FileOperations(private val uriMap: MutableMap<String, Uri>) {
@@ -119,23 +121,17 @@ class FileOperations(private val uriMap: MutableMap<String, Uri>) {
119
121
  val destFileSafe = safeGetDestination(attemptedDestFile, destinationDir)
120
122
 
121
123
  val copyStreamToFile: (InputStream?) -> Unit = { inputStream ->
122
- if (inputStream == null) {
123
- throw FileNotFoundException("No input stream was found for the source file")
124
- }
125
- FileOutputStream(destFileSafe).channel.use { destinationFileChannel ->
126
- val inputChannel = Channels.newChannel(inputStream)
127
- val size = destinationFileChannel.transferFrom(inputChannel, 0, Long.MAX_VALUE)
128
- if (size == 0L) {
129
- throw IOException("No data was copied to the destination file")
130
- }
124
+ inputStream ?: throw FileNotFoundException("No input stream was found for the source file")
125
+ val bytesCopied = copyStreamToAnother(inputStream, FileOutputStream(destFileSafe))
126
+ if (bytesCopied == 0L) {
127
+ throw IOException("No data was copied to the destination file")
131
128
  }
132
129
  }
133
130
 
134
131
  if (convertVirtualFileAsType == null) {
135
- context.contentResolver.openInputStream(from).use(copyStreamToFile)
132
+ copyStreamToFile(context.contentResolver.openInputStream(from))
136
133
  } else {
137
- getInputStreamForVirtualFile(context.contentResolver, from, convertVirtualFileAsType)
138
- .use(copyStreamToFile)
134
+ copyStreamToFile(getInputStreamForVirtualFile(context.contentResolver, from, convertVirtualFileAsType))
139
135
  }
140
136
 
141
137
  return destFileSafe
@@ -161,9 +157,7 @@ class FileOperations(private val uriMap: MutableMap<String, Uri>) {
161
157
  }
162
158
 
163
159
  fun writeDocumentImpl(sourceUri: Uri?, targetUriString: String?, context: ReactApplicationContext): DocumentMetadataBuilder {
164
- if (sourceUri == null) {
165
- throw IllegalArgumentException("The source URI is null. Call saveDocument() before writeDocument()")
166
- }
160
+ sourceUri ?: throw IllegalArgumentException("The source URI is null. Call saveDocument() before writeDocument()")
167
161
  val targetUri: Uri? = uriMap[targetUriString]
168
162
 
169
163
  if (targetUri == null) {
@@ -180,25 +174,30 @@ class FileOperations(private val uriMap: MutableMap<String, Uri>) {
180
174
  val mimeFromUri = contentResolver.getType(targetUri)
181
175
  metadataBuilder.mimeType(mimeFromUri)
182
176
 
183
- // TODO https://gist.github.com/vonovak/73affb1a5b904ee165d9b5981d8dfe9a
184
- contentResolver.openInputStream(sourceUri).use { inputStream ->
185
- if (inputStream == null) {
186
- metadataBuilder.metadataReadingError("No output stream found for source file")
187
- } else {
188
- contentResolver.openOutputStream(targetUri).use { outputStream ->
189
- if (outputStream == null) {
190
- metadataBuilder.metadataReadingError("No output stream found for destination file")
191
- } else {
192
- val bytesCopied = inputStream.copyTo(outputStream)
193
- if (bytesCopied == 0L) {
194
- metadataBuilder.metadataReadingError("No data was copied to the destination file")
195
- }
196
- outputStream.flush()
197
- }
198
- }
199
- }
177
+ val inputStream = contentResolver.openInputStream(sourceUri)
178
+ ?: return metadataBuilder.metadataReadingError("No input stream found for source file")
179
+
180
+ val outputStream = contentResolver.openOutputStream(targetUri)
181
+ ?: return metadataBuilder.metadataReadingError("No output stream found for destination file")
182
+
183
+ val bytesCopied = copyStreamToAnother(inputStream, outputStream)
184
+ if (bytesCopied == 0L) {
185
+ metadataBuilder.metadataReadingError("No data was copied to the destination file")
200
186
  }
201
187
 
202
188
  return metadataBuilder
203
189
  }
190
+
191
+ val copyStreamToAnother: (InputStream, OutputStream) -> Long = { inputStream, outputStream ->
192
+ inputStream.use { input ->
193
+ outputStream.use { output ->
194
+ val bytesCopied = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
195
+ FileUtils.copy(inputStream, outputStream)
196
+ } else {
197
+ inputStream.copyTo(outputStream)
198
+ }
199
+ return@use bytesCopied
200
+ }
201
+ }
202
+ }
204
203
  }
@@ -28,10 +28,10 @@ import MobileCoreServices
28
28
  }
29
29
 
30
30
  public func getMetadataFor(url: URL) throws -> DocumentMetadataBuilder {
31
- if (currentOptions?.isOpenMode() == true) {
32
- return try self.getOpenedDocumentInfo(url: url, requestLongTermAccess: currentOptions?.requestLongTermAccess ?? false)
31
+ return if (currentOptions?.isOpenMode() == true) {
32
+ try self.getOpenedDocumentInfo(url: url, requestLongTermAccess: currentOptions?.requestLongTermAccess ?? false)
33
33
  } else {
34
- return try self.getAnyModeMetadata(url: url)
34
+ try self.getAnyModeMetadata(url: url)
35
35
  }
36
36
  }
37
37
 
@@ -4,45 +4,47 @@ import Foundation
4
4
 
5
5
  @objc public class FileOperations: NSObject {
6
6
 
7
- @objc public static func keepLocalCopyAtUniqueDestination(from: Array<Dictionary<String, String>>, destinationPreset: String, resolve: @escaping RNDPPromiseResolveBlock) {
8
- Task {
9
- let results = await moveFiles(from: from, destinationPreset: destinationPreset)
7
+ @objc public static func keepLocalCopyAtUniqueDestination(from: [[String: String]], destinationPreset: String, resolve: @escaping RNDPPromiseResolveBlock) {
8
+ DispatchQueue.global(qos: .utility).async {
9
+ let results = moveFiles(from: from, destinationPreset: destinationPreset)
10
10
  resolve(results)
11
11
  }
12
12
  }
13
13
 
14
- static func moveFiles(from: Array<Dictionary<String, String>>, destinationPreset: String) async -> [[String: String?]] {
14
+ static func moveFiles(from: [[String: String]], destinationPreset: String) -> [[String: String?]] {
15
15
  let destinationRootDir = getDirectoryForFileDestination(destinationPreset)
16
16
  let uniqueSubDirName = UUID().uuidString
17
- let destinationDir: URL = destinationRootDir.appendingPathComponent("\(uniqueSubDirName)/", isDirectory: true)
18
- // TODO do we need all of this Task dance?
17
+ let destinationDir = destinationRootDir.appendingPathComponent(uniqueSubDirName, isDirectory: true)
19
18
 
20
- return await withTaskGroup(of: LocalCopyResponse.self) { group in
21
- var results: Array<Dictionary<String, String?>> = [[String: String?]]()
22
-
23
- for dictionary in from {
24
- group.addTask {
25
- do {
26
- guard let uriString = dictionary["uri"], let uri = URL(string: uriString) else {
27
- return LocalCopyResponse.error(sourceUri: dictionary["uri"], copyError: "Invalid URI")
28
- }
29
- guard let fileName = dictionary["fileName"] else {
30
- return LocalCopyResponse.error(sourceUri: uri.absoluteString, copyError: "Invalid fileName")
31
- }
32
-
33
- let destinationUrl = try moveToDestination(from: uri, usingFilename: fileName, destinationDir: destinationDir)
34
- return LocalCopyResponse.success(sourceUri: uri.absoluteString, localUri: destinationUrl.absoluteString)
35
- } catch {
36
- return LocalCopyResponse.error(sourceUri: dictionary["uri"]!, copyError: error.localizedDescription)
37
- }
38
- }
19
+ do {
20
+ try FileManager.default.createDirectory(at: destinationDir, withIntermediateDirectories: true, attributes: nil)
21
+ } catch {
22
+ return from.map { dictionary in
23
+ LocalCopyResponse.error(sourceUri: dictionary["uri"], copyError: "Failed to create destination directory: \(error.localizedDescription)").dictionaryRepresentation
39
24
  }
40
-
41
- for await result in group {
42
- results.append(result.dictionaryRepresentation)
43
- }
44
-
45
- return results
25
+ }
26
+
27
+ // move files
28
+ return from.map { dictionary in
29
+ moveSingleFile(dictionary: dictionary, destinationDir: destinationDir).dictionaryRepresentation
30
+ }
31
+ }
32
+
33
+ private static func moveSingleFile(dictionary: [String: String], destinationDir: URL) -> LocalCopyResponse {
34
+ guard let uriString = dictionary["uri"],
35
+ let uri = URL(string: uriString),
36
+ let fileName = dictionary["fileName"] else {
37
+ return LocalCopyResponse.error(
38
+ sourceUri: dictionary["uri"],
39
+ copyError: "Invalid URI or fileName"
40
+ )
41
+ }
42
+
43
+ do {
44
+ let destinationUrl = try moveToDestination(from: uri, usingFilename: fileName, destinationDir: destinationDir)
45
+ return LocalCopyResponse.success(sourceUri: uri.absoluteString, localUri: destinationUrl.absoluteString)
46
+ } catch {
47
+ return LocalCopyResponse.error(sourceUri: uriString, copyError: error.localizedDescription)
46
48
  }
47
49
  }
48
50
 
@@ -50,19 +52,20 @@ import Foundation
50
52
  let destinationFile = destinationDir.appendingPathComponent(fileName).standardized
51
53
 
52
54
  guard destinationFile.path.hasPrefix(destinationDir.standardized.path) else {
53
- throw NSError(domain: "PathTraversalPrevention", code: 400, userInfo: [NSLocalizedDescriptionKey: "The copied file is attempting to write outside of the target directory."])
55
+ throw NSError(
56
+ domain: "PathTraversalPrevention",
57
+ code: 400,
58
+ userInfo: [NSLocalizedDescriptionKey: "The copied file is attempting to write outside of the target directory."]
59
+ )
54
60
  }
55
61
 
56
- try FileManager.default.createDirectory(at: destinationDir, withIntermediateDirectories: true, attributes: nil)
57
62
  try FileManager.default.moveItem(at: from, to: destinationFile)
58
63
 
59
64
  return destinationFile
60
65
  }
61
66
 
62
67
  static func getDirectoryForFileDestination(_ copyToDirectory: String) -> URL {
63
- if copyToDirectory == "documentDirectory" {
64
- return FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
65
- }
66
- return FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask).first!
68
+ let searchPath: FileManager.SearchPathDirectory = copyToDirectory == "documentDirectory" ? .documentDirectory : .cachesDirectory
69
+ return FileManager.default.urls(for: searchPath, in: .userDomainMask).first!
67
70
  }
68
71
  }
@@ -28,14 +28,10 @@ import UniformTypeIdentifiers
28
28
 
29
29
  static func createUTType(kind: String, value: String) -> UTType? {
30
30
  switch kind {
31
- case "UTType":
32
- return UTType(value)
33
- case "mimeType":
34
- return UTType(mimeType: value)
35
- case "extension":
36
- return UTType(filenameExtension: value)
37
- default:
38
- return nil
31
+ case "UTType": UTType(value)
32
+ case "mimeType": UTType(mimeType: value)
33
+ case "extension": UTType(filenameExtension: value)
34
+ default: nil
39
35
  }
40
36
  }
41
37
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@react-native-documents/picker",
3
- "version": "10.1.3",
3
+ "version": "10.1.5",
4
4
  "description": "A react native interface to access documents from dropbox, google drive, iCloud...",
5
5
  "main": "lib/commonjs/index",
6
6
  "module": "lib/module/index",