7

I have created an Apex REST service that accepts a POST with a binary attachment. When the Content-Type is set to "application/pdf", it works fine, accepts the file, and the attachment is valid as an attachment in Salesforce. However, if the Content-Type is "application/vnd.ms-excel" or "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" the file becomes corrupted when stored in Salesforce. Has anyone made this work?

This is the code:

@RestResource(urlMapping='/FileTOOLS/v1.0/*')
global class FileTool {

 @HttpPost
global static String attachBinary(){
    RestRequest req = RestContext.request;
    RestResponse res = RestContext.response;

    String responseSTR;
    String contenttype = req.headers.get('Content-Type');
    Id reqid = req.requestURI.substring(req.requestURI.lastIndexOf('/')+1);
    String attachName='';
    if(req.headers.get('filename') != null ){
            String sourcefile=req.headers.get('filename');
            Integer last = sourcefile.lastIndexOf('.');
            system.debug(last);
            attachName = sourcefile.subString(0,last).replaceAll('\\.','_')+sourcefile.subString(last);
            system.debug(attachName);
    }else{
        attachName='generic.txt';
    }

    Blob b = req.requestBody;
    Integer docsize = b.size();
    try{
        sObject a;
        if(docsize < 26214400){
            a = new Attachment (ParentId = reqid,
                                       Body = b,
                                       ContentType = contenttype,
                                       Name = attachName);
        }else{
            a = new FeedItem(parentId = reqid,
                                    ContentData = b,
                                    ContentFileName = attachName);
        }
        try{
            insert a;
            responseSTR= a.id ;
        }catch(DMLException d){
            responseSTR= d.getDMLMessage(0);
        }   
    }catch(Exception e){
        responseSTR ='Error:: - ' + e.getMessage();
    }
    return responseSTR;
}
4
  • Still no progress on this, as a quick update, this is what I see in the file, when I use a different content-type: ------WebKitFormBoundaryrI4YciG6nUqyyELb Content-Type: application/vnd.ms-excel
    – JimRae
    Commented Jul 11, 2014 at 12:51
  • Although this idea requires some work from you -- what happens if you wrote a small VF controller that accepted an upload of file with content_type application/vnd.ms-excel and then stored the attachment using the same logic you have in your REST method? Or, instead of storing as an attachment, have the REST method also save a ContentVersion to try and help isolate the issue.
    – cropredy
    Commented Jul 11, 2014 at 21:11
  • Are you sure that the problem does not lie in your client-side code?
    – Keith C
    Commented Jul 13, 2014 at 15:33
  • Keith, what would that problem be? I get the same result using Postman in chrome and from the "real" client app, written in .net. I think the issue is related to the way SF handles the saved attachment. What I noticed is that a pdf is saved the same way I mentioned in my comment, but shows correctly when viewed, but the Excel file is not. I am not sure how to get just the binary part posted without the other header info
    – JimRae
    Commented Jul 13, 2014 at 15:40

1 Answer 1

4
+50

Based on your comment it would appear the binary contents of the XLS file is being POSTed to the REST web service wrapped in a multipart form request. The corresponding multipart boundaries are being included in the file data. Excel is rejecting these boundaries as an invalid file. Your PDF reader might be able to ignore the invalid data.

Here is a sample request I sent to the same webservice using Postman.

enter image description here

If you preview the request in Postman you will see that it is adding the multipart boundaries for you around the selected file.

enter image description here

I see in the Postman docs they have a binary mode. This should get around the issue, but I didn't have that option.


Instead, I created a simple test C# console application to post the binary data to the REST service.

static void Main(string[] args)
    {
        HttpWebRequest request = (HttpWebRequest)WebRequest.Create("https://na5.salesforce.com/services/apexrest/DFB/FileTOOLS/v1.0/00Q70000BOND007");
        string sessionId = "00D700000000001!AQoAQGGERUsF7QQ_NOTAREALSESSIONID_9K6M6bNuFqftKeM_t9";
        request.Headers.Add(HttpRequestHeader.Authorization, "OAuth " + sessionId);
        request.ContentType = "application/vnd.ms-excel";
        request.Headers.Add("filename", "fooBar.xls");
        request.Method = WebRequestMethods.Http.Post;

        byte[] fileData = System.IO.File.ReadAllBytes("C:\\Path\\To\\Your\\Excel\\File\\Book1.xlsx");
        request.ContentLength = fileData.Length;

        using (System.IO.BinaryWriter postStream = new System.IO.BinaryWriter(request.GetRequestStream()))
        {
            postStream.Write(fileData);
            postStream.Close();
        }

        HttpWebResponse response = (HttpWebResponse)request.GetResponse();
        System.Diagnostics.Debug.Assert(response.StatusCode == HttpStatusCode.OK);

        string responseMessage = string.Empty;
        using (System.IO.StreamReader sr = new System.IO.StreamReader(response.GetResponseStream()))
        {
            responseMessage = sr.ReadToEnd();
        }
        System.Diagnostics.Debug.WriteLine(responseMessage);

    }

This worked. I was able to confirm that the resulting attachment had the same number of bytes as the original file and could be opened in Excel without issue.

One note, ensure that the filename has the correct extension. The Attachment.ContentType will be populated based on the extension. See Determine the correct file type extension for an Attachment

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .