From c13c81f36d16ecce074022f6fcfdd46bd0d9b6a4 Mon Sep 17 00:00:00 2001 From: Salakar Date: Sat, 7 Jul 2018 03:10:35 +0100 Subject: [PATCH] [storage][ios] fix automatic mime/content type detection for storage meta --- .../firebase/storage/RNFirebaseStorage.java | 8 +- bridge/ios/Podfile.lock | 74 ++++++++---- bridge/ios/testing.xcodeproj/project.pbxproj | 16 --- ios/RNFirebase/storage/RNFirebaseStorage.m | 106 ++++-------------- 4 files changed, 82 insertions(+), 122 deletions(-) diff --git a/android/src/main/java/io/invertase/firebase/storage/RNFirebaseStorage.java b/android/src/main/java/io/invertase/firebase/storage/RNFirebaseStorage.java index 3185f7d4..d87d737d 100644 --- a/android/src/main/java/io/invertase/firebase/storage/RNFirebaseStorage.java +++ b/android/src/main/java/io/invertase/firebase/storage/RNFirebaseStorage.java @@ -601,12 +601,10 @@ public class RNFirebaseStorage extends ReactContextBaseJavaModule { */ private void sendJSEvent(String appName, final String name, final String path, WritableMap body) { WritableMap event = Arguments.createMap(); - - event.putString("appName", appName); - event.putString("eventName", name); - event.putString("path", path); event.putMap("body", body); - + event.putString("path", path); + event.putString("eventName", name); + event.putString("appName", appName); Utils.sendEvent(this.getReactApplicationContext(), STORAGE_EVENT, event); } diff --git a/bridge/ios/Podfile.lock b/bridge/ios/Podfile.lock index a10d0ce6..da0abb4e 100644 --- a/bridge/ios/Podfile.lock +++ b/bridge/ios/Podfile.lock @@ -55,18 +55,18 @@ PODS: - FirebaseAnalytics (5.0.1): - FirebaseCore (~> 5.0) - FirebaseInstanceID (~> 3.0) - - GoogleToolboxForMac/NSData+zlib (~> 2.1) + - "GoogleToolboxForMac/NSData+zlib (~> 2.1)" - nanopb (~> 0.3) - FirebaseAuth (5.0.1): - FirebaseCore (~> 5.0) - GTMSessionFetcher/Core (~> 1.1) - FirebaseCore (5.0.4): - - GoogleToolboxForMac/NSData+zlib (~> 2.1) + - "GoogleToolboxForMac/NSData+zlib (~> 2.1)" - FirebaseCrash (3.0.0): - FirebaseAnalytics (~> 5.0) - FirebaseInstanceID (~> 3.0) - GoogleToolboxForMac/Logger (~> 2.1) - - GoogleToolboxForMac/NSData+zlib (~> 2.1) + - "GoogleToolboxForMac/NSData+zlib (~> 2.1)" - Protobuf (~> 3.5) - FirebaseDatabase (5.0.1): - FirebaseCore (~> 5.0) @@ -95,8 +95,8 @@ PODS: - GoogleAPIClientForREST (~> 1.0) - GoogleSignIn (~> 4.1) - GoogleToolboxForMac/Logger (~> 2.1) - - GoogleToolboxForMac/NSDictionary+URLArguments (~> 2.1) - - GoogleToolboxForMac/NSString+URLArguments (~> 2.1) + - "GoogleToolboxForMac/NSDictionary+URLArguments (~> 2.1)" + - "GoogleToolboxForMac/NSString+URLArguments (~> 2.1)" - GoogleToolboxForMac/StringEncoding (~> 2.1) - GoogleToolboxForMac/URLBuilder (~> 2.1) - GTMOAuth2 (~> 1.0) @@ -114,7 +114,7 @@ PODS: - FirebaseSwizzlingUtilities/ISASwizzling (~> 2.0) - FirebaseSwizzlingUtilities/MethodSwizzling (~> 2.0) - GoogleToolboxForMac/Logger (~> 2.1) - - GoogleToolboxForMac/NSData+zlib (~> 2.1) + - "GoogleToolboxForMac/NSData+zlib (~> 2.1)" - GTMSessionFetcher/Core (~> 1.1) - Protobuf (~> 3.5) - FirebaseRemoteConfig (3.0.0): @@ -122,7 +122,7 @@ PODS: - FirebaseAnalytics (~> 5.0) - FirebaseCore (~> 5.0) - FirebaseInstanceID (~> 3.0) - - GoogleToolboxForMac/NSData+zlib (~> 2.1) + - "GoogleToolboxForMac/NSData+zlib (~> 2.1)" - Protobuf (~> 3.5) - FirebaseStorage (3.0.0): - FirebaseCore (~> 5.0) @@ -137,8 +137,8 @@ PODS: - GoogleAPIClientForREST/Core (1.3.4): - GTMSessionFetcher (>= 1.1.7) - GoogleSignIn (4.1.2): - - GoogleToolboxForMac/NSDictionary+URLArguments (~> 2.1) - - GoogleToolboxForMac/NSString+URLArguments (~> 2.1) + - "GoogleToolboxForMac/NSDictionary+URLArguments (~> 2.1)" + - "GoogleToolboxForMac/NSString+URLArguments (~> 2.1)" - GTMOAuth2 (~> 1.0) - GTMSessionFetcher/Core (~> 1.1) - GoogleToolboxForMac/Core (2.1.4): @@ -148,20 +148,20 @@ PODS: - GoogleToolboxForMac/Defines (2.1.4) - GoogleToolboxForMac/Logger (2.1.4): - GoogleToolboxForMac/Defines (= 2.1.4) - - GoogleToolboxForMac/NSData+zlib (2.1.4): + - "GoogleToolboxForMac/NSData+zlib (2.1.4)": - GoogleToolboxForMac/Defines (= 2.1.4) - - GoogleToolboxForMac/NSDictionary+URLArguments (2.1.4): + - "GoogleToolboxForMac/NSDictionary+URLArguments (2.1.4)": - GoogleToolboxForMac/DebugUtils (= 2.1.4) - GoogleToolboxForMac/Defines (= 2.1.4) - - GoogleToolboxForMac/NSString+URLArguments (= 2.1.4) - - GoogleToolboxForMac/NSString+URLArguments (2.1.4) + - "GoogleToolboxForMac/NSString+URLArguments (= 2.1.4)" + - "GoogleToolboxForMac/NSString+URLArguments (2.1.4)" - GoogleToolboxForMac/StringEncoding (2.1.4): - GoogleToolboxForMac/Defines (= 2.1.4) - GoogleToolboxForMac/URLBuilder (2.1.4): - GoogleToolboxForMac/Core (= 2.1.4) - GoogleToolboxForMac/Defines (= 2.1.4) - - GoogleToolboxForMac/NSDictionary+URLArguments (= 2.1.4) - - GoogleToolboxForMac/NSString+URLArguments (= 2.1.4) + - "GoogleToolboxForMac/NSDictionary+URLArguments (= 2.1.4)" + - "GoogleToolboxForMac/NSString+URLArguments (= 2.1.4)" - gRPC (1.12.0): - gRPC-RxLibrary (= 1.12.0) - gRPC/Main (= 1.12.0) @@ -238,13 +238,49 @@ DEPENDENCIES: - RNFirebase (from `../../ios/RNFirebase.podspec`) - yoga (from `../node_modules/react-native/ReactCommon/yoga`) +SPEC REPOS: + https://github.com/cocoapods/specs.git: + - BoringSSL + - Crashlytics + - Fabric + - Firebase + - FirebaseABTesting + - FirebaseAnalytics + - FirebaseAuth + - FirebaseCore + - FirebaseCrash + - FirebaseDatabase + - FirebaseDynamicLinks + - FirebaseFirestore + - FirebaseFunctions + - FirebaseInstanceID + - FirebaseInvites + - FirebaseMessaging + - FirebasePerformance + - FirebaseRemoteConfig + - FirebaseStorage + - FirebaseSwizzlingUtilities + - Google-Mobile-Ads-SDK + - GoogleAPIClientForREST + - GoogleSignIn + - GoogleToolboxForMac + - gRPC + - gRPC-Core + - gRPC-ProtoRPC + - gRPC-RxLibrary + - GTMOAuth2 + - GTMSessionFetcher + - leveldb-library + - nanopb + - Protobuf + EXTERNAL SOURCES: React: - :path: ../node_modules/react-native + :path: "../node_modules/react-native" RNFirebase: - :path: ../../ios/RNFirebase.podspec + :path: "../../ios/RNFirebase.podspec" yoga: - :path: ../node_modules/react-native/ReactCommon/yoga + :path: "../node_modules/react-native/ReactCommon/yoga" SPEC CHECKSUMS: BoringSSL: cf3f1793eb6e3c445c4d150456341f149c268a35 @@ -286,4 +322,4 @@ SPEC CHECKSUMS: PODFILE CHECKSUM: 582ceaad051470812ad9203e13b5ea8ad20c78ac -COCOAPODS: 1.4.0 +COCOAPODS: 1.5.3 diff --git a/bridge/ios/testing.xcodeproj/project.pbxproj b/bridge/ios/testing.xcodeproj/project.pbxproj index ae6883f8..8b2913cd 100644 --- a/bridge/ios/testing.xcodeproj/project.pbxproj +++ b/bridge/ios/testing.xcodeproj/project.pbxproj @@ -595,7 +595,6 @@ 00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */, CC0F353E1D461097008BB94F /* Embed Frameworks */, 7D57265F10EEF7CD92D7973F /* Copy Detox Framework */, - 4EEB648E968FA6F093B42978 /* [CP] Embed Pods Frameworks */, CC8D61273332DE7812CFF010 /* [CP] Copy Pods Resources */, ); buildRules = ( @@ -971,21 +970,6 @@ shellPath = /bin/sh; shellScript = "export NODE_BINARY=node\n../node_modules/react-native/scripts/react-native-xcode.sh"; }; - 4EEB648E968FA6F093B42978 /* [CP] Embed Pods Frameworks */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - ); - name = "[CP] Embed Pods Frameworks"; - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-testing/Pods-testing-frameworks.sh\"\n"; - showEnvVarsInLog = 0; - }; 7D57265F10EEF7CD92D7973F /* Copy Detox Framework */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; diff --git a/ios/RNFirebase/storage/RNFirebaseStorage.m b/ios/RNFirebase/storage/RNFirebaseStorage.m index 8b470d44..2c00b898 100644 --- a/ios/RNFirebase/storage/RNFirebaseStorage.m +++ b/ios/RNFirebase/storage/RNFirebaseStorage.m @@ -209,91 +209,33 @@ RCT_EXPORT_METHOD(putFile:(NSString *) appDisplayName metadata:(NSDictionary *) metadata resolver:(RCTPromiseResolveBlock) resolve rejecter:(RCTPromiseRejectBlock) reject) { - FIRStorageMetadata *firmetadata = [self buildMetadataFromMap:metadata]; - if ([localPath hasPrefix:@"assets-library://"] || [localPath hasPrefix:@"ph://"]) { - PHFetchResult *assets; - - if ([localPath hasPrefix:@"assets-library://"]) { - NSURL *localFile = [[NSURL alloc] initWithString:localPath]; - assets = [PHAsset fetchAssetsWithALAssetURLs:@[localFile] options:nil]; - } else { - NSString *assetId = [localPath substringFromIndex:@"ph://".length]; - assets = [PHAsset fetchAssetsWithLocalIdentifiers:@[assetId] options:nil]; - } - - PHAsset *asset = [assets firstObject]; - - // this is based on http://stackoverflow.com/questions/35241449 - if (asset.mediaType == PHAssetMediaTypeImage) { - // images - PHImageRequestOptions *options = [PHImageRequestOptions new]; - options.networkAccessAllowed = true; - [[PHImageManager defaultManager] requestImageDataForAsset:asset options:options resultHandler:^(NSData *imageData, NSString *dataUTI, UIImageOrientation orientation, NSDictionary *info) { - if (info[PHImageErrorKey] == nil) { - if (UTTypeConformsTo((__bridge CFStringRef)dataUTI, kUTTypeJPEG)) { - firmetadata.contentType = [self utiToMimeType:dataUTI]; - [self uploadData:appDisplayName data:imageData firmetadata:firmetadata path:path resolver:resolve rejecter:reject]; - } else { - // if the image UTI is not JPEG then convert to JPEG, e.g. HEI - CGImageSourceRef source = CGImageSourceCreateWithData((__bridge CFDataRef)imageData, NULL); - NSDictionary *imageInfo = (__bridge NSDictionary*)CGImageSourceCopyPropertiesAtIndex(source, 0, NULL); - NSDictionary *imageMetadata = [imageInfo copy]; - NSMutableData *imageDataJPEG = [NSMutableData data]; - CGImageDestinationRef destination = CGImageDestinationCreateWithData((__bridge CFMutableDataRef)imageDataJPEG, kUTTypeJPEG, 1, NULL); - CGImageDestinationAddImageFromSource(destination, source, 0, (__bridge CFDictionaryRef)imageMetadata); - CGImageDestinationFinalize(destination); - // Manually set mimetype to JPEG - firmetadata.contentType = @"image/jpeg"; - [self uploadData:appDisplayName data:[NSData dataWithData:imageDataJPEG] firmetadata:firmetadata path:path resolver:resolve rejecter:reject]; - } - } else { - reject(@"storage/request-image-data-failed", @"Could not obtain image data for the specified file.", nil); - } - }]; - } else if (asset.mediaType == PHAssetMediaTypeVideo) { - // video - PHVideoRequestOptions *options = [PHVideoRequestOptions new]; - options.networkAccessAllowed = true; - [[PHImageManager defaultManager] requestExportSessionForVideo:asset options:options exportPreset:AVAssetExportPresetHighestQuality resultHandler:^(AVAssetExportSession *_Nullable exportSession, NSDictionary *_Nullable info) { - if (info[PHImageErrorKey] == nil) { - NSURL *tempUrl = [self temporaryFileUrl]; - exportSession.outputURL = tempUrl; - - NSArray *resources = [PHAssetResource assetResourcesForAsset:asset]; - for (PHAssetResource *resource in resources) { - exportSession.outputFileType = resource.uniformTypeIdentifier; - if (exportSession.outputFileType != nil) break; - } - - [exportSession exportAsynchronouslyWithCompletionHandler:^{ - if (exportSession.status == AVAssetExportSessionStatusCompleted) { - firmetadata.contentType = [self utiToMimeType:exportSession.outputFileType]; - [self uploadFile:appDisplayName url:tempUrl firmetadata:firmetadata path:path resolver:resolve rejecter:reject]; - // we're not cleaning up the temporary file at the moment, just relying on the OS to do that in it's own time - todo? - } else { - reject(@"storage/temporary-file-failure", @"Unable to create temporary file for upload.", nil); - } - }]; - } else { - reject(@"storage/export-session-failure", @"Unable to create export session for asset.", nil); - } - }]; - } - } else { - // TODO: Content type for file? - NSData *data = [[NSFileManager defaultManager] contentsAtPath:localPath]; - [self uploadData:appDisplayName data:data firmetadata:firmetadata path:path resolver:resolve rejecter:reject]; + if (![[NSFileManager defaultManager] fileExistsAtPath:localPath]) { + reject(@"storage/file-not-found", @"File specified at path does not exist.", nil); + return; } - + + NSData *data = [NSData dataWithContentsOfFile:localPath]; + FIRStorageMetadata *firmetadata = [self buildMetadataFromMap:metadata]; + + if ([firmetadata valueForKey:@"contentType"] == nil) { + firmetadata.contentType = [self mimeTypeForPath:localPath]; + } + + // TODO convert heic -> jpeg only if users asks for it + + [self uploadData:appDisplayName data:data firmetadata:firmetadata path:path resolver:resolve rejecter:reject]; } -- (NSURL *)temporaryFileUrl { - NSString *filename = [NSString stringWithFormat:@"%@.tmp", [[NSProcessInfo processInfo] globallyUniqueString]]; - return [[NSURL fileURLWithPath:NSTemporaryDirectory()] URLByAppendingPathComponent:filename]; -} - -- (NSString *)utiToMimeType:(NSString *) dataUTI { - return (__bridge_transfer NSString *)UTTypeCopyPreferredTagWithClass((__bridge CFStringRef)dataUTI, kUTTagClassMIMEType); +- (NSString*) mimeTypeForPath: (NSString *) path { + CFStringRef UTI = UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, (__bridge CFStringRef)[path pathExtension], NULL); + CFStringRef mimeType = UTTypeCopyPreferredTagWithClass (UTI, kUTTagClassMIMEType); + CFRelease(UTI); + + if (!mimeType) { + return @"application/octet-stream"; + } + + return (__bridge_transfer NSString *) mimeType; } - (void)uploadFile:(NSString *)appDisplayName url:(NSURL *)url firmetadata:(FIRStorageMetadata *)firmetadata path:(NSString *)path resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject {