Begin mea culpa. One of the features that Dave Carroll and I briefly mentioned during our Winter ’12 Preview webinar earlier today was the ability to insert and update binary data using the Force.com REST API. This is currently a Pilot feature in Winter ’12 (i.e. you’ll need to contact Salesforce support to have it enabled for you) that allows you to insert and update up to 500Mb of binary data into any Standard Object (e.g. Attachment, Document or ContentVersion) using the standard Force.com REST API. Previously, the REST API supported querying blob fields and inserting/updating Base64 encoded binary data (upto 50MB), but with this new Pilot feature the REST API now supports the MIME multipart content-type standard which allows you to upload large binary files of up to 500Mb (no additional encoding required).

But that’s not what this blog post is about. After the webinar, one of attendees – Richard Tuttle –  posted a very pertinent question on Twitter. To paraphrase Richard, apart from the standard Force.com REST API can I also process blob data via Apex REST (which is GA in Winter ’12 btw)? In my haste to answer, I tweeted back that Blob primitives are not supported as return or parameter types in Apex REST and so the answer is no. Wrong answer. I’ll conveniently blame it on my earlier that usual wakeup alarm (the first webinar was @ 7am PST), but the bottom-line is that I should have known better. In fact, I had even demoed processing blob/binary data in an Apex REST service in the webinar that Alex Toussaint and I did on the topic a couple of month back! What I had forgotten in my haste to reply to Richard was that even though you can’t use a Blob primitive as the return or parameter type for an Apex REST method, you can still use the standard RestRequest and RestResponse classes to access the request or response data as blob/binary.

Here’s a small snippet from the Apex REST class that I demoed during the Apex REST webinar.

@RestResource(urlMapping='/CaseManagement/v1/*')
global with sharing class CaseMgmtService
{
     .....
    @HttpPost
    global static String attachPic(RestRequest req, RestResponse res){
        Id caseId = req.requestURI.substring(req.requestURI.lastIndexOf('/')+1);
        Blob pic = req.requestBody;
        Attachment a = new Attachment (ParentId = caseId,
                                       Body = pic,
                                       ContentType = 'image/jpg',
                                       Name = 'SendViaMyPhone');
        insert a;
        return a.Id;
    }
}

The method accepts a picture taken from an Android device and adds it as an Attachment to an existing Case record. Here’s the corresponding Java code snippet from my Android sample application that invokes the above Apex REST service.

String resourceURI = loginResult.getInstanceUrl() +
                          "/services/apexrest/CaseManagement/v1/"+sobjectId;
HttpClient client = AndroidHttpClient.newInstance("Android");
HttpPost getRequest = new HttpPost(resourceURI);
getRequest.setHeader("Authorization", "OAuth " + loginResult.getAccessToken());
getRequest.setHeader("Content-Type", "application/json");

getRequest.setEntity(new ByteArrayEntity(jpeg[0]));

try {
        HttpResponse response = client.execute(getRequest);
}catch (Exception e) {}

So, the bottom-line is that it’s absolutely possible to process binary data with Apex REST. You just have to use the RestRequest/RestResponse classes to work with the binary data instead of defining Blob return types or parameters in your Apex REST method. Sorry Richard – hopefully this detailed post makes up for misinforming you earlier. End mea culpa.

