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’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unable to Create ContentVersion with sendRequest #237

Closed
ritikadhandia opened this issue Sep 8, 2021 · 5 comments
Closed

Unable to Create ContentVersion with sendRequest #237

ritikadhandia opened this issue Sep 8, 2021 · 5 comments
Labels

Comments

@ritikadhandia
Copy link

I am trying to upload a clicked picture from the app to Salesforce, using sendRequest method with fileParams as mentioned here.
However, even though the example shown (profile pic upload) works just fine, but an upload to ContentVersion / Document endpoint is giving an error - “Multipart message must include a non-binary part”. I’ve tried putting other parameters in payload but no luck.
Not able to figure out anyway of sending the non-binary part.

Code Snippet -
net.sendRequest('/services/data',
'/v52.0/sobjects/ContentVersion',
(response) => {
console.log('success')
},
(error) => {
console.log('Failed to upload image' + error);
},
'POST',
{entity_content :
{
Title: 'Some Title'
}},
{},
{VersionData:
{
fileUrl:picUrls[0],
fileMimeType:'image/jpeg',
fileName:'somepicture.jpg'
}
});

Error Received -
{"message":"Multipart message must include a non-binary part","errorCode":"INVALID_MULTIPART_REQUEST"}]

@wmathurin
Copy link
Contributor

I think you are missing some fields.
For more info on that REST API, see https://www.youtube.com/watch?v=KZB4Yhi9xv0

net.sendRequest('/services/data',
                '/v52.0/sobjects/ContentVersion',
                (response) => {
                    console.log('success')
                },
                (error) => {
                    console.log('Failed to upload image' + error);
                },
                'POST',
                {
                    Title: 'Some Title',
                    PathOnClient: 'xxx',
                    OwnerId: 'xxx',
                    ContentLocation: 'xxx'
                },
                {},
                {
                    VersionData: {
                        fileUrl:picUrls[0],
                        fileMimeType:'image/jpeg',
                        fileName:'somepicture.jpg'
                    }
                });

@ritikadhandia
Copy link
Author

Thanks for your reply. I have tried adding all these fields, but the same error - Multipart message must include a non-binary part","errorCode":"INVALID_MULTIPART_REQUEST.
Were you able to get this working ?
Thanks

@wmathurin
Copy link
Contributor

Is is on iOS or Android or both?

@wmathurin
Copy link
Contributor

There is indeed an issue with multipart post requests on iOS when they have a non-binary part.
But it seems to work fine on Android. We have opened a bug on iOS and will look into it.

  1. Generate a basic react native application by running forcerreact create --apptype=react_native
  2. Add "react-native-image-picker": "^4.0.6" to dependencies in package.json
  3. Run ./installios.js and ./installandroid.js
  4. Paste the code below in app.js
  5. Run npm start from the root directory of your generated app
  6. Finally launch Xcode and/or Android Studio and run the application

==> App will successfully upload images you pick from your device on Android but not iOS.

import React from 'react';
import {
    StyleSheet,
    Text,
    View,
    Image,
    Button
} from 'react-native';

import { NavigationContainer } from '@react-navigation/native';
import { createStackNavigator } from '@react-navigation/stack';
import { oauth, net} from 'react-native-force';
import { launchCamera, launchImageLibrary} from 'react-native-image-picker';

const pickPhoto = (callback) => {
    const options = {
        cancelButtonTitle: 'Cancel',
        takePhotoButtonTitle: 'Take Photo...', 
        chooseFromLibraryButtonTitle: 'Choose from Library...', 
        cameraType: 'front', 
        mediaType: 'photo', 
        maxWidth: 200, 
        maxHeight: 200, 
        allowsEditing: true, 
        noData: true,
        storageOptions: { 
            skipBackup: true, 
            path: 'images' 
        }
    };

    console.log("pickPhoto: Showing image picker");
    
    launchImageLibrary(options, (response) => {
        console.log('pickPhoto: launchImageLibrary response = ' +  JSON.stringify(response));

        if (response.didCancel) {
            console.log('pickPhoto: User cancelled image picker');
        }
        else if (response.error) {
            console.log('pickPhoto: ImagePicker Error: ' + response.error);
        }
        else if (response.customButton) {
            console.log('pickPhoto: User tapped custom button: ' +  response.customButton);
        }
        else {
            callback(response);
        }
    });
};

const uploadPhoto = (localPhotoUrl, callback) => {
    const fileName = localPhotoUrl.replace(/^.*[\\\/]/, '')
    net.sendRequest('/services/data',
                    '/v52.0/sobjects/ContentVersion',
                    (response) => {
                        console.log('uploadPhoto: success: ' + JSON.stringify(response))
                        callback(response)
                    },
                    (error) => {},
                    'POST',
                    {
                        Title: fileName,
                        PathOnClient: fileName,
                        ContentLocation: 'S'
                    },
                    {},
                    { VersionData: {fileUrl:localPhotoUrl, fileMimeType:'image/jpeg', fileName:fileName} }
    );
}    


class MainScreen extends React.Component {
    constructor(props) {
        super(props);
        this.state = {photoUrl: null};
    }
    
    componentDidMount() {
        var that = this;
        oauth.getAuthCredentials(
            () => {}, // already logged in
            () => {
                oauth.authenticate(
                    () => {},
                    (error) => console.log('Failed to authenticate:' + error)
                );
            });
    }

    onChangePic() {
        var that = this;
        pickPhoto((response) => {
            localPhotoUrl = response.assets[0].uri
            that.setState({photoUrl: localPhotoUrl});
            uploadPhoto(localPhotoUrl, (response) => {
                // 
            });
        });
    }


    render() {
        return (
            <View style={styles.container}>
              <View style={styles.content}>
                { this.state.photoUrl ? <Image style={styles.photo} source={{uri: this.state.photoUrl}} /> : <Text>No pic selected</Text> }
                <Button onPress={this.onChangePic.bind(this)} title="Select Image" />
            </View>                
            </View>
        );
    }
}

const styles = StyleSheet.create({
    container: {
        flex: 1,
        paddingTop: 22,
        backgroundColor: 'white',
    },
    content: {
        flex: 1,
        flexDirection: 'column',
        alignItems: 'center',
        justifyContent: 'center'
    },    
    photo: {
        height:200,
        width:200,
    },    
});

const Stack = createStackNavigator();

export const App = function() {
    return (
        <NavigationContainer>
          <Stack.Navigator>
            <Stack.Screen name="Image Uploader" component={MainScreen} />
          </Stack.Navigator>
        </NavigationContainer>
    );
}

Thanks for reporting the issue!

@wmathurin
Copy link
Contributor

PR for fix: #238
Will be in our next release.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants