+ Start a Discussion
ForceRookieForceRookie 

Batch callout: How to upload Attachments from multiple objects (dynamically)?

Hi! I am trying to upload an Atttachments based on the selection and criteria from Lightning Component that will be saved to Custom Setting.

For example, the criteria is Opportunity - Stage - Closed Won, I need to query to get all attachments from Closed Won Opportunity and upload them to AWS.

But I don't know how to do it dynamically, please help me.

User-added image

global class UploadAttachments implements Database.Batchable<sObject> {
    
    global Database.QueryLocator start(Database.BatchableContext BC) {
        String query = 'SELECT Id, Objects__c, Fields__c, Field_Value__c FROM ForUploadCustomSettings__c';
        return Database.getQueryLocator(query);
    }

       global void execute(Database.BatchableContext BC, List<sObject> scope) {
        String formattedDateString = Datetime.now().format('EEE, dd MMM yyyy HH:mm:ss z');   
        String host = 's3.amazonaws.com';
        String method = 'PUT';
        
        HttpRequest req = new HttpRequest();
        Http http = new Http();
        
        Set<Id> Ids = new Set<Id>();
        for (sObject so : scope) {
            Ids.add(so.Id);
        }
        List<Attachment> att = [SELECT Id, Name, Body, ContentType FROM Attachment WHERE ParentId IN :Ids];
        
        List<AWScredentialsSettings__c> values = [SELECT Id, ClientKey__c, SecretKey__c, BucketName__c FROM AWScredentialsSettings__c LIMIT 1];
        if (!att.isEmpty() && !values.isEmpty()) {
            String bucketname = values[0].BucketName__c;
            String key = values[0].ClientKey__c;
            String secret = values[0].SecretKey__c;
            String attachmentBody = EncodingUtil.base64Encode(att[0].Body);
            String filename = att[0].Name;
            
            req.setMethod(method);
            req.setEndpoint('https://' + host + '/' + bucketname + '/' + filename); // The file should be uploaded to this path in AWS -- ObjectName/Salesforce Id/Secret Files/filename
            req.setHeader('Content-Length', String.valueOf(attachmentBody.length()));
            req.setHeader('Content-Encoding', 'UTF-8');
            req.setHeader('Content-type', att[0].ContentType);
            req.setHeader('Connection', 'keep-alive');
            req.setHeader('Date', formattedDateString);
            req.setHeader('ACL', 'public-read');
            req.setBody(attachmentBody);
            
            String stringToSign = method+'\n\n\n'+ att[0].ContentType + '\n' + formattedDateString +'\n/'+ bucketname +'/' + filename;
            Blob mac = Crypto.generateMac('HMACSHA1', blob.valueof(stringToSign),blob.valueof(secret));
            String signed  = EncodingUtil.base64Encode(mac);
            String authHeader = 'AWS' + ' ' + secret + ':' + signed;
            req.setHeader('Authorization',authHeader);
            
            HTTPResponse res = http.send(req);
            System.debug('*Resp:' + String.ValueOF(res.getBody()));
            System.debug('RESPONSE STRING: ' + res.toString());
            System.debug('RESPONSE STATUS: ' + res.getStatus());
            System.debug('STATUS_CODE: ' + res.getStatusCode());
        }
    }
    
    global void finish(Database.BatchableContext BC) {
        
    }
}
NagendraNagendra (Salesforce Developers) 
Hi,

Sorry for this issue you are facing.

You need to build out a dynamic query and you'll also need to loop through the scope records so you can run the query and get the IDs for them all.
// Set for storing found ids Set ids = new set();
//Loop through scope for(sObject s:scope){
// Set variables String objectname = s.Objects__c; String fieldname = s.Fields__c; String targetvalue = s.Field_Value__c;
// Build query String query = 'SELECT Id FROM' + objectname + 'WHERE' + fieldname + '= : ' + targetvalue;
// Loop through query and add Ids to set for(sObject sob:Database.query(query)){
Ids.add((String) sob.get(id));
}
Then your attachment query would yield some results.

Thanks,
Nagendra
ForceRookieForceRookie
Hi, Nagendra. You mean I will be querying inside the loop? Is it allowed? I think that's not a best practice.