Skip to content

Commit 569f445

Browse files
authored
feat(storage): fill content-type based on file extension (#400)
1 parent db7a094 commit 569f445

File tree

3 files changed

+60
-12
lines changed

3 files changed

+60
-12
lines changed

Sources/Storage/Helpers.swift

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
//
2+
// Helpers.swift
3+
//
4+
//
5+
// Created by Guilherme Souza on 22/05/24.
6+
//
7+
8+
import Foundation
9+
10+
#if canImport(CoreServices)
11+
import CoreServices
12+
#endif
13+
14+
#if canImport(UniformTypeIdentifiers)
15+
import UniformTypeIdentifiers
16+
#endif
17+
18+
#if os(Linux) || os(Windows)
19+
/// On Linux or Windows this method always returns `application/octet-stream`.
20+
func mimeTypeForExtension(_: String) -> String {
21+
"application/octet-stream"
22+
}
23+
#else
24+
func mimeTypeForExtension(_ fileExtension: String) -> String {
25+
if #available(macOS 11.0, iOS 14.0, watchOS 7.0, tvOS 14.0, visionOS 1.0, *) {
26+
return UTType(filenameExtension: fileExtension)?.preferredMIMEType ?? "application/octet-stream"
27+
} else {
28+
guard
29+
let type = UTTypeCreatePreferredIdentifierForTag(
30+
kUTTagClassFilenameExtension,
31+
fileExtension as NSString,
32+
nil
33+
)?.takeUnretainedValue(),
34+
let mimeType = UTTypeCopyPreferredTagWithClass(
35+
type,
36+
kUTTagClassMIMEType
37+
)?.takeUnretainedValue()
38+
else { return "application/octet-stream" }
39+
40+
return mimeType as String
41+
}
42+
}
43+
#endif
44+
45+
extension String {
46+
var pathExtension: String {
47+
(self as NSString).pathExtension
48+
}
49+
50+
var fileName: String {
51+
(self as NSString).lastPathComponent
52+
}
53+
}

Sources/Storage/StorageFileApi.swift

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -38,14 +38,14 @@ public class StorageFileApi: StorageApi {
3838
file: Data,
3939
options: FileOptions
4040
) async throws -> FileUploadResponse {
41-
let contentType = options.contentType
41+
let contentType = options.contentType ?? mimeTypeForExtension(path.pathExtension)
4242
var headers = HTTPHeaders([
4343
"x-upsert": "\(options.upsert)",
4444
])
4545

4646
headers["duplex"] = options.duplex
4747

48-
let fileName = fileName(fromPath: path)
48+
let fileName = path.fileName
4949

5050
let form = FormData()
5151
form.append(
@@ -474,13 +474,13 @@ public class StorageFileApi: StorageApi {
474474
file: Data,
475475
options: FileOptions = FileOptions()
476476
) async throws -> SignedURLUploadResponse {
477-
let contentType = options.contentType
477+
let contentType = options.contentType ?? mimeTypeForExtension(path.pathExtension)
478478
var headers = HTTPHeaders([
479479
"x-upsert": "\(options.upsert)",
480480
])
481481
headers["duplex"] = options.duplex
482482

483-
let fileName = fileName(fromPath: path)
483+
let fileName = path.fileName
484484

485485
let form = FormData()
486486
form.append(file: File(
@@ -511,7 +511,3 @@ public class StorageFileApi: StorageApi {
511511
return SignedURLUploadResponse(path: path, fullPath: fullPath)
512512
}
513513
}
514-
515-
private func fileName(fromPath path: String) -> String {
516-
(path as NSString).lastPathComponent
517-
}

Sources/Storage/Types.swift

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -44,9 +44,8 @@ public struct FileOptions: Sendable {
4444
/// in the `Cache-Control: max-age=<seconds>` header. Defaults to 3600 seconds.
4545
public var cacheControl: String
4646

47-
/// The `Content-Type` header value. Should be specified if using a `fileBody` that is neither
48-
/// `Blob` nor `File` nor `FormData`, otherwise will default to `text/plain;charset=UTF-8`.
49-
public var contentType: String
47+
/// The `Content-Type` header value.
48+
public var contentType: String?
5049

5150
/// When upsert is set to true, the file is overwritten if it exists. When set to false, an error
5251
/// is thrown if the object already exists. Defaults to false.
@@ -59,7 +58,7 @@ public struct FileOptions: Sendable {
5958

6059
public init(
6160
cacheControl: String = "3600",
62-
contentType: String = "text/plain;charset=UTF-8",
61+
contentType: String? = nil,
6362
upsert: Bool = false,
6463
duplex: String? = nil
6564
) {

0 commit comments

Comments
 (0)