Incorrect Mimetype for profile_file Upload - JavaScript SDK

Hi all, looks like this hasn’t been asked yet, so here it is. I’m trying to set a profile picture via the sendbird JavaScript SDK like so:

const mimeType = "image/png";
const url = `data:${mimeType};base64,${myBase64string}`;
const data = await fetch(url);
const blob = await data.blob();
const file = new File([blob], sendbirdId, {type: mimeType});
  
// upload to sendbird:
sendbird.connect(sendbirdId, () => {
     sendbird.updateCurrentUserInfoWithProfileImage(firstName, file);
})

But sendbird is complaining:

SendBirdException: Invalid value: "Profile file mimetype".

Is “image/png” not an acceptable mimetype? Any assistance here would be greatly appreciated!

Also if i can avoid this reconversion, I already have the file I want in base64 format, can I use that directly?

Thanks!

Hi @fullStackChris,

Welcome to the Sendbird Community.

I did a bit of testing around this and it looks like when we process the file you sent, we parse out the sendbirdId and use it to assume the mimeType. In this case that fails because the userId does not contain any indication of what mimetype it is.

You could try replacing the sendbirdId with the actual file name+extension.
You can reference mimetypes — Map filenames to MIME types — Python 3.9.7 documentation to understand how we’re determining mimeType for this method.

I should note that when I passed in the file directly from the browser, it worked as expected. Though in your case since you have it in base64, you would need to convert it.

Hi Tyler,

Do you have a code snippet of what you mean? It doesn’t make much sense to me that the field labeled as ‘userId’ in the typings should instead contain a mimetype. And by “passing in a file directly” you mean the File API right? (File() - Web APIs | MDN)

The challenge here is that react native isn’t really a dom or web environment, so things like base64 conversion or the File API are technically unavailable - though perhaps technically possible with some hacks.

Hey Chris,

If you take a look at the link I supplied in my last post, you’ll see how Python is using this mimetypes.guess_type() method. In our case, we take your file and pass it in as a string such like str(file). So when you’re passing in the sendbirdId, which you’re saying is the name of the File(), then fails out because there is nothing to infer the type. I did some testing, and if you append .png to the end of the sendbirdId, it works. Here is an example:

const file = new File([blob], `${sendbirdId}.png`, { type: 'image/png' });

When I referenced passing the file directly, I am using React in Chrome, as such:

<input
  type="file"
  ref={inputRef}
  multiple
  onChange={handleAttachChange}
  className="channelMessageInputAttachInput"
 ></input>

And then my handleAttachChange is:

const handleAttachChange = async (event) => {
      sb.updateCurrentUserInfoWithProfileImage('Tyler Hammer', event.target.files[0]);
}

So you can see I’m just directly passing the file I pulled from the file explorer.

Unfortunately, I don’t have access to the file API in a React Native app. I’ve tried a new image picker library and I tried sending all the following fields:

{
  "creationDate": "1630587230",
  "cropRect": null,
  "data": null,
  "duration": null,
  "exif": null,
  "filename": "IMG_0009.PNG",
  "height": 560,
  "localIdentifier": "526359F6-AC0A-4BA3-8C49-BEAB04908198/L0/001",
  "mime": "image/jpeg",
  "modificationDate": "1630587230",
  "path": "/Users/chris/Library/Developer/CoreSimulator/Devices/1ECF2194-D7D1-4623-8F68-06C590543B0B/data/Containers/Data/Application/7E846684-0E9F-40B5-B20E-1F9453FAA31D/tmp/react-native-image-crop-picker/054E14BE-9F23-48EB-B8F3-A140BDD5A521.jpg",
  "size": 33268,
  "sourceURL": "file:///Users/chris/Library/Developer/CoreSimulator/Devices/1ECF2194-D7D1-4623-8F68-06C590543B0B/data/Media/DCIM/100APPLE/IMG_0009.PNG",
  "width": 528
}

But now I get:

SendBirdException: Invalid parameter.

I’m hoping its just a few of these keys aren’t recognized by Sendbird, and that I’m nearly there - any recommendations as to which key values I should be sending?

Basically, I need to know exactly what shape this file object needs to be. It’s typed in the JavaScript SDK simply as ‘any’, so no help there.

Hi Chris,

I’m a bit confused. In your original post, you were using the file API via

const file = new File([blob], sendbirdId, {type: mimeType});

Are we moving away from that now? In terms of what you’re currently sending with your new image picker, it looks like you’re sending an object that we don’t expect and thus can’t process it.

Taking a look at our ReactNative sample, I converted the method that we use to send a fileMessage to achieve what you’re looking for, and that code looks like:

  const selectFile = async () => {
      if (Platform.OS === 'android') {
        const permission = await check(PERMISSIONS.ANDROID.READ_EXTERNAL_STORAGE);
        if (permission !== RESULTS.GRANTED) {
          const result = await request(PERMISSIONS.ANDROID.READ_EXTERNAL_STORAGE);
          if (result !== RESULTS.GRANTED) {
            throw new Error('Please allow the storage access permission request.');
          }
        }
      } else if (Platform.OS === 'ios') {
        // TODO:
      }
      const result = await DocumentPicker.pick({
        type: [
          DocumentPicker.types.images,
          DocumentPicker.types.video,
          DocumentPicker.types.audio,
          DocumentPicker.types.plainText,
          DocumentPicker.types.zip
        ]
      });
      const copyPath = `${RNFS.TemporaryDirectoryPath}/${result.name}`;
      await RNFS.copyFile(result.uri, copyPath);

      const fileStat = await RNFS.stat(copyPath);
      sb.updateCurrentUserInfoWithProfileImage('iOSTest01', {
        ...result,
        uri: `file://${fileStat.path}`
      });

This allowed me to successfully upload a profile image. I’ve attached a video showing that successfully complete.

Sorry, I work a lot in web as well and was rushing realizing later that File()'s are not possible in the react native environment. Thanks a lot for the provided example, it was very helpful, though in the end I was using an image picker solution instead of a file picker. All in all, the working solution for a file from an image picker (in my case react-native-image-crop-picker) to uploading a profile photo is as follows:

const uploadPhoto = async () => {
    const sendbirdId  = "some test id";
    const result = await ImagePicker.openPicker({cropping: false});
    const mimeParts = result.mime.split("/");

    const file = {
      uri: result.sourceURL,
      type: result.mime,
      name: `${sendbirdId}.${mimeParts[1]}`,
      size: result.size,
    };

    sendbird.connect(sendbirdId, () => {
      sendbird.updateCurrentUserInfoWithProfileImage(
        "test name",
        file,
        (err, user) => {
         // do something with error and user here 
         // (user will have plainProfileUrl with a URL to the newly uploaded profile pic if it worked properly)
        },
      );
    });
};

Thanks for your help, I hope this is useful to whoever may find it!

I think the crucial point was including the proper values in file, namely correctly choosing uri, type, name, and size.

Just discovered, this works for iOS, but not for Android. No clue why yet.