[SalesForce] Upload files to a Case in SalesForce via REST API

I'm working on uploading attachments from my website to Salesforce, attachments should be related to case object. I'm using REST API in PHP. So problem is that I get an error when I tried to upload new file. Other requests to Salesforce working well.

I'm logged in using this code:

        curl_setopt($curl, CURLOPT_URL, "https://" . $this->salesforce_host . "/services/oauth2/token");
        curl_setopt($curl, CURLOPT_POSTFIELDS, "client_id=" . $this->salesforce_client_id. "&grant_type=password&client_secret=" . $this->salesforce_client_secret . "&username=" . $this->salesforce_username . "&password=" . $this->salesforce_password . $this->salesforce_token);
        curl_setopt($curl, CURLOPT_HTTPHEADER, array("Content-Type: application/x-www-form-urlencoded;"));

where $this->salesforce_host == 'test.salesforce.com'. I'm tested on developer account now.

In result I receive session ID and instance URL which I use in futher scripts. Instance url = https://cs30.salesforce.com.

Here is a PHP script that I use for upload files:

// https://$salesforceurl/services/data/v29.0/chatter/feeds/record/$caseId/feed-items
$session = $this->getSession();
$url = $session["instanceUrl"] . "/services/data/v" . $this->salesforce_version . "/chatter/feeds/record/" . $parentId . '/feed-items';

$curl = $this->_getDefaultCurl();
curl_setopt($curl, CURLOPT_URL, $url);
curl_setopt($curl, CURLOPT_CUSTOMREQUEST, "POST");

//get file mime type and binary content of file

// form field separator
$delimiter = uniqid();
$fileFields = array(
    'fileFeedItem' => array(
        'type' => $finfo_mime,
        'content' => implode('', $buffer)
    ),
);

$data = '';

$data .= "--" . $delimiter . "\r\n";
$data .= 'Content-Disposition: form-data; name="json";' . "\r\n";
$data .= 'Content-Type: application/json; charset=UTF-8;' . "\r\n\r\n";
$fileName = basename($file);
$json = array(
    'body' => array(
        'messageSegments' => array(
            'type' => 'Text',
            'text' => 'User uploaded attachment'
        )
    ),
    'attachment' => array(
        'attachmentType' => 'NewFile',
        'title' => $fileName
    ),

);
$data .= json_encode($json)."\r\n";
$data .= "\r\n\r\n";


foreach ($fileFields as $name => $fileItem) {
    $data .= "--" . $delimiter . "\r\n";
    $data .= 'Content-Disposition: form-data; name="feedItemFileUpload"; filename="' .$fileName . '"' . "\r\n";
    $data .= 'Content-Type: ' . $fileItem['type'] . "\r\n";
    $data .= 'Content-Transfer-Encoding: binary' . "\r\n";
    $data .= "\r\n";
    $data .= $fileItem['content'] . "\r\n";
}
$data .= "--" . $delimiter . "--\r\n";

$headers = '';
$headers .= "Accept: application/xml"."\r\n";
$headers .= "Content-Type: multipart/form-data; boundary=".$delimiter."\r\n";
$headers .= "Authorization: OAuth ". $session["sessionId"] ."\r\n";
$headers .= "Host: ". $session["instanceUrl"] ."\r\n";
$headers .= "Content-Length: " . strlen($data) . "\r\n";
$headers .= "Expect: 100-continue" . "\r\n";
$headers .= "\r\n";

$data = $headers . $data;

curl_setopt($curl, CURLOPT_POSTFIELDS, $data);

$response_json = curl_exec($curl);

here is $data content that I've added to request:

Accept: application/xml
Content-Type: multipart/form-data; boundary=52dd7f7e5fd87
Authorization: OAuth <session_id>
Host: https://cs30.salesforce.com
Content-Length: 494
Expect: 100-continue

--52dd7f7e5fd87
Content-Disposition: form-data; name="json";
Content-Type: application/json; charset=UTF-8;

{"body":{"messageSegments":{"type":"Text","text":"User uploaded attachment"}},"attachment":{"attachmentType":"NewFile","title":"attachment_test.txt"}}


--52dd7f7e5fd87
Content-Disposition: form-data; name="feedItemFileUpload"; filename="attachment_test.txt"
Content-Type: text/plain; charset=us-ascii
Content-Transfer-Encoding: binary

�ttachment_test
--52dd7f7e5fd87--

But in response I got this error:

[{"message":"Session expired or
invalid","errorCode":"INVALID_SESSION_ID"}]

Please help me how to solve this problem. I've tried many variants of code to decide this problem.
Perhaps I should reset security token? But other Salesforce functions works well. Or I should to use another instance url?

Thanks,
Yuriy

EDIT: new script

        $session = $this->myLogin();
        $url = $session["instanceUrl"] . "/services/data/v" . $this->salesforce_version . "/chatter/feeds/record/" . $parentId . '/feed-items';

        $curl = $this->_getDefaultCurl();
        curl_setopt($curl, CURLOPT_URL, $url);
        curl_setopt($curl, CURLOPT_CUSTOMREQUEST, "POST");

        $fileName = basename($file);
        $json = array(
            'body' => array(
                'messageSegments' => array(
                    'type' => 'Text',
                    'text' => 'User uploaded attachment'
                )
            ),
            'attachment' => array(
                'attachmentType' => 'NewFile',
                'title' => $fileName
            ),
        );

        $headers = array();
        $headers[] = "Authorization: OAuth ". $session["sessionId"];
        $headers[] = "Content-Type: application/json; charset=utf-8";
        $headers[] = "Accept: application/json";

        $fileArray = array(
            'json' =>$json,
            'feedItemFileUpload' => '@'.realpath($file),
        );

        $data = json_encode($fileArray);

        curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
        curl_setopt($curl, CURLOPT_POST, true);
        curl_setopt($curl, CURLOPT_POSTFIELDS, $data);
        curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($curl, CURLINFO_HEADER_OUT, true);

        $response_json = curl_exec($curl);

Auth works well, but I got an error:

[{"message":"Unrecognized field \"json\" at [line:1,
column:10]","errorCode":"JSON_PARSER_ERROR"}]

Is anyone can help me?

Thanks

Best Answer

You're very close - the tricky bit is that the Chatter REST API is expecting a multipart form. Also, it wants the message segments as its own array. After some poking around it looks like the easiest way to handle it is by wrangling the post data together.

I'm using this doc as a base: http://www.salesforce.com/us/developer/docs/chatterapi/Content/intro_input.htm

And here is my script, note that I pasted the url manually (which you should not):

    <?php
    $url = "https://na15.salesforce.com/services/data/v29.0/chatter/feeds/record/500i0000004AcIq/feed-items";
    $curl = curl_init();
    curl_setopt($curl, CURLOPT_URL, $url);
    curl_setopt($curl, CURLOPT_CUSTOMREQUEST, "POST");

    $fileName = basename($file);

    $headers = array();
    $headers[] = "Authorization: OAuth {token}";
    $headers[] = 'Content-Type: multipart/form-data; boundary=F9jBDELnfBLAVmLNbnLIYibT5Icp0h3VJ7mkI';

    $segments = array();
    $segments['type'] = 'Text';
    $segments['text'] = 'User uploaded attachment';

    $message = array();
    $message[] = $segments;

    $dataArray = array(
        'body' => array(
            'messageSegments' => $message
        ),
        'attachment' => array(
            'attachmentType' => 'NewFile',
            'title' => 'Test File'
        )
    );

    $data = $data.json_encode($dataArray);
    $file = realpath('phoenix-logo.png');


    $post_text = '--F9jBDELnfBLAVmLNbnLIYibT5Icp0h3VJ7mkI
Content-Disposition: form-data; name="json"
Content-Type: application/json; charset=UTF-8

{ "body":
   {
      "messageSegments" : [
      {
         "type" : "Text", 
         "text" : "Here is another file for review."
      }, {
         "type" : "Hashtag", 
         "tag" : "important"
      }, {
         "type" : "Text", 
         "text" : "Again, please review this as soon as possible."
      }
      ]
   }, 
   "attachment": 
   {
      "attachmentType" : "NewFile",
      "description": "Phoenix Logo",
      "title" : "Logo.png"
   }
}

--F9jBDELnfBLAVmLNbnLIYibT5Icp0h3VJ7mkI
Content-Disposition: form-data; name="feedItemFileUpload"; filename="logo.png"
Content-Type: image/png

'.file_get_contents($file).'

--F9jBDELnfBLAVmLNbnLIYibT5Icp0h3VJ7mkI--';

    curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
    curl_setopt($curl, CURLOPT_POST, true);
    curl_setopt($curl, CURLOPT_VERBOSE, 1);
    curl_setopt($curl, CURLOPT_POSTFIELDS, $post_text);
    curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($curl, CURLINFO_HEADER_OUT, true);

    $response_json = curl_exec($curl);

    print $response_json;
    ?>
Related Topic