11import Foundation
22import Helpers
3+
34import class MultipartFormData. MultipartFormData
45
56#if canImport(FoundationNetworking)
67 import FoundationNetworking
78#endif
89
9- let DEFAULT_SEARCH_OPTIONS = SearchOptions (
10+ let defaultSearchOptions = SearchOptions (
1011 limit: 100 ,
1112 offset: 0 ,
1213 sortBy: SortBy (
@@ -21,6 +22,40 @@ private let defaultFileOptions = FileOptions(
2122 upsert: false
2223)
2324
25+ enum FileUpload {
26+ case data( Data )
27+ case url( URL )
28+
29+ func encode( to formData: MultipartFormData , withPath path: String , options: FileOptions ) {
30+ formData. append (
31+ options. cacheControl. data ( using: . utf8) !,
32+ withName: " cacheControl "
33+ )
34+
35+ if let metadata = options. metadata {
36+ formData. append ( encodeMetadata ( metadata) , withName: " metadata " )
37+ }
38+
39+ switch self {
40+ case let . data( data) :
41+ formData. append (
42+ data,
43+ withName: " " ,
44+ fileName: path. fileName,
45+ mimeType: options. contentType ?? mimeType ( forPathExtension: path. pathExtension)
46+ )
47+
48+ case let . url( url) :
49+ formData. append ( url, withName: " " )
50+ }
51+ }
52+
53+ private func encodeMetadata( _ metadata: JSONObject ) -> Data {
54+ let encoder = AnyJSON . encoder
55+ return ( try ? encoder. encode ( metadata) ) ?? " {} " . data ( using: . utf8) !
56+ }
57+ }
58+
2459/// Supabase Storage File API
2560public class StorageFileApi : StorageApi , @unchecked Sendable {
2661 /// The bucket id to operate on.
@@ -39,36 +74,23 @@ public class StorageFileApi: StorageApi, @unchecked Sendable {
3974 let signedURL : URL
4075 }
4176
42- private func encodeMetadata( _ metadata: JSONObject ) -> Data {
43- let encoder = AnyJSON . encoder
44- return ( try ? encoder. encode ( metadata) ) ?? " {} " . data ( using: . utf8) !
45- }
46-
4777 private func _uploadOrUpdate(
4878 method: HTTPMethod ,
4979 path: String ,
50- formData : MultipartFormData ,
80+ file : FileUpload ,
5181 options: FileOptions ?
5282 ) async throws -> FileUploadResponse {
5383 let options = options ?? defaultFileOptions
5484 var headers = options. headers. map { HTTPHeaders ( $0) } ?? HTTPHeaders ( )
5585
56- let metadata = options. metadata
57-
5886 if method == . post {
5987 headers. update ( name: " x-upsert " , value: " \( options. upsert) " )
6088 }
6189
6290 headers [ " duplex " ] = options. duplex
6391
64- if let metadata {
65- formData. append ( encodeMetadata ( metadata) , withName: " metadata " )
66- }
67-
68- formData. append (
69- options. cacheControl. data ( using: . utf8) !,
70- withName: " cacheControl "
71- )
92+ let formData = MultipartFormData ( )
93+ file. encode ( to: formData, withPath: path, options: options)
7294
7395 struct UploadResponse : Decodable {
7496 let Key : String
@@ -109,27 +131,26 @@ public class StorageFileApi: StorageApi, @unchecked Sendable {
109131 data: Data ,
110132 options: FileOptions = FileOptions ( )
111133 ) async throws -> FileUploadResponse {
112- let fileName = path. fileName
113- let formData = MultipartFormData ( )
114- formData. append (
115- data,
116- withName: fileName,
117- fileName: fileName,
118- mimeType: options. contentType ?? mimeType ( forPathExtension: path. pathExtension)
134+ try await _uploadOrUpdate (
135+ method: . post,
136+ path: path,
137+ file: . data( data) ,
138+ options: options
119139 )
120- return try await _uploadOrUpdate ( method: . post, path: path, formData: formData, options: options)
121140 }
122141
123142 @discardableResult
124143 public func upload(
125144 _ path: String ,
126- fileURL: Data ,
145+ fileURL: URL ,
127146 options: FileOptions = FileOptions ( )
128147 ) async throws -> FileUploadResponse {
129- let fileName = path. fileName
130- let formData = MultipartFormData ( )
131- formData. append ( fileURL, withName: fileName, fileName: fileName)
132- return try await _uploadOrUpdate ( method: . post, path: path, formData: formData, options: options)
148+ try await _uploadOrUpdate (
149+ method: . post,
150+ path: path,
151+ file: . url( fileURL) ,
152+ options: options
153+ )
133154 }
134155
135156 /// Replaces an existing file at the specified path with a new one.
@@ -144,15 +165,12 @@ public class StorageFileApi: StorageApi, @unchecked Sendable {
144165 data: Data ,
145166 options: FileOptions = FileOptions ( )
146167 ) async throws -> FileUploadResponse {
147- let fileName = path. fileName
148- let formData = MultipartFormData ( )
149- formData. append (
150- data,
151- withName: fileName,
152- fileName: fileName,
153- mimeType: options. contentType ?? mimeType ( forPathExtension: path. pathExtension)
168+ try await _uploadOrUpdate (
169+ method: . put,
170+ path: path,
171+ file: . data( data) ,
172+ options: options
154173 )
155- return try await _uploadOrUpdate ( method: . put, path: path, formData: formData, options: options)
156174 }
157175
158176 /// Replaces an existing file at the specified path with a new one.
@@ -167,9 +185,12 @@ public class StorageFileApi: StorageApi, @unchecked Sendable {
167185 fileURL: URL ,
168186 options: FileOptions = FileOptions ( )
169187 ) async throws -> FileUploadResponse {
170- let formData = MultipartFormData ( )
171- formData. append ( fileURL, withName: path. fileName)
172- return try await _uploadOrUpdate ( method: . put, path: path, formData: formData, options: options)
188+ try await _uploadOrUpdate (
189+ method: . put,
190+ path: path,
191+ file: . url( fileURL) ,
192+ options: options
193+ )
173194 }
174195
175196 /// Moves an existing file, optionally renaming it at the same time.
@@ -388,7 +409,7 @@ public class StorageFileApi: StorageApi, @unchecked Sendable {
388409 ) async throws -> [ FileObject ] {
389410 let encoder = JSONEncoder ( )
390411
391- var options = options ?? DEFAULT_SEARCH_OPTIONS
412+ var options = options ?? defaultSearchOptions
392413 options. prefix = path ?? " "
393414
394415 return try await execute (
@@ -579,18 +600,10 @@ public class StorageFileApi: StorageApi, @unchecked Sendable {
579600 data: Data ,
580601 options: FileOptions ? = nil
581602 ) async throws -> SignedURLUploadResponse {
582- let fileName = path. fileName
583- let formData = MultipartFormData ( )
584- formData. append (
585- data,
586- withName: fileName,
587- fileName: fileName,
588- mimeType: options? . contentType ?? mimeType ( forPathExtension: path. pathExtension)
589- )
590- return try await _uploadToSignedURL (
603+ try await _uploadToSignedURL (
591604 path: path,
592605 token: token,
593- formData : formData ,
606+ file : . data ( data ) ,
594607 options: options
595608 )
596609 }
@@ -606,23 +619,21 @@ public class StorageFileApi: StorageApi, @unchecked Sendable {
606619 public func uploadToSignedURL(
607620 _ path: String ,
608621 token: String ,
609- fileURL: Data ,
622+ fileURL: URL ,
610623 options: FileOptions ? = nil
611624 ) async throws -> SignedURLUploadResponse {
612- let formData = MultipartFormData ( )
613- formData. append ( fileURL, withName: path. fileName)
614- return try await _uploadToSignedURL (
625+ try await _uploadToSignedURL (
615626 path: path,
616627 token: token,
617- formData : formData ,
628+ file : . url ( fileURL ) ,
618629 options: options
619630 )
620631 }
621632
622633 private func _uploadToSignedURL(
623634 path: String ,
624635 token: String ,
625- formData : MultipartFormData ,
636+ file : FileUpload ,
626637 options: FileOptions ?
627638 ) async throws -> SignedURLUploadResponse {
628639 let options = options ?? defaultFileOptions
@@ -631,11 +642,8 @@ public class StorageFileApi: StorageApi, @unchecked Sendable {
631642 headers [ " x-upsert " ] = " \( options. upsert) "
632643 headers [ " duplex " ] = options. duplex
633644
634- if let metadata = options. metadata {
635- formData. append ( encodeMetadata ( metadata) , withName: " metadata " )
636- }
637-
638- formData. append ( options. cacheControl. data ( using: . utf8) !, withName: " cacheControl " )
645+ let formData = MultipartFormData ( )
646+ file. encode ( to: formData, withPath: path, options: options)
639647
640648 struct UploadResponse : Decodable {
641649 let Key : String
@@ -664,7 +672,8 @@ public class StorageFileApi: StorageApi, @unchecked Sendable {
664672
665673 private func _removeEmptyFolders( _ path: String ) -> String {
666674 let trimmedPath = path. trimmingCharacters ( in: CharacterSet ( charactersIn: " / " ) )
667- let cleanedPath = trimmedPath. replacingOccurrences ( of: " /+ " , with: " / " , options: . regularExpression)
675+ let cleanedPath = trimmedPath. replacingOccurrences (
676+ of: " /+ " , with: " / " , options: . regularExpression)
668677 return cleanedPath
669678 }
670679}
0 commit comments