Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We鈥檒l occasionally send you account related emails.

Already on GitHub? Sign in to your account

[馃悰] Storage putFile doesn't honor contentType metadata on iOS Simulator #5846

Closed
1 of 10 tasks
gewfy opened this issue Nov 12, 2021 · 13 comments
Closed
1 of 10 tasks
Labels
Impact: Bug New bug report Platform: iOS Service: Storage Firebase Cloud Storage Type: Stale Issue has become stale - automatically added by Stale bot Workflow: Needs Review Pending feedback or review from a maintainer. Workflow: Waiting for User Response Blocked waiting for user response.

Comments

@gewfy
Copy link
Contributor

gewfy commented Nov 12, 2021

Issue

When uploading a jpeg with...

storage().ref(...).putFile(localFilePath)

...the uploaded file gets the wrong contentType. Trying to set the contentType explicitly with...

storage().ref(...).putFile(localFilePath, { contentType: 'image/jpeg' })

...is not working either.

This worked in @react-native-firebase/storage@12.7.3 but not in 13.0.1.

What do work is to upload as a base64 string with the contentType explicitly set:

storage().ref(...).putString(data, 'base64', { contentType: 'image/jpeg' })

I didn't provide any more details below as it doesn't seem relevant. But let me know and I'll provide more info.

Also want to take the opportunity to thank you for this massive library! Especially the speed and quality of each release. It makes me happy every time I use or upgrade it and really sets a standard in the react native community 馃檶


Project Files

Javascript

Click To Expand

package.json:

# N/A

firebase.json for react-native-firebase v6:

# N/A

iOS

Click To Expand

ios/Podfile:

  • I'm not using Pods
  • I'm using Pods and my Podfile looks like:
# N/A

AppDelegate.m:

// N/A


Android

Click To Expand

Have you converted to AndroidX?

  • my application is an AndroidX application?
  • I am using android/gradle.settings jetifier=true for Android compatibility?
  • I am using the NPM package jetifier for react-native compatibility?

android/build.gradle:

// N/A

android/app/build.gradle:

// N/A

android/settings.gradle:

// N/A

MainApplication.java:

// N/A

AndroidManifest.xml:

<!-- N/A -->


Environment

Click To Expand

react-native info output:

 OUTPUT GOES HERE
  • Platform that you're experiencing the issue on:
    • iOS
    • Android
    • iOS but have not tested behavior on Android
    • Android but have not tested behavior on iOS
    • Both
  • react-native-firebase version you're using that has this issue:
    • e.g. 5.4.3
  • Firebase module(s) you're using that has the issue:
    • e.g. Instance ID
  • Are you using TypeScript?
    • Y/N & VERSION


@gewfy gewfy added Help: Needs Triage Issue needs additional investigation/triaging. Impact: Bug New bug report labels Nov 12, 2021
@mikehardy
Copy link
Collaborator

Hey, thanks for the thanks ;-), we really do try.

I agree with you that you appear to have provided enough of the relevant details, at least for now, with the execution environment where you see these results - is it iOS Simulator going against storage emulator, or iOS real device against live storage etc. That could be informative

Initial thoughts - really suspicious that this happened across versions here, since I know we haven't changed the storage code, examine how static this package has been since adding emulator support - just releases and version bumps for gradle really https://github.com/invertase/react-native-firebase/commits/main/packages/storage

So what does change across releases? The firebase-ios-sdk version.

v12.7.3 here was on firebase-ios-sdk 8.6.0
v13.0.1 here is on firebase-ios-sdk 8.9.1

Are you maybe overriding the version like so: https://rnfirebase.io/#ios ?

If you are not, then the most important first test here is, what happens when you override your version back to firebase-ios-sdk 8.6.0 - does it work again? If it does, what version stops working?

I'm not able to find any issues upstream (yet) https://github.com/firebase/firebase-ios-sdk/issues?q=is%3Aissue+contentType so you might be first here

I definitely remember while implementing storage emulator that it behaved incorrectly between firebase-ios-sdk and firebase storage emulator, specifically:

if (device.getPlatform() === 'android') {
// FIXME on iOS this is 'application/octet-stream'?
metadata.contentType.should.equal('image/jpeg');
}

It is still outstanding upstream: firebase/firebase-tools#3398

@mikehardy mikehardy added Workflow: Waiting for User Response Blocked waiting for user response. Platform: iOS Service: Storage Firebase Cloud Storage labels Nov 12, 2021
@gewfy
Copy link
Contributor Author

gewfy commented Nov 13, 2021

Oh now that you say it I remember that this is only an issue in the iOS simulator. However I don't use the storage emulator, so same issue against a live firebase environment. I just built a production build and there it works as expected.

To answer your questions. It probably behaved exactly the same way in 12.7.3, but I compared with the prod build and that's why I thought it stopped working. No version overrides.

So basically it boils down to an iOS simulator only issue :)

@mikehardy
Copy link
Collaborator

Okay - I'm going to close this as upstream with no action here sadly, and rearrange the title just a little bit for future searchers, hopefully the upstream bug gets attention soon, I'll drop a ping on that as well. Good luck on your project!

@mikehardy mikehardy added Blocked: Firebase SDK Pending a confirmed fix landing on the official native sdk's (iOS/Android). and removed Workflow: Waiting for User Response Blocked waiting for user response. Help: Needs Triage Issue needs additional investigation/triaging. Service: Storage Firebase Cloud Storage labels Nov 13, 2021
@mikehardy mikehardy changed the title [馃悰] Storage putFile doesn't honor contentType metadata on iOS [馃悰] Storage putFile doesn't honor contentType metadata on iOS Simulator Nov 13, 2021
@gewfy
Copy link
Contributor Author

gewfy commented Nov 13, 2021

馃挴 % reasonable! Thanks for the triage

@sneh-lio
Copy link

sneh-lio commented Feb 4, 2022

@mikehardy I think this is a valid issue.
As per this line; contentType from metadata is ignored and no matter what, it is reassigned by the contentType returned by iOS's detected mimeType

@mikehardy
Copy link
Collaborator

@sneh-lio when I re-read the code you linked, what I read is that it is inside the completion handler for the firebase-ios-sdk storage module, that is, the contentType there is what is provided from firebase-ios-sdk as it calls back in to our code on completion. What do you see when you look at the firebase-ios-sdk code that executes, determines the contentType, and then provides that value as a parameter in the callback?

@sneh-lio
Copy link

sneh-lio commented Feb 4, 2022

@mikehardy The thing is when I specify contentType as "application/pdf" for a audio file then for Android it will be overwritten as a pdf file on storage bucket but for iOS it will be uploaded as the type detected by iOS sdk!
So, its a strange pattern for both the platforms! It should be unified.
The point here is on iOS it doesn't honour the contentType provided in metadata.

@mikehardy
Copy link
Collaborator

Understood. How does it look inside firebase-ios-sdk? It's Open source, and the code there calls this completion handler, populating the variable I think.

@sneh-lio
Copy link

sneh-lio commented Feb 5, 2022

@mikehardy I checked the firebase-ios-sdk, in their implementation they don't overwrite the contentType provided in metadata but in the react-native-firebase/storage implemetation, contentType provided is overwritten by completion handler dishonouring the contentType provided by developer.

If you can confirm that I am correct, then I can create a PR!

@mikehardy
Copy link
Collaborator

Thanks for persisting @sneh-lio - I think you are on to something here. I had more time and followed the callchain through, it appears that completion handler is not called from firebase-ios-sdk, that was an error in my mental model of how the control flow went.

It appears that is just the internal method here in this module that does some pre-processing before it calls in to firebase-ios-sdk

It appears that it attempts to get a mime type from the file here

[RNFBStorageCommon mimeTypeForPath:localFilePath]);
and that's the value that is sent through as a parameter to the completion handler