tagged Bookmark the permalink. Trackbacks are closed, but you can post a comment.
  • http://twitter.com/danfowlie Dan Fowlie

    This is great. How about about the reverse situation – using HTTP classes to retrieve a blob from an external service? Currently the getBody method on HTTPResponse only returns a string.

    • Anonymous

      In Winter ’12, you can still only access String data via the HTTPResponse.getBody method. However, the good news is that support for retrieving binary data via HTTPResponse is a high priority item on our roadmap and except to see something in the next release (Spring 12). As always, Safe Harbor applies.

  • Pablo Averbuj

    Hello, Developer Support directed me to this blog post and this sounds promising but it sounds like in your example the entirety of ths POST content is the image. In the case I’m trying to work through, I’m being posted a series of parameters one of which is a file I’d like to create as an attachment. Does the Apex REST interface account for this? If not, is it there another way to do this?

    • Anonymous

      Great question. The real way to support such a requirement is to use Multipart MIME (which is what the REST API now supports). Since Apex REST does not support Multipart MIME, there are 3 different ways you could implement your requirement

      1) Base64 encode the binary attachment and then send out a regular JSON request from the client with all the necessary parameters (one of which would be the base64 encoded file). In the Apex class use EncodingUtil to convert the base64 string back into a blob and create the Attachment record. The biggest issue with this approach is governor limits. If the binary file is big enough, the Apex Base64 decoding can potentially blow a governor limit (like the 3MB heap size limit). You could also define ‘Attachment’ as one of the method parameters and let the platform do the base64 serialization/deserialzation for you.

      2) Add all the parameters other than the binary attachment as query parameters to the end of the REST URI (e.g. ‘/MyApexRestSvc?param1=somevalue’). On the Apex side, you would still extract the binary attachment using RestRequest.requestBody, but you can use RestRequest.params to get the additional params as well.

      3) Send all parameters other than the binary attachment as HTTP headers and then use RestRequest.headers to extract them on the Apex side.

      Any of the above should work. Pick whichever one works best for your use case. Let me know if this helps.

      • Pablo Averbuj

        Unfortunately none of those work because I don’t control the client-side. However, I’ve gotten this to almost work using standard apex/vf. For example here’s the binary file part of the multipart mime POST:

        –xYzZY
        Content-Disposition: form-data; name=”attachment1″; filename=”zoom.png”
        Content-Type: image/png

        PNG[...binary data...]

        When I list the parameters available to GetParameters() here are the (relevant) parameters that are available and their values:

        “attachment1″ = /home/sfdc/salesforce/sfdc/jsp/form/form1874702852.tmp
        “attachment1.content-type” = image/png
        “attachment1.file” = /home/sfdc/salesforce/sfdc/jsp/form/form1874702852.tmp
        “attachment1.filename” = zoom.png

        It seems that Salesforce is storing the binary contents into /home/sfdc/…. but I can’t figure out how to access them from the controller. But otherwise it seems to handle the multipart mime perfectly.

        Any advice?

        • Anonymous

          Sorry – I don’t know of any way of handling multipart MIME with Apex/VF.

        • Darshini

          All those options works fine

  • pragati mathur

    Hello, My requirement is to send a Word file from Salesforce to external system. Is this possible?

    • Anonymous

      Can you describe your use case in more detail? How is the word file stored in Salesforce? As an Attachment record? What interface does the external system support? A REST endpoint? A SOAP service? Something else?

  • Ariel Techiouba

    i tried to implement the rest service as you posted, invoking it via CURL or Apigee, but it doesn’t seem to work.

    The attachment is created, but the content is not readable. I saved the attachment and opened the file with notepad++: it contains my jpg that i attached, but it is preceeded by the following lines:

    —-0246824681357ACXZabcxyz
    Content-Type: image/jpeg
    Content-Transfer-Encoding: binary
    Content-ID:
    Content-Disposition: attachment; name=”test3″; filename=”test.jpg”

    ****HERE CONTINUES WITH CONTENT OF THE “BINARY” FILE (JPG)***

    My POST request, using apigee service, result as following:

    POST /services/apexrest/AttachImage/001U00000070pnb/test3 HTTP/1.1
    X-HostCommonName:
    na12.salesforce.com
    Authorization:
    OAuth ——-here is my working session token——–
    X-PrettyPrint:
    1
    MIME-Version:
    1.0
    Host:
    na12.salesforce.com
    Content-Length:
    25680
    X-Forwarded-For:
    10.203.10.109
    X-Target-URI:
    https://na12.salesforce.com
    Content-Type:
    application/json; boundary=–0246824681357ACXZabcxyz
    Connection:
    Keep-Alive

    —-0246824681357ACXZabcxyz
    Content-Type: image/jpeg
    Content-Transfer-Encoding: binary
    Content-ID:
    Content-Disposition: attachment; name=”test3″; filename=”test.jpg”

    —-0246824681357ACXZabcxyz–

    My REST Apex Service code is here:

    @RestResource(urlMapping=’/AttachImage/*’)
    global with sharing class TestPostImages {

    @HttpPost
    global static string attachPicture (RestRequest req, RestResponse resp)
    {
    string[] my_params = req.requestURI.split(‘/’);
    string attach_name = my_params[my_params.size() -1];
    string account_id = my_params[my_params.size() -2];

    Blob picture = req.requestBody;

    Attachment a = new Attachment (ParentId = account_id
    , Body = picture
    , ContentType = ‘image/jpg’
    , Name = attach_name);

    insert a;
    return a.id;

    }

    }

    Why is Salesforce adding the header to my attachment? How can i prevent it?

    • Anonymous

      Its hard to tell for sure from the POST request, but it looks like you might be sending a multipart MIME message to the Apex REST service. Multipart MIME is not currently supported with Apex REST and so can you try sending just the binary data?

      • Ariel Techiouba

        can you post me an example of CURL command to accomplish that? i tried but the obtained file is not the same as the original

        • Anonymous

          Ariel – here in an example of a CURL command to send a binary file to an Apex REST endpoint – http://www.salesforce.com/us/developer/docs/apexcode/Content/apex_rest_code_sample_restrequest.htm.
          Note also that in Spring 12, the syntax for Apex REST has changed somewhat from what I had posted in this blog post. For e.g. you no longer need to define RestRequest and RestResponse parameters in the Apex method. Make sure that you’re using an Org that has been upgraded to Spring 12 and then use the sample in that link I posted to test binary data.

          • Ariel Techiouba

            The CURL command now works. I have 2 questions for you.. maybe i’ll post them on forum aswell:

            1) How to send the image through the JavaScript? I’m using a web app with the Mobile SDK framework. How can i build the json to be interpreted correctly by the corresponding Apex REST service?

            2) Do you know about any limits on the JSON parameters passed to Salesforce Apex REST services? For example: i builded an Apex REST wich gets a string array (POST) and create a corresponding custom object in my org. However, when my array gets over some lines, the ajax call (by jquery) invoke the ERROR callback and not the SUCCESS one. The error contains a “responseText” with http 200 and status OK. How is it possible? What is the limits about json and parameters? Why if i pass just a few lines array it works calling the correct success callback?

  • Dipankar Barman

    If the client side is (Objective -C ) IOS we may use the salesforce mobile sdk
    a wrapper over OAuth and RestKit (restful web-service framework).
    When we use the mobile SDK it redirects us to salesforce OAuth login screen.
    salesforce validates it and gives the framework a few token to maintain in the App.
    With each request the tokens are attached to the Http Header. Salesforce validates the request by the token.

    Once the control returns to application, internally used RestKit is already setup with the correct token and url.
    What remains are a few lines of code to obtain the shared instance and then fire the request.

    RKClient * client = [RKClient sharedClient]; /*RestKit client provides this facility to obtain the same singleton*/
    UIImage* image = [UIImage imageNamed:@"Brand.png"]; //Image object from file path within the project

    NSData* imageData = UIImageJPEGRepresentation(image,1); // getting the raw data from file in a data *

    RKSerializableImage * images= [[RKSerializableImage alloc]init]; /*Our client here requires us to pass all data as an object Parameter which conforms to a protocol (interface) ** */

    images.image = imageData;

    [client post:@"/services/apexrest/SessionManagement/v1/a0990000009Tlp6AAC" params:images delegate:self]; //this call send it to the webservice we implemented earlier

    —————————————
    RKSerializableImage.h
    **
    #import

    #import “RKRequestSerializable.h”

    @interface RKSerializableImage : NSObject

    @property (nonatomic,retain) NSData * image;

    - (NSData*)HTTPBody; //method that returns the raw data here the property we set above

    - (NSString*)HTTPHeaderValueForContentType; //returns application/json

    @end

    Implementation is trivial return the property

    • Yogesh Nandane

      thanks nice work.

      but how do i get attachment blob using this url /services/apexrest/SessionManagement/v1/a0990000009Tlp6AAC
      using get request

  • Hiren Adesara

    Does Apex REST supports multipart MIME?

  • Keith Clarke

    If you are working in Java, you might find this post “Creating an Attachment SObject complete with its body using the REST API from Java” helpful:

    http://force201.wordpress.com/2013/05/17/creating-an-attachment-sobject-complete-with-its-body-using-the-rest-api-from-java/