expo-file-system 16.0.4 β†’ 16.0.6

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 CHANGED
@@ -10,6 +10,18 @@
10
10
 
11
11
  ### πŸ’‘ Others
12
12
 
13
+ ## 16.0.6 β€” 2024-02-06
14
+
15
+ ### πŸ› Bug fixes
16
+
17
+ - On `iOS`, fix upload task requests. ([#26880](https://github.com/expo/expo/pull/26880) by [@alanjhughes](https://github.com/alanjhughes))
18
+
19
+ ## 16.0.5 β€” 2024-01-23
20
+
21
+ ### πŸ› Bug fixes
22
+
23
+ - On `iOS`, set `httpMethod` on upload requests. ([#26516](https://github.com/expo/expo/pull/26516) by [@alanjhughes](https://github.com/alanjhughes))
24
+
13
25
  ## 16.0.4 β€” 2024-01-18
14
26
 
15
27
  _This version does not introduce any user-facing changes._
@@ -3,7 +3,7 @@ apply plugin: 'kotlin-android'
3
3
  apply plugin: 'maven-publish'
4
4
 
5
5
  group = 'host.exp.exponent'
6
- version = '16.0.4'
6
+ version = '16.0.6'
7
7
 
8
8
  def expoModulesCorePlugin = new File(project(":expo-modules-core").projectDir.absolutePath, "ExpoModulesCorePlugin.gradle")
9
9
  if (expoModulesCorePlugin.exists()) {
@@ -94,7 +94,7 @@ android {
94
94
  namespace "expo.modules.filesystem"
95
95
  defaultConfig {
96
96
  versionCode 30
97
- versionName "16.0.4"
97
+ versionName "16.0.6"
98
98
  }
99
99
  }
100
100
 
@@ -67,3 +67,15 @@ final class CannotDetermineDiskCapacity: Exception {
67
67
  "Unable to determine free disk storage capacity"
68
68
  }
69
69
  }
70
+
71
+ final class FailedToCreateBodyException: Exception {
72
+ override var reason: String {
73
+ "Unable to create multipart body"
74
+ }
75
+ }
76
+
77
+ final class FailedToAccessDirectoryException: Exception {
78
+ override var reason: String {
79
+ "Failed to access `Caches` directory"
80
+ }
81
+ }
@@ -156,7 +156,7 @@ public final class FileSystemModule: Module {
156
156
  throw FileNotExistsException(localUrl.path)
157
157
  }
158
158
  let session = options.sessionType == .background ? backgroundSession : foregroundSession
159
- let task = createUploadTask(session: session, targetUrl: targetUrl, sourceUrl: localUrl, options: options)
159
+ let task = try createUploadTask(session: session, targetUrl: targetUrl, sourceUrl: localUrl, options: options)
160
160
  let taskDelegate = EXSessionUploadTaskDelegate(resolve: promise.resolver, reject: promise.legacyRejecter)
161
161
 
162
162
  sessionTaskDispatcher.register(taskDelegate, for: task)
@@ -165,7 +165,7 @@ public final class FileSystemModule: Module {
165
165
 
166
166
  AsyncFunction("uploadTaskStartAsync") { (targetUrl: URL, localUrl: URL, uuid: String, options: UploadOptions, promise: Promise) in
167
167
  let session = options.sessionType == .background ? backgroundSession : foregroundSession
168
- let task = createUploadTask(session: session, targetUrl: targetUrl, sourceUrl: localUrl, options: options)
168
+ let task = try createUploadTask(session: session, targetUrl: targetUrl, sourceUrl: localUrl, options: options)
169
169
  let onSend: EXUploadDelegateOnSendCallback = { [weak self] _, _, totalBytesSent, totalBytesExpectedToSend in
170
170
  self?.sendEvent(EVENT_UPLOAD_PROGRESS, [
171
171
  "uuid": uuid,
@@ -30,55 +30,69 @@ func createUrlRequest(url: URL, headers: [String: String]?) -> URLRequest {
30
30
  return request
31
31
  }
32
32
 
33
- func createUploadTask(session: URLSession, targetUrl: URL, sourceUrl: URL, options: UploadOptions) -> URLSessionUploadTask {
33
+ func createUploadTask(session: URLSession, targetUrl: URL, sourceUrl: URL, options: UploadOptions) throws -> URLSessionUploadTask {
34
34
  var request = createUrlRequest(url: targetUrl, headers: options.headers)
35
+ request.httpMethod = options.httpMethod.rawValue
35
36
 
36
37
  switch options.uploadType {
37
38
  case .binaryContent:
38
39
  return session.uploadTask(with: request, fromFile: sourceUrl)
39
40
  case .multipart:
40
41
  let boundaryString = UUID().uuidString
41
- let data = try? createMultipartBody(boundary: boundaryString, sourceUrl: sourceUrl, options: options)
42
+ guard let data = createMultipartBody(boundary: boundaryString, sourceUrl: sourceUrl, options: options) else {
43
+ throw FailedToCreateBodyException()
44
+ }
42
45
 
43
46
  request.setValue("multipart/form-data; boundary=\(boundaryString)", forHTTPHeaderField: "Content-Type")
44
- request.httpBody = data
45
47
 
46
- return session.uploadTask(withStreamedRequest: request)
48
+ let localURL = try createLocalUrl(from: sourceUrl)
49
+ try? data.write(to: localURL)
50
+
51
+ return session.uploadTask(with: request, fromFile: localURL)
52
+ }
53
+ }
54
+
55
+ func createLocalUrl(from sourceUrl: URL) throws -> URL {
56
+ guard let cachesDir = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask).first else {
57
+ throw FailedToAccessDirectoryException()
47
58
  }
59
+ let tempDir = cachesDir.appendingPathComponent("uploads")
60
+ FileSystemUtilities.ensureDirExists(at: tempDir)
61
+ return tempDir.appendingPathComponent(sourceUrl.lastPathComponent)
48
62
  }
49
63
 
50
- func createMultipartBody(boundary: String, sourceUrl: URL, options: UploadOptions) throws -> Data {
51
- let fileName = options.fieldName ?? sourceUrl.lastPathComponent
52
- let fileContents = try String(contentsOf: sourceUrl)
53
- let mimeType = options.mimeType ?? findMimeType(forAttachment: sourceUrl)
64
+ func createMultipartBody(boundary: String, sourceUrl: URL, options: UploadOptions) -> Data? {
65
+ let fieldName = options.fieldName ?? sourceUrl.lastPathComponent
66
+ var mimeType = options.mimeType ?? findMimeType(forAttachment: sourceUrl)
67
+ guard let data = try? Data(contentsOf: sourceUrl) else {
68
+ return nil
69
+ }
54
70
 
55
- let body = """
56
- \(headersForMultipartParams(options.parameters, boundary: boundary))
57
- --\(boundary)
58
- Content-Disposition: form-data; name="\(fileName)"; filename="\(sourceUrl.lastPathComponent)"
59
- Content-Type: \(mimeType)
71
+ var body = Data()
72
+ headersForMultipartParams(options.parameters, boundary: boundary, body: &body)
60
73
 
61
- \(fileContents)
62
- --\(boundary)--
63
- """
74
+ body.append("--\(boundary)\r\n".data)
75
+ body.append("Content-Disposition: form-data; name=\"\(fieldName)\"; filename=\"\(sourceUrl.lastPathComponent)\"\r\n".data)
76
+ body.append("Content-Type: \(mimeType)\r\n\r\n".data)
77
+ body.append(data)
78
+ body.append("\r\n".data)
79
+ body.append("--\(boundary)--\r\n".data)
64
80
 
65
- guard let bodyData = body.data(using: .utf8) else {
66
- throw HeaderEncodingFailedException(sourceUrl.absoluteString)
67
- }
68
- return bodyData
81
+ return body
69
82
  }
70
83
 
71
- func headersForMultipartParams(_ params: [String: String]?, boundary: String) -> String {
72
- guard let params else {
73
- return ""
84
+ func headersForMultipartParams(_ params: [String: String]?, boundary: String, body: inout Data) {
85
+ if let params {
86
+ for (key, value) in params {
87
+ body.append("--\(boundary)\r\n".data)
88
+ body.append("Content-Disposition: form-data; name=\"\(key)\"\r\n\r\n".data)
89
+ body.append("\(value)\r\n".data)
90
+ }
74
91
  }
75
- return params.map { (key: String, value: String) in
76
- """
77
- --\(boundary)
78
- Content-Disposition: form-data; name="\(key)"
92
+ }
79
93
 
80
- \(value)
81
- """
82
- }
83
- .joined()
94
+ // All swift strings are unicode correct.
95
+ // This avoids the optional created by string.data(using: .utf8)
96
+ private extension String {
97
+ var data: Data { Data(self.utf8) }
84
98
  }
@@ -20,7 +20,7 @@ class EXFileSystemSpec: ExpoSpec {
20
20
  it("should handle UTF-8 characters") {
21
21
  let utf8UriInput = "file:///var/mobile/δΈ­ζ–‡"
22
22
  let utf8UriExpectedOutput = "file:///var/mobile/%E4%B8%AD%E6%96%87"
23
- let utf8Uri = fileSystem.percentEncodedURL(fromURIString:utf8UriInput)
23
+ let utf8Uri = fileSystem.percentEncodedURL(fromURIString: utf8UriInput)
24
24
 
25
25
  expect(utf8Uri?.absoluteString) == utf8UriExpectedOutput
26
26
  expect(utf8Uri?.scheme) == "file"
@@ -29,7 +29,7 @@ class EXFileSystemSpec: ExpoSpec {
29
29
  it("should handle URI with percent, numbers and UTF-8 characters") {
30
30
  let input = "file:///document/directory/%40%2FδΈ­ζ–‡"
31
31
  let expectedOutput = "file:///document/directory/%40%2F%E4%B8%AD%E6%96%87"
32
- let uri = fileSystem.percentEncodedURL(fromURIString:input)
32
+ let uri = fileSystem.percentEncodedURL(fromURIString: input)
33
33
 
34
34
  expect(uri?.absoluteString) == expectedOutput
35
35
  }
@@ -37,7 +37,7 @@ class EXFileSystemSpec: ExpoSpec {
37
37
  it("should not decode percentages in URI") {
38
38
  let input = "file:///document/hello%2Fworld.txt"
39
39
  let unexpectedOutput = "file:///document/hello/world.txt"
40
- let uri = fileSystem.percentEncodedURL(fromURIString:input)
40
+ let uri = fileSystem.percentEncodedURL(fromURIString: input)
41
41
 
42
42
  // Should not create a directory named "hello"
43
43
  expect(uri?.absoluteString) != unexpectedOutput
@@ -45,7 +45,7 @@ class EXFileSystemSpec: ExpoSpec {
45
45
 
46
46
  it("should handle assets-library URIs") {
47
47
  let assetsLibraryUriInput = "assets-library://asset/asset.JPG?id=3C1D9C54-9521-488F-BB27-AA1EA0F8AF04/L0/001&ext=JPG"
48
- let assetsLibraryUri = fileSystem.percentEncodedURL(fromURIString:assetsLibraryUriInput)
48
+ let assetsLibraryUri = fileSystem.percentEncodedURL(fromURIString: assetsLibraryUriInput)
49
49
 
50
50
  expect(assetsLibraryUri?.absoluteString) == assetsLibraryUriInput
51
51
  expect(assetsLibraryUri?.scheme) == "assets-library"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "expo-file-system",
3
- "version": "16.0.4",
3
+ "version": "16.0.6",
4
4
  "description": "Provides access to the local file system on the device.",
5
5
  "main": "build/index.js",
6
6
  "types": "build/index.d.ts",
@@ -41,5 +41,5 @@
41
41
  "peerDependencies": {
42
42
  "expo": "*"
43
43
  },
44
- "gitHead": "102899632731658eecba006c0d1c79b98ba8f5f7"
44
+ "gitHead": "4f3dcf3e23eae997f884117fdd34ad734efad9fd"
45
45
  }