Post File From Salesforce Apex to External HTTP Webservices

This article will show you how  to send files from Salesforce to an external webservice using the Apex HttpRequest Class. You’ll learn how make a ‘multipart/form-data’ HTTP request which includes your file as an attachment. 

At Docparser we love Salesforce nearly as much as we love automating document based workflows. A lot of our customers are using our API in combination with Salesforce to extract data fields from PDF documents (for example to Process Invoices, Purchase Orders, Delivery Notes) and automate their business process.

Since we launched our native Salesforce integration we got asked a lot how one can send files from Salesforce to Docparser using Apex. Luckily our Docparser API provides lets you import PDF documents with a standard multipart/form-data HTTP request. This kind of request is the same request a HTTP Form would produce and is the most common way to transfer a file to another API.

So we decided to write this blog post which will not only be helpful for our customers, but anyone who wants to send files from Salesforce Apex to another webservice.

The Power of Salesforce Apex

Salesforce is great for several reasons. For starters, Salesforce is much more than just another CRM solution. Thanks to the Salesforce AppExchange, users can choose between hundreds of add-ons and integrations which makes Salesforce rather a platform than just a CRM software. Secondly, Salesforce also allows developers to create entire applications on top of their platform with their programming language Apex.

So you are having files stored inside Salesforce which you would like to send to an external webservice?

First thing you should do is to check if you can find an add-on in the AppExchange. Chances are high that you’ll find what you are looking for, may it be “Copy file from Salesforce to Box”, “Move files from Salesforce to Dropbox”, “Salesforce eSignature integration”,  “Sync Salesforce with your Document Management System”, … you name it.

If you can’t find what you are looking for in the AppExchange marketplace, it’s time to get your hands dirty with Apex. By using Apex one can send out files and documents from Salesforce to any HTTP API. Which means that with a little bit of tweaking you should be able to develop a solution 100% tailored to your needs.

Posting Files from Salesforce Apex with  ‘multipart/form-data’

How does sending a file with HTTP POST work?

Before we look at the implementation in Apex, let’s try to understand how sending data (including file attachments) with a multipart/form-data HTTP POST request actually works.

The short answer: All posted data, including the file attachments, are stored in the text body of the request in a certain way. A specific format needs to be respected so that the receiving server can “decode” the body payload and identify regular POST fields and attached files.

Below you’ll find a simplified representation of a HTTP body payload which includes one regular field (called “email”) and an attached file named “myfile.pdf” which is sent through the form field “upload_test”.

----BOUNDARY_STRING
Content-Disposition: form-data; name="email";

[email protected]
----BOUNDARY_STRING
Content-Disposition: form-data; name="upload_test"; filename="myfile.pdf"
Content-Type: text/octet-stream

ENCRYPTED_FILE_CONTENT
----BOUNDARY_STRING

So what is happening here?

As you can see, each field is separated by a specific boundary string (—-BOUNDARY_STRING). This separation string is a random token and is generated by the sender before posting the request. The receiver will use this boundary string to identify the start/end of each field.

Each separation string is then followed by a short header describing the data field and field content. The lines starting with “Content-Disposition:” and “Content-Type:” basically tell the receiver what kind of data to expect, how this field is named and how the data gets transferred.

When you look at the section describing the attached file, you’ll notice the line “Content-Type: text/octet-stream”. This line tells the receiver that the following data is the content of a file transferred in binary form. The line before indicates the HTTP field name and the file name.

Making HTTP POST Requests in Apex

Before we go into the details on how to send a file with Apex to another webservice, let’s look at the HttpRequest and Http Class provided by Apex. As you can see in the documentation, this class lets you “programmatically create HTTP requests like GET, POST, PUT, and DELETE”. So good so far.

Let’s look at the most simple example which will send out a basic HTTP POST request from Salesforce.

HttpRequest req = new HttpRequest();
req.setEndPoint('https://your-url.com');
req.setBody('Hello World');

Http http = new Http();
HTTPResponse res = http.send(req);
System.debug(res.getBody());

Alright, that was rather easy! But how about attaching a file to a HTTP request in Apex?

If you already flipped through the HttpRequest documentation, you might have realised, that there is unfortunately no method which would let you attach a file. Not cool!

For us that means that we need to create our own code to post files and documents from Salesforce Apex.

Send text based file attachment from Salesforce Apex with a ‘multipart/form-data’ HTTP request

In the previous section we learned what the body of a “multipart/form-data” request is made up of. So let’s jump into the details and post “multipart/form-data” out of Salesforce.com with Apex.

The following code assembles the correct body payload according to the schema described before. Once assembled, the HttpRequest class is used to make a HTTP POST request with a multipart/form-data header, the boundary description and the body payload :

// change the following variables according to your use-case
String fileName = 'MY_FILE.txt';
String fileContent = 'Hello World';
String targetURL = 'https://your.target.com/api';
String separationString = 'A_RANDOM_STRING';

// assemble the body payload
String header = '--' + separationString + '\nContent-Disposition: form-data; name="file"; filename="' + fileName + '"\nContent-Type: application/octet-stream\n\n';
String body = EncodingUtil.base64Encode(Blob.valueOf(fileContent));
String footer = '--' + separationString + '--';
String bodyPayload = header + body + footer;

// send out the request
HttpRequest req = new HttpRequest();
req.setHeader('Content-Type', 'multipart/form-data; boundary=' + separationString);
req.setHeader('Content-Length', String.valueof(bodyPayload));
req.setMethod('POST');
req.setEndpoint(targetURL);
req.setBody(bodyPayload);

Http http = new Http();
http.send(req);

Please note: The code above only works for text based files such as plain text, html, xml or csv. The next section will give us more insights on how to post non-ascii binary files from Salesforce.

Uploading non-ascii binary file using Apex and ‘multipart/form-data’ HTTP request

As already mentioned, the code above only works with text based files. So the question how to post binary data to an external service from Apex remains.

It would have been really convenient if sending binary files was as easy as sending ascii-text based files. But unfortunately it isn’t. Why do you ask? The short answer is that it is not possible to send binary as ‘base 64 encoded’ data (we use base64 encoding above). At the same time, you can’t just send the raw binary data either as this will lead to a corrupted file after transmission.

So what is the long answer and more importantly, what is the solution for sending out a non-ascii binary file with Apex? Luckily, Enrico Murru digged deep and came up with a solution to post any type of attachments out of APEX. You can find a working piece of code at the end of this article on his blog.

Send a PDF document from Salesforce to Docparser

As mentioned in the beginning of the article, a lot of our customers are using Docparser in combination with Salesforce. The usual setup is something like this:

  • PDF documents are sent from Salesforce to the Docparser API
  • Docparser extracts data from PDF files
  • The extracted data fields are then sent back to Salesforce with a HTTP webhook or our native Salesforce integration

If you know your way around in Apex, integrating Docparser with Salesforce should be quite easy. We have a couple of Apex code snippets on stock which you can use to get started. Just visit our Github Repository to download them.

7 Responses

  1. My application where this is sending to is getting an error “unexpected end of multipart file”

  2. When I try to compile the line:
    String body = EncodingUtil.base64Encode(fileContent) + ‘\n’;

    I get the following error:
    Error 1 Method does not exist or incorrect signature: void base64Encode(String) from the type System.EncodingUtil D:\OneDrive\Documents\The Welkin Suite\Projects\SalesforceProject Partial 09-14-2021\SalesforceProject Partial 09-14-2021\src\classes\AzureUtils.cls 49 1

    1. Hi Patrick,

      Thanks for reaching out and sorry to hear about the trouble here! Could you forward a copy of the file you were unable to import to us at [email protected] and we will have a closer look?

      Have a good week!

  3. Hi Joshua,
    What I eventually discovered is the Azure org I am sending to does not accept setMethod(‘POST’). I changed to setMethod(‘PUT’) and that seemed to resolve the issue. Thank you so much for the response.

    HttpRequest req = new HttpRequest();
    req.setHeader(‘Content-Type’,’multipart/form-data; boundary=’+boundary);
    req.setHeader(‘x-ms-blob-type’, ‘BlockBlob’);
    req.setMethod(‘PUT’);
    req.setEndpoint(reqEndPoint);
    req.setBodyAsBlob(bodyBlob);
    req.setTimeout(120000);
    System.debug(‘value of response: ‘ + req);
    Http http = new Http();
    HTTPResponse res = http.send(req);
    System.debug(‘value of res: ‘ + res)

    By any chance, do you happen to have a sample test class that would correspond to the method you posted above?

    Thanks in advance.

  4. Hi Joshua,
    I want to send three parameters in the body, (Agent, userId , media). Here Agent is optional parameter. userId & media is mandatory. How to pass 2 parameters separated by boundary value. userId is a text field & media is any image file.

    Could you please help us ?
    Thanks !!

    1. Hi Pallavi,

      Thanks for reaching out! Unfortunately we aren’t experts in the Salesforce ecosystem and can’t provide guidance on building custom payloads, I would recommend reaching out to your Salesforce Administrator. If you find a solution please let us know at [email protected] and we’ll update the article.

Leave a Reply

Your email address will not be published. Required fields are marked *

Convert your first
PDF to data.

No credit card required.

Facebook
Twitter
LinkedIn

Schedule a one-on-one demo