Skip to content

Instantly share code, notes, and snippets.

@jonny-harte
Last active November 12, 2022 00:31
Show Gist options
  • Save jonny-harte/4d49f6fbff40d7fdcdb9c6b5e8a20b57 to your computer and use it in GitHub Desktop.
Save jonny-harte/4d49f6fbff40d7fdcdb9c6b5e8a20b57 to your computer and use it in GitHub Desktop.
public with sharing class HttpFormDataHandler {
public static Blob createRequestBody(String boundary, Object request) {
String last4Bytes = '';
List<String> requestBody = new List<String>();
Map<String, Object> requestMap = (Map<String, Object>) JSON.deserializeUntyped(
JSON.serialize(request)
);
for (String key : requestMap.keySet()) {
try {
if (requestMap.get(key) == null) {
continue;
}
for (Object value : (List<Object>) requestMap.get(key)) {
String entry = createBase64RequestBodyEntry(
last4Bytes,
boundary,
key,
value
);
last4Bytes = entry.substring(entry.length() - 4, entry.length());
requestBody.add(entry.removeEnd(last4Bytes));
}
} catch (TypeException e) {
String entry = createBase64RequestBodyEntry(
last4Bytes,
boundary,
key,
requestMap.get(key)
);
last4Bytes = entry.substring(entry.length() - 4, entry.length());
requestBody.add(entry.removeEnd(last4Bytes));
}
}
requestBody.add(
encodeBase64StringWithNewLine(last4Bytes, '--' + boundary + '--')
);
return EncodingUtil.base64Decode(String.join(requestBody, ''));
}
public static String createContentDisposition(String name, String value) {
String schemaName = ExternalServiceHelper.translateVariable(name);
return 'Content-Disposition: form-data; name="' +
schemaName +
'"\r\n\r\n' +
value;
}
public static String createContentDispositionFile(
String name,
ContentVersion file
) {
String contentType = MimeHelper.getMimeType(file.FileExtension);
String schemaName = ExternalServiceHelper.translateVariable(name);
return 'Content-Disposition: form-data; name="' +
schemaName +
'"; filename="' +
file.Title +
'.' +
file.FileExtension +
'"\r\nContent-Type: ' +
contentType;
}
public static Param createParam(String name, ContentVersion file) {
String contentDisposition = createContentDispositionFile(name, file);
String encodedVersionData = EncodingUtil.base64Encode(file.VersionData);
Param param = new Param(contentDisposition, encodedVersionData);
return param;
}
private static String createBase64RequestBodyEntry(
String last4Bytes,
String boundary,
String key,
Object value
) {
String entry = '--' + boundary + '\r\n';
try {
Param param = (Param) JSON.deserialize(
JSON.serialize(value),
Param.class
);
entry += param.contentDisposition;
if (param.file != null) {
// TODO: Split into own function.
String encodedEntry = '';
if (last4Bytes == '') {
encodedEntry = encodeBase64String(entry + '\r\n\r\n');
} else {
encodedEntry = encodeBase64StringWithNewLine(
last4Bytes,
entry + '\r\n\r\n'
);
}
while (encodedEntry.endsWith('=')) {
entry += ' ';
if (last4Bytes == '') {
encodedEntry = encodeBase64String(entry + '\r\n\r\n');
} else {
encodedEntry = encodeBase64StringWithNewLine(
last4Bytes,
entry + '\r\n\r\n'
);
}
}
return encodedEntry + param.file;
}
} catch (JSONException e) {
entry += createContentDisposition(key, (String) value);
}
if (last4Bytes == '') {
return encodeBase64String(entry);
}
return encodeBase64StringWithNewLine(last4Bytes, entry);
}
private static String encodeBase64String(String value) {
if (String.isBlank(value)) {
return value;
}
return EncodingUtil.base64Encode(Blob.valueOf(value));
}
private static String encodeBase64String(Blob value) {
if (value == null) {
return '';
}
return EncodingUtil.base64Encode(value);
}
private static String encodeBase64StringWithNewLine(
String encodedText,
String newText
) {
String paddingEnd = '';
String paddingStart = '';
String paddedText = '';
String last4Bytes = encodedText.substring(
encodedText.length() - 4,
encodedText.length()
);
if (last4Bytes.endsWith('==')) {
paddingEnd = last4Bytes.substring(0, 2) + '0K';
paddedText =
encodedText.substring(0, encodedText.length() - 4) + paddingEnd;
} else if (last4Bytes.endsWith('=')) {
paddingEnd = last4Bytes.substring(0, 3) + 'N';
paddingStart = '\n';
paddedText =
encodedText.substring(0, encodedText.length() - 4) + paddingEnd;
} else {
paddingStart = '\r\n';
paddedText = encodedText;
}
return paddedText + encodeBase64String(paddingStart + newText);
}
public class Param {
private String contentDisposition;
private String file;
public Param(String contentDisposition, String file) {
this.contentDisposition = contentDisposition;
this.file = file;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment