Skip to main content

Latest Post

Custom Metadata Types vs Custom Settings vs Custom Labels

  Custom Metadata Types vs Custom Settings vs Custom Labels 💬 In plain words:  Three lookalikes: Custom Metadata = configuration that DEPLOYS with your code (best for app settings). Custom Settings = org/user-specific values, changeable at runtime (hierarchy type is great for bypass switches). Custom Labels = translatable text for the UI. 📌 Example:  API endpoint URLs per environment → Custom Metadata (deploys with code, sandbox vs prod values). A 'Bypass_Automation__c' checkbox an admin flips during data load → hierarchy Custom Setting. The word 'Submit' translated to Hindi → Custom Label. 🎬 Real-Life Example: The Fee Table Trapped Inside Code  Skyline charges a different delivery fee per city. Apex needs those rates on every booking. The Old/Bad Way:  if (city == 'Delhi') fee = 50. Else if (city == 'Mumbai') fee = 65. … Every rate change is a code change, a test run, and a deployment. Why this is bad:  Business data is trappe...

Upload file using multipart/form data from apex salesforce

Here is a solution to upload a file to a third-party system using Apex in Salesforce.

Below is the FormData builder Apex class, which builds the required multipart parameters:

public class FormData {
    private final static String Boundary = '1ff13444ed8140c7a32fc4e6451aa76d';

    public enum EndingType {
        Cr,
        CrLf,
        None
    }

    /**
     * Returns the request's content type for multipart/form-data requests.
     */
    public static String GetContentType() {
        return 'multipart/form-data; charset="UTF-8"; boundary="' + Boundary + '"';
    }

    /**
     * Pad the value with spaces until the base64 encoding is no longer padded.
     */
    private static String SafelyPad(String value, String valueCrLf64, String lineBreaks) {
        String valueCrLf = '';
        Blob valueCrLfBlob = null;

        while (valueCrLf64.endsWith('=')) {
            value += ' ';
            valueCrLf = value + lineBreaks;
            valueCrLfBlob = Blob.valueOf(valueCrLf);
            valueCrLf64 = EncodingUtil.base64Encode(valueCrLfBlob);
        }

        return valueCrLf64;
    }

    /**
     * Write a boundary between parameters to the form's body.
     */
    public static String WriteBoundary() {
        String value = '--' + Boundary + '\r\n';
        Blob valueBlob = Blob.valueOf(value);

        return EncodingUtil.base64Encode(valueBlob);
    }

    /**
     * Write a boundary at the end of the form's body.
     */
    public static String WriteBoundary(EndingType ending) {
        String value = '';

        if (ending == EndingType.Cr) {
            value += '\n';
        } else if (ending == EndingType.None) {
            value += '\r\n';
        }

        value += '--' + Boundary + '--';

        Blob valueBlob = Blob.valueOf(value);
        return EncodingUtil.base64Encode(valueBlob);
    }

    /**
     * Write a key-value pair to the form's body.
     */
    public static String WriteBodyParameter(String key, String value) {
        String contentDisposition = 'Content-Disposition: form-data; name="' + key + '"';
        String contentDispositionCrLf = contentDisposition + '\r\n\r\n';
        
        Blob contentDispositionCrLfBlob = Blob.valueOf(contentDispositionCrLf);
        String contentDispositionCrLf64 = EncodingUtil.base64Encode(contentDispositionCrLfBlob);
        
        String content = SafelyPad(contentDisposition, contentDispositionCrLf64, '\r\n\r\n');
        
        if (key == 'file') {
            System.debug(value);
        }
        
        String valueCrLf = value + '\r\n';
        Blob valueCrLfBlob = Blob.valueOf(valueCrLf);
        
        if (key == 'file') {
            System.debug(valueCrLfBlob);
        }
        
        String valueCrLf64 = EncodingUtil.base64Encode(valueCrLfBlob); 
        content += SafelyPad(value, valueCrLf64, '\r\n');
        
        return content;
    }

    public static String writeFileBody(String key, Blob attachBody, String filename) {
        String header = '--' + Boundary + '\r\n' +
                        'Content-Type: application/octet-stream\r\n' +
                        'Content-Disposition: form-data; name="' + key + '"; filename="' + filename + '"';        
        
        String headerEncoded = EncodingUtil.base64Encode(Blob.valueOf(header + '\r\n\r\n'));
        while (headerEncoded.endsWith('=')) {
            header += ' ';
            headerEncoded = EncodingUtil.base64Encode(Blob.valueOf(header + '\r\n\r\n'));
        }
        
        String bodyEncoded = EncodingUtil.base64Encode(attachBody);
        return headerEncoded + bodyEncoded;
    }    

    public static String append(String key, String value) {
        return FormData.WriteBoundary() + WriteBodyParameter(key, value);
    }
    
    public static Blob makeBlobWithFile(String key, Blob attachBody, String filename, String otherParamsEncoded) {
        String header = '--' + Boundary + '\r\n' +
                        'Content-Type: application/octet-stream\r\n' +
                        'Content-Disposition: form-data; name="' + key + '"; filename="' + filename + '"';        
        
        String headerEncoded = EncodingUtil.base64Encode(Blob.valueOf(header + '\r\n\r\n'));
        while (headerEncoded.endsWith('=')) {
            header += ' ';
            headerEncoded = EncodingUtil.base64Encode(Blob.valueOf(header + '\r\n\r\n'));
        }
        
        String footer = '--' + Boundary + '--';     
        String bodyEncoded = EncodingUtil.base64Encode(attachBody);       
        Blob formBlob = null;    
        String last4Bytes = bodyEncoded.substring(bodyEncoded.length() - 4, bodyEncoded.length());
        
        if (last4Bytes.endsWith('==')) {
            last4Bytes = last4Bytes.substring(0, 2) + '0K';
            bodyEncoded = bodyEncoded.substring(0, bodyEncoded.length() - 4) + last4Bytes;
            String footerEncoded = EncodingUtil.base64Encode(Blob.valueOf(footer));
            formBlob = EncodingUtil.base64Decode(otherParamsEncoded + headerEncoded + bodyEncoded + footerEncoded);
        } else if (last4Bytes.endsWith('=')) {
            last4Bytes = last4Bytes.substring(0, 3) + 'N';
            bodyEncoded = bodyEncoded.substring(0, bodyEncoded.length() - 4) + last4Bytes;
            footer = '\n' + footer;
            String footerEncoded = EncodingUtil.base64Encode(Blob.valueOf(footer));
            formBlob = EncodingUtil.base64Decode(otherParamsEncoded + headerEncoded + bodyEncoded + footerEncoded);
        } else {
            footer = '\r\n' + footer;
            String footerEncoded = EncodingUtil.base64Encode(Blob.valueOf(footer));
            formBlob = EncodingUtil.base64Decode(otherParamsEncoded + headerEncoded + bodyEncoded + footerEncoded);
        }
        
        return formBlob;
    }
    
    public static Blob makeBlob(String ParamsEncoded) {
        String footer = '--' + Boundary + '--';   
        String footerEncoded = EncodingUtil.base64Encode(Blob.valueOf(footer));
        return EncodingUtil.base64Decode(ParamsEncoded + footerEncoded); 
    }
}

Here is the file upload method where multiple parameters (document metadata) and the binary file body are sent:

public void uploadFile(Blob attachBody, String filename) {
    String contentType = FormData.GetContentType();
    String form64 = '';
    
    // Adding document metadata or properties
    form64 += FormData.append('key1', 'Value1');
    form64 += FormData.append('key2', 'Value2');
    
    // Adding document body with file parameter
    Blob formBlob = FormData.makeBlobWithFile('file', attachBody, filename, form64);
    String contentLength = String.valueOf(formBlob.size());
     
    HttpRequest httpRequest = new HttpRequest(); 
    httpRequest.setBodyAsBlob(formBlob);
    httpRequest.setHeader('Connection', 'keep-alive');
    httpRequest.setHeader('Content-Length', contentLength);
    httpRequest.setHeader('Content-Type', contentType);
    httpRequest.setMethod('POST');
    httpRequest.setTimeout(120000);        
    httpRequest.setHeader('Accept', 'application/json');
    httpRequest.setHeader('Authorization', 'Bearer YOUR_TOKEN');
    httpRequest.setEndpoint('YOUR_ENDPOINT_URL');
    
    Http http = new Http();
    HttpResponse res = http.send(httpRequest);
}

Extract From: muenzpraeger / salesforce-einstein-vision-apex

Popular Posts

Salesforce LWC Code for Multi-Select Lookup

Introduction: In Salesforce Lightning Web Components (LWC), implementing a multi-select lookup field can enhance the user experience and provide greater flexibility for selecting multiple related records. In this blog post, we will walk through the process of creating a multi-select lookup field using LWC. We will cover the required code snippets and provide step-by-step instructions to help you implement this functionality in your Salesforce org.