-1

I am using the below code to upload file to Amazon S3 from Apex class.

public void uploadToAmazonS3 () {
    String filename = this.record.File_Name__c;
    String attachmentBody = EncodingUtil.base64Encode(fileBlob);
    String formattedDateString = DateTime.now().formatGMT('EEE, dd MMM yyyy HH:mm:ss z');
    String bucketname = 'test-salesforce-upload';
    String host = 's3.amazonaws.com';

    String key = credentials.key;
    String secret = credentials.secret;
    String stringToSign = 'PUT\n\n\n'+formattedDateString+'\n\n/'+bucketname+'/'+filename;
    Blob mac = Crypto.generateMac('HMACSHA1', blob.valueOf(stringToSign), blob.valueof(secret));
    String signed = EncodingUtil.base64Encode(mac);
    String authHeader = 'AWS' + ' ' + key + ':' + signed;

    HttpRequest req = new HttpRequest();
    req.setMethod('PUT');
    req.setHeader('Authorization', authHeader);
    req.setEndpoint('https://s3.amazonaws.com/'+bucketname);
    req.setHeader('Host', bucketname + '.' + host);
    req.setHeader('Content-Length', String.valueOf(attachmentBody.length()));
    req.setHeader('Content-Type', contentType);
    req.setHeader('Connection', 'keep-alive');
    req.setHeader('Date', formattedDateString);
    req.setHeader('ACL', 'public-read-write');
    Blob blobBody = EncodingUtil.base64Decode(attachmentBody);
    req.setBodyAsBlob(blobBody);

    Http http = new Http();
    HTTPResponse resp = http.send(req);
    System.debug(resp);
}

The code give 503 error. The actual message in the logs is:

DEBUG|System.HttpResponse[Status=Forbidden, StatusCode=403]

How can I fix this issue? Feel free to ask for any clarifications. Thanks for your time.

Edit 1

I upload a sample file to s3 manually and set its permission to public. Now when I try to access the file in apex using the below code it give 503 error but it works using postman.

HttpRequest req = new HttpRequest();
    req.setMethod('GET');
    req.setHeader('Host', 'test-salesforce-upload.s3.amazonaws.com');
    req.setEndpoint('https://s3.amazonaws.com/test-salesforce-upload/AWS+Program.docx');

    Http http = new Http();
    HTTPResponse res = http.send(req);
    System.debug('RESPONSE STRING: ' + res.toString());
    System.debug('RESPONSE STATUS: ' + res.getStatus());
    System.debug('STATUS_CODE: ' + res.getStatusCode());

enter image description here

  • What about the bucket policies in S3 and the CORS configuration? are those setup to accept requests from your instance – Yucel Jan 19 '18 at 13:50
  • Yes the bucket policies are set. I think the issue might be with the host. – Anshuman Jasrotia Jan 19 '18 at 15:49
0

I'm using this script in one of my classes:

public with sharing class AmazonS3Controller {
    public void uploadObject(){

        Blob fileBlob = Blob.valueOf('Unit Test Attachment Body');
        String fileBody = EncodingUtil.base64Encode(fileBlob);
        String now = Datetime.now().formatGMT('EEE, dd MMM yyyy HH:mm:ss z');
        String bucketname = 'emoranbucket';
        String host='emoranbucket.s3.us-east-1.amazonaws.com';
        System.debug('fileBody: ' + fileBody);
        String contentType = 'text/html';
        String fileName = 'text.txt';
        String method = 'PUT';
        String key = '';
        String secret = '';

        HttpRequest req = new HttpRequest();
        req.setMethod(method);
        req.setEndpoint('http://' + bucketname + '.' + host + '/' +UserInfo.getUserId() + '/' + fileName);
        req.setHeader('Host', bucketname + '.' + host);
        req.setHeader('Content-Length', String.valueOf(fileBody.length()));
        req.setHeader('Content-Encoding', 'UTF-8');
        req.setHeader('Content-type', contentType);
        req.setHeader('Connection', 'keep-alive');
        req.setHeader('Date', now);
        req.setHeader('ACL', 'public-read');
        req.setBody(fileBody);

        String stringToSign = 'PUT\n\n' + contentType + '\n' + now + '\n' +'/' + bucketname + '/' + UserInfo.getUserId() + '/' + fileName;

        String encodedStringToSign = EncodingUtil.urlEncode(stringToSign, 'UTF-8');
        Blob mac = Crypto.generateMac('HMACSHA1', Blob.valueOf(stringToSign), Blob.valueOf(secret));
        String signed = EncodingUtil.base64Encode(mac);
        String authHeader = 'AWS' + ' ' + key + ':' + signed;
        req.setHeader('Authorization', authHeader);
        String decoded = EncodingUtil.urlDecode(encodedStringToSign , 'UTF-8');

        Http http = new Http();
        HTTPResponse res = http.send(req);
        System.debug('RESPONSE STRING: ' + res.toString());
        System.debug('RESPONSE STATUS: ' + res.getStatus());
        System.debug('STATUS_CODE: ' + res.getStatusCode());


    }
}

Hope it helps.

  • Thanks. I tried your code. First it gave me error that bucketname.s3.us-east-1.amazonaws.com is blocked due to remote settings. After I have added the bucketname.s3.us-east-1.amazonaws.com in remote settings I get 503 error – Anshuman Jasrotia Jan 20 '18 at 8:26
  • Also this way every time I have to upload to a different bucket I will have to add that bucket to the in the list of all sites in the remote settings – Anshuman Jasrotia Jan 20 '18 at 8:27

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.