From 967a9cee559870268fab48bfbd85c7692343d853 Mon Sep 17 00:00:00 2001 From: Guilherme Souza Date: Wed, 22 May 2024 16:23:09 -0300 Subject: [PATCH 1/2] feat(storage): fill content-type based on file extension --- Sources/Storage/Helpers.swift | 40 ++++++++++++++++++++++++++++ Sources/Storage/StorageFileApi.swift | 12 +++------ Sources/Storage/Types.swift | 7 +++-- 3 files changed, 47 insertions(+), 12 deletions(-) create mode 100644 Sources/Storage/Helpers.swift diff --git a/Sources/Storage/Helpers.swift b/Sources/Storage/Helpers.swift new file mode 100644 index 000000000..f5cbf269d --- /dev/null +++ b/Sources/Storage/Helpers.swift @@ -0,0 +1,40 @@ +// +// Helpers.swift +// +// +// Created by Guilherme Souza on 22/05/24. +// + +import CoreServices +import Foundation +import UniformTypeIdentifiers + +func mimeTypeForExtension(_ fileExtension: String) -> String { + if #available(macOS 11.0, iOS 14.0, watchOS 7.0, tvOS 14.0, visionOS 1.0, *) { + return UTType(filenameExtension: fileExtension)?.preferredMIMEType ?? "application/octet-stream" + } else { + guard + let type = UTTypeCreatePreferredIdentifierForTag( + kUTTagClassFilenameExtension, + fileExtension as NSString, + nil + )?.takeUnretainedValue(), + let mimeType = UTTypeCopyPreferredTagWithClass( + type, + kUTTagClassMIMEType + )?.takeUnretainedValue() + else { return "application/octet-stream" } + + return mimeType as String + } +} + +extension String { + var pathExtension: String { + (self as NSString).pathExtension + } + + var fileName: String { + (self as NSString).lastPathComponent + } +} diff --git a/Sources/Storage/StorageFileApi.swift b/Sources/Storage/StorageFileApi.swift index efce0b568..402e6b836 100644 --- a/Sources/Storage/StorageFileApi.swift +++ b/Sources/Storage/StorageFileApi.swift @@ -38,14 +38,14 @@ public class StorageFileApi: StorageApi { file: Data, options: FileOptions ) async throws -> FileUploadResponse { - let contentType = options.contentType + let contentType = options.contentType ?? mimeTypeForExtension(path.pathExtension) var headers = HTTPHeaders([ "x-upsert": "\(options.upsert)", ]) headers["duplex"] = options.duplex - let fileName = fileName(fromPath: path) + let fileName = path.fileName let form = FormData() form.append( @@ -472,13 +472,13 @@ public class StorageFileApi: StorageApi { file: Data, options: FileOptions = FileOptions() ) async throws -> SignedURLUploadResponse { - let contentType = options.contentType + let contentType = options.contentType ?? mimeTypeForExtension(path.pathExtension) var headers = HTTPHeaders([ "x-upsert": "\(options.upsert)", ]) headers["duplex"] = options.duplex - let fileName = fileName(fromPath: path) + let fileName = path.fileName let form = FormData() form.append(file: File( @@ -509,7 +509,3 @@ public class StorageFileApi: StorageApi { return SignedURLUploadResponse(path: path, fullPath: fullPath) } } - -private func fileName(fromPath path: String) -> String { - (path as NSString).lastPathComponent -} diff --git a/Sources/Storage/Types.swift b/Sources/Storage/Types.swift index 0e8872106..75bf7210f 100644 --- a/Sources/Storage/Types.swift +++ b/Sources/Storage/Types.swift @@ -44,9 +44,8 @@ public struct FileOptions: Sendable { /// in the `Cache-Control: max-age=` header. Defaults to 3600 seconds. public var cacheControl: String - /// The `Content-Type` header value. Should be specified if using a `fileBody` that is neither - /// `Blob` nor `File` nor `FormData`, otherwise will default to `text/plain;charset=UTF-8`. - public var contentType: String + /// The `Content-Type` header value. + public var contentType: String? /// When upsert is set to true, the file is overwritten if it exists. When set to false, an error /// is thrown if the object already exists. Defaults to false. @@ -59,7 +58,7 @@ public struct FileOptions: Sendable { public init( cacheControl: String = "3600", - contentType: String = "text/plain;charset=UTF-8", + contentType: String? = nil, upsert: Bool = false, duplex: String? = nil ) { From 2f2cfa88a040bfc5789829efcb030aa5c590256b Mon Sep 17 00:00:00 2001 From: Guilherme Souza Date: Wed, 22 May 2024 16:35:44 -0300 Subject: [PATCH 2/2] automatic mime type works only on Apple platforms --- Sources/Storage/Helpers.swift | 51 ++++++++++++++++++++++------------- 1 file changed, 32 insertions(+), 19 deletions(-) diff --git a/Sources/Storage/Helpers.swift b/Sources/Storage/Helpers.swift index f5cbf269d..3bbdf0d42 100644 --- a/Sources/Storage/Helpers.swift +++ b/Sources/Storage/Helpers.swift @@ -5,29 +5,42 @@ // Created by Guilherme Souza on 22/05/24. // -import CoreServices import Foundation -import UniformTypeIdentifiers -func mimeTypeForExtension(_ fileExtension: String) -> String { - if #available(macOS 11.0, iOS 14.0, watchOS 7.0, tvOS 14.0, visionOS 1.0, *) { - return UTType(filenameExtension: fileExtension)?.preferredMIMEType ?? "application/octet-stream" - } else { - guard - let type = UTTypeCreatePreferredIdentifierForTag( - kUTTagClassFilenameExtension, - fileExtension as NSString, - nil - )?.takeUnretainedValue(), - let mimeType = UTTypeCopyPreferredTagWithClass( - type, - kUTTagClassMIMEType - )?.takeUnretainedValue() - else { return "application/octet-stream" } +#if canImport(CoreServices) + import CoreServices +#endif - return mimeType as String +#if canImport(UniformTypeIdentifiers) + import UniformTypeIdentifiers +#endif + +#if os(Linux) || os(Windows) + /// On Linux or Windows this method always returns `application/octet-stream`. + func mimeTypeForExtension(_: String) -> String { + "application/octet-stream" } -} +#else + func mimeTypeForExtension(_ fileExtension: String) -> String { + if #available(macOS 11.0, iOS 14.0, watchOS 7.0, tvOS 14.0, visionOS 1.0, *) { + return UTType(filenameExtension: fileExtension)?.preferredMIMEType ?? "application/octet-stream" + } else { + guard + let type = UTTypeCreatePreferredIdentifierForTag( + kUTTagClassFilenameExtension, + fileExtension as NSString, + nil + )?.takeUnretainedValue(), + let mimeType = UTTypeCopyPreferredTagWithClass( + type, + kUTTagClassMIMEType + )?.takeUnretainedValue() + else { return "application/octet-stream" } + + return mimeType as String + } + } +#endif extension String { var pathExtension: String {