Then on the line you posted originally, it is taking that value no matter what:

storageMetadata.contentType = contentType;

Then - the interesting part to me - the immediately following lines appear like they would take the provided metadata from the caller (your project code...) and use them if the metadata dictionary contentType weren't set already:

if ([storageMetadata valueForKey:@"contentType"] == nil) {
storageMetadata.contentType =
[RNFBStorageCommon mimeTypeForPath:localFilePath];
}

...but of course because of line 432 just above, the metadata dictionary contentType is always set, so the project-specified contentType is never used.

So yes! I think you have uncovered and correctly diagnosed an error and I think removing line 432 is safe. However, I think if the project-specific code does not provide a contentType, (so we enter the "content type is not set in the metadata dictionary" conditional) then rather than call the exact same method ([RNFBStorageCommon mimeTypeForPath:localFilePath]) that was called already with the result provided as "contentType" parameter - we should then put the line that was line 432 - just directly setting it there with the value of the parameter, as the fallback to make sure the metadata dictionary has a content type.

What do you think? If that sounds right and was what you were thinking, please do raise a PR! It would be most excellent if the relevant E2E storage tests were updated / extended to probe this case since it appears like an obvious error now that you've unmasked it, but the tests are currently passing and giving us a false positive https://github.com/invertase/react-native-firebase/blob/main/tests/README.md /

uploadTaskSnapshot.metadata.should.be.an.Object();
-- a stricter assertion there perhaps - to assert on the actual type vs just "is an object"

@mikehardy mikehardy reopened this Feb 5, 2022
@mikehardy mikehardy added Service: Storage Firebase Cloud Storage Workflow: Needs Review Pending feedback or review from a maintainer. Workflow: Waiting for User Response Blocked waiting for user response. and removed Blocked: Firebase SDK Pending a confirmed fix landing on the official native sdk's (iOS/Android). labels Feb 5, 2022
@stale
Copy link

stale bot commented Apr 16, 2022

Hello 馃憢, to help manage issues we automatically close stale issues.
This issue has been automatically marked as stale because it has not had activity for quite some time. Has this issue been fixed, or does it still require the community's attention?

This issue will be closed in 15 days if no further activity occurs.
Thank you for your contributions.

@stale stale bot added the Type: Stale Issue has become stale - automatically added by Stale bot label Apr 16, 2022
@stale
Copy link

stale bot commented May 1, 2022

Closing this issue after a prolonged period of inactivity. If this is still present in the latest release, please feel free to create a new issue with up-to-date information.

@stale stale bot closed this as completed May 1, 2022
@VictorioMolina
Copy link
Contributor

VictorioMolina commented Jul 10, 2023

@mikehardy In my case, doing:

export async function uploadAudioToStorage(audioUri, storageFolder) {
  const blob = await uriToBlob(audioUri);

  let audioId = blob._data.blobId;

  const fileType = getFileType(audioUri);

  // Add the file's extension to the audio id
  audioId += `.${fileType}`;

  const storageRef = storage()
    .ref(storageFolder)
    .child(audioId);

  await storageRef.put(blob, { contentType: "audio/m4a" });

  blob.close();

  return audioId;
}

Uploads a broken file with correct mime type to Firebase Storage. This code worked good with Firebase JS SDK.

If instead, I do the following, everything goes ok, but the mime type is "audio/x-m4a":

await storageRef.put(blob); <--- Content type not declared

Then, I try with the new MIME type:

await storageRef.put(blob, { contentType: "audio/x-m4a" }聽);

And the stored file is broken again.

I am using @react-native-firebase/storage v18.1.0

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Impact: Bug New bug report Platform: iOS Service: Storage Firebase Cloud Storage Type: Stale Issue has become stale - automatically added by Stale bot Workflow: Needs Review Pending feedback or review from a maintainer. Workflow: Waiting for User Response Blocked waiting for user response.
Projects
None yet
Development

No branches or pull requests

4 participants