StorageUtils.swift 3.3 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091
  1. // Copyright 2022 Google LLC
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. import Foundation
  15. #if os(iOS) || os(tvOS) || os(visionOS)
  16. import MobileCoreServices
  17. #elseif os(macOS) || os(watchOS)
  18. import CoreServices
  19. #endif // os(iOS) || os(tvOS)
  20. @available(iOS 13, tvOS 13, macOS 10.15, macCatalyst 13, watchOS 7, *)
  21. class StorageUtils {
  22. class func defaultRequestForReference(reference: StorageReference,
  23. queryParams: [String: String]? = nil)
  24. -> URLRequest {
  25. var components = URLComponents()
  26. components.scheme = reference.storage.scheme
  27. components.host = reference.storage.host
  28. components.port = reference.storage.port
  29. if let queryParams {
  30. var queryItems = [URLQueryItem]()
  31. for (key, value) in queryParams {
  32. queryItems.append(URLQueryItem(name: key, value: value))
  33. }
  34. components.queryItems = queryItems
  35. // NSURLComponents does not encode "+" as "%2B". This is however required by our backend, as
  36. // it treats "+" as a shorthand encoding for spaces. See also
  37. // https://stackoverflow.com/questions/31577188/how-to-encode-into-2b-with-nsurlcomponents
  38. components.percentEncodedQuery = components.percentEncodedQuery?.replacingOccurrences(
  39. of: "+",
  40. with: "%2B"
  41. )
  42. }
  43. let encodedPath = encodedURL(for: reference.path)
  44. components.percentEncodedPath = encodedPath
  45. guard let url = components.url else {
  46. fatalError("FirebaseStorage Internal Error: Failed to create url for \(reference.bucket)")
  47. }
  48. return URLRequest(url: url)
  49. }
  50. class func encodedURL(for path: StoragePath) -> String {
  51. let bucketString = "/b/\(GCSEscapedString(path.bucket))"
  52. var objectString: String
  53. if let objectName = path.object {
  54. objectString = "/o/\(GCSEscapedString(objectName))"
  55. } else {
  56. objectString = "/o"
  57. }
  58. return "/v0\(bucketString)\(objectString)"
  59. }
  60. class func GCSEscapedString(_ string: String) -> String {
  61. // This is the list at https://cloud.google.com/storage/docs/json_api/ without &, ; and +.
  62. let allowedSet =
  63. CharacterSet(
  64. charactersIn: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~!$'()*,=:@"
  65. )
  66. return string.addingPercentEncoding(withAllowedCharacters: allowedSet)!
  67. }
  68. class func MIMETypeForExtension(_ fileExtension: String?) -> String {
  69. guard let fileExtension = fileExtension else {
  70. return "application/octet-stream"
  71. }
  72. if let type = UTTypeCreatePreferredIdentifierForTag(
  73. kUTTagClassFilenameExtension,
  74. fileExtension as NSString,
  75. nil
  76. )?.takeRetainedValue() {
  77. if let mimeType = UTTypeCopyPreferredTagWithClass(type, kUTTagClassMIMEType)?
  78. .takeRetainedValue() {
  79. return mimeType as String
  80. }
  81. }
  82. return "application/octet-stream"
  83. }
  84. }