通过 Apex 在 REST API 中发送文件

Sending Files in REST API via Apex

我正在与接受在其系统中上传文件的供应商之一进行整合。这样做时,他们要求文件以 multipart/form-data 格式发送,格式如下 !!

我尝试使用 Base64 字符串将其配置为多部分表单数据,但仍然无法正常工作!

-H "accept: application/json"
-H "x-api-key: API_KEY" 
-H "x-api-token: API_TOKEN" 
-H "Content-Type: multipart/form-data" 
-F "file=@b4b35527-522d-4228-a266-bdaa80e28a8b.jpg;type=image/jpeg"

salesforce apex 标注不支持您正在查找的内容。 它仍在 salesforce Idea Exchange 中,这里是 link 相同的。

https://trailblazer.salesforce.com/ideaView?id=08730000000Kr80AAC

您可以为其投票,但目前 multipart/form-data header 不受支持。

如果您有任何问题,请告诉我。

Salesforce 确实支持 multipart/form-data,但它不支持将图像作为 Blob 发送。

根据我的要求,我需要将图像/PDF/附件作为表单数据发送。我使用了稍微修改过的 http://www.fishofprey.com/2017/04/steps-required-to-support-posting.html

我得到了另一种 blob 方法,可以将图像和附件作为 blob 包含在内。目前我还没有为视频和其他文档添加编解码器,但很快我会添加这些,因为 API 确实支持那个。

在此处查找修改后的 HttpFormBuilder 版本,将图像和其他附件作为 blob 包含在内。我们只需要添加适当的编解码器。

public class HttpFormBuilder_Custom {
    private final static string Boundary = '1ff13444ed8140c7a32fc4e6451aa76d';
    public static string GetContentType() {
        return 'multipart/form-data; charset="UTF-8"; boundary="' + Boundary + '"';
    }
    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;
    }
    public static string WriteBoundary() {
        string value = '--' + Boundary + '\r\n';
        blob valueBlob = blob.valueOf(value);

        return EncodingUtil.base64Encode(valueBlob);
    }
    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');
        string valueCrLf = value + '\r\n';
        blob valueCrLfBlob = blob.valueOf(valueCrLf);
        string valueCrLf64 = EncodingUtil.base64Encode(valueCrLfBlob);

        content += SafelyPad(value, valueCrLf64, '\r\n');

        return content;
    }

    /**
     *  Helper enum indicating how a file's base64 padding was replaced.
     */
    public enum EndingType {
        Cr,
        CrLf,
        None
    }
    public static string WriteBlobBodyParameter(string key, string file64, string fileName) {
        
        String mimeType = resolveMimeType(fileName);
        
        string contentDisposition = 'Content-Disposition: form-data; name="' + key + '"; filename="'+fileName+'"';
        string contentDispositionCrLf = contentDisposition + '\r\n';
        blob contentDispositionCrLfBlob = blob.valueOf(contentDispositionCrLf);
        string contentDispositionCrLf64 = EncodingUtil.base64Encode(contentDispositionCrLfBlob);
        string content = SafelyPad(contentDisposition, contentDispositionCrLf64, '\r\n');
        
        string contentTypeHeader = 'Content-Type: ' + mimeType;
        string contentTypeCrLf = contentTypeHeader + '\r\n\r\n';
        blob contentTypeCrLfBlob = blob.valueOf(contentTypeCrLf);
        string contentTypeCrLf64 = EncodingUtil.base64Encode(contentTypeCrLfBlob);
        content += SafelyPad(contentTypeHeader, contentTypeCrLf64, '\r\n\r\n');
        
        integer file64Length = file64.length();
        String last4Bytes = file64.substring(file64.length()-4,file64.length());

        // Avoid padding the file data with spaces, which SafelyPad does
        // http://salesforce.stackexchange.com/a/33326/102
        EndingType ending = EndingType.None;
        if (last4Bytes.endsWith('==')) {
            last4Bytes = last4Bytes.substring(0,2) + '0K';
            file64 = file64.substring(0,file64.length()-4) + last4Bytes;
            ending = EndingType.CrLf;
        } else if (last4Bytes.endsWith('=')) {
            last4Bytes = last4Bytes.substring(0,3) + 'N';
            file64 = file64.substring(0,file64.length()-4) + last4Bytes;
            ending = EndingType.Cr;
        }
               
        content += file64;
        
        content += WriteBoundary(ending);
        return content;
    }
    
    private static String resolveMimeType(String fileName) {
        String fileType = fileName.substringAfterLast('.');
        String mimeType = 'image/png'; // fallback value
        if (fileType.equalsIgnoreCase('png')) {
            mimeType = 'image/png';
        } else if (fileType.equalsIgnoreCase('jpeg') || fileType.equalsIgnoreCase('jpg')) {
            mimeType = 'image/jpg';
        } else if (fileType.equalsIgnoreCase('pgm')) {
            mimeType = 'image/x-portable-graymap';
        } else if (fileType.equalsIgnoreCase('ppm')) {
            mimeType = 'image/x-portable-pixmap';            
        }
        return mimeType;
    }
}