function readOnly(count){ }
Starting November 20, the site will be set to read-only. On December 4, 2023,
forum discussions will move to the Trailblazer Community.
+ Start a Discussion
johncusackjrjohncusackjr 

RESTful WebService Call Using PUT Method (Binary Data)?

Hi,

 

I would like to make an external RESTful web service call using PUT method. The service accepts file data as the body of the http request. When I use HttpRequest.setBody(‘some text’), this works and external service understands this.

 

However, when I try to read a blob (such as a document body in salesforce) and upload that such as HttpRequest.setBody(blobData.toString()), the service gets corrupted data.

 

For example:

List<Document> docs = [select body from document where ID = 'someId'];
System.debug('RETURN: ' +MyCustomUtil.createFile('testImage.jpg',EncodingUtil.base64Encode(docs[0].body),'image/jpeg'));
System.debug('URL:::' + MyCustomUtil.getURL('testImage.jpg'));

 

Is there a better way to do this? I tried encoding-decoding this blob data with EncodingUtil class but I couldn’t get anywhere. I would like to see an example where binary data can be send to an external RESTful service.

 

Thanks,

John

Pradeep_NavatarPradeep_Navatar

Fetching the blob data from salesforce org provides the data in encypted format  and that can not be decrypted so you are sending  encrypted data to the request, that shows corrupted data.

 

Hope this helps.

sfdcfoxsfdcfox

Your file isn't corrupted, but instead it's still Base64-encoded when it is written to the file. You need to tell the server that the content is encoded during the transfer. Usually that means doing something similar to the following in your code:

 

 

request.setHeader('Content-Transfer-Encoding','base64');

This should advise the server that your content is encoded, and when it sees this header, the data will be reassembled back to binary format. Note that the scripting language used is probably responsible for the re-encoding, so you may need to coordinate with your developer or host provider.

 

johncusackjrjohncusackjr

Thanks but Content-Transfer-Encoding is not supported by the service provided (Amazon S3).

sfdcfoxsfdcfox

I just did a small experiment, and it appears that you could do the following:

 

 

List<Document> docs = [select body from document where ID = 'someId'];
System.debug('RETURN: ' +MyCustomUtil.createFile('testImage.jpg',docs[0].body.toString(),'image/jpeg'));
System.debug('URL:::' + MyCustomUtil.getURL('testImage.jpg'));

Strings appear to support arbitrary binary data, so you might be able to send it directly. Try that and let me know if it works.

 

johncusackjrjohncusackjr

Unfortunately it is not working. Same error.

SuperfellSuperfell

You can't use strings to send binary data. Currently there's no support for sending binary data in apex callouts.

johncusackjrjohncusackjr

Yes, this is bad, because in our project we need to send files to Amazon S3 over Apex. String containing text files are OK, but images, binary data are failing. I hope they will add these important feature in new releases. Thanks.

sfdcfoxsfdcfox

Thanks for the information, Simon.

 

I guess a workaround would be to post to a third-party server instead of Amazon S3 directly, and have that server decode the base64-data and then submit the request. A proxy should most certainly work...

johncusackjrjohncusackjr

Thanks sfdcfox. It is the only solution, but customer does not want to use third-party server. We implemented that logic in Java to use as a third-party server but it is not useful for this solution.

We need to wait until Salesforce enables that feature for Apex development.

Shah RShah R

I see a number of apps on AppExchange offering file backups using Amazon S3. I wonder how they're doing this if Salesforce side of files are base64encoded and Amazon doesn't support transfer encoding. RESTful or WSDL, transport of files wouldn't matter, right?

 

Does anyone know how they're doing this?

 

--Shah

GoForceGoGoForceGo

 

I get the infamous "SignatureDoesNotMatch" response when trying to use rest service.  John (or someone), what am I doing wrong below? Seems John were able to get it to work for some basic text type files and upload garbage with binary. I have played around with all kinds of permulations/combinations for setting the host parameter and endpoint, but keep getting this errors.  I have also tried urlencoding the stringtosign. When I look at the error message, the string to sign appears what it should be... (PUT


Tue, 01 Mar 2011 19:56:10 GMT
/mybucket/xyz.txt

 

 

     private String make_sig(string canonicalBuffer) {        
String sig ;
Blob mac = Crypto.generateMac('HMacSHA1', blob.valueof(canonicalBuffer),blob.valueof(as3.secret));
sig = EncodingUtil.base64Encode(mac);
return sig;
}

public void s3RestPut(String docBody,string filename) {

HttpRequest req = new HttpRequest(); Datetime now = Datetime.now(); String formattedDateString = now.formatGMT('EEE, dd MMM yyyy HH:mm:ss z'); req.setMethod('PUT'); String bucketname = 'mybucket'; integer fileSize = docBody.length(); req.setHeader('Content-Length',String.valueOf(fileSize)); req.setHeader('Host', bucketname + '.s3.amazonaws.com'); //virtual type req.setEndpoint('https://' + bucketname + '.s3.amazonaws.com' + '/' + filename); //virtual style //path based //req.setHeader('Host', 's3.amazonaws.com'); //req.setEndpoint('https://s3.amazonaws.com/' + bucketname + '/' + filename); req.setHeader('Date',formattedDateString); String stringToSign = 'PUT\n\n\n'+formattedDateString+'\n/'+bucketname+'/'+filename; string encodedStringToSign = EncodingUtil.urlEncode(stringToSign,'UTF-8'); System.Debug('encodedStringtoSign is ' + encodedStringToSign); String signed = make_sig(encodedStringToSign); // String authHeader = 'AWS' + ' ' + as3.key + ':' + signed; req.setHeader('Authorization',authHeader); req.setBody(docBody); Http http = new Http(); HTTPResponse res = http.send(req);

 

 

 

 

GoForceGoGoForceGo

Shah,

 

I think none of these apps perform an SFDC server to Amazon transfer. it is either between browser and amazon, or between the app server hosted by these providers and Amazon.

 

 

 

 

GoForceGoGoForceGo

The following code now works - but of course - only for text files. No luck for binary files....

 

 

 

  public void s3RestPut(String docBody,string filename) { 
      
	  HttpRequest req = new HttpRequest();

	  Datetime now = Datetime.now();
	  String formattedDateString = now.formatGMT('EEE,   dd MMM yyyy HH:mm:ss z');
	  req.setMethod('PUT');
	  integer fileSize = docBody.length(); 
	  String bucketname = 'mybucket'; 
	  req.setHeader('Content-Length',String.valueOf(fileSize));  
	  req.setEndpoint('https://mybucket.s3.amazonaws.com' + '/' + filename);
	  req.setHeader('Date',formattedDateString);
	  String stringToSign = 'PUT\n\n\n'+formattedDateString+'\n/'+bucketname+'/'+filename;	  
          String signed = make_sig(stringToSign); 
          String authHeader = 'AWS' + ' ' + as3.key + ':' + signed;
          req.setHeader('Authorization',authHeader);  
          req.setBody(docBody); 
          Http http = new Http();
          HTTPResponse res = http.send(req);