Envoi des fichiers depuis Salesforce à un Webservice Externe
Salesforce est une plateforme qui offre beaucoup de possibilités aux développeurs mais avec beaucoup de contraintes aussi. Ces contraintes rendent plus passionnant le développement pour des développeurs qui sont toujours à la recherche de nouveaux défis.
Voici un exemple :
Service tiers :
Nous disposons un webservice qui consomme du multipart comme type de
contenu.
C’est à dire un
mixte de texte et de binaire.
Les données attendues sont :
- Un champ message qui contient un texte libre.
- Un champ attachedFile qui contient un fichier binaire.
Salesforce :
Depuis la Console Service, nous disposons un formulaire développé en LWC qui
permet à
l’utilisateur de renseigner un message et attacher un fichier (image, pdf,
…)
puis de soumettre.
La question est maintenant, comment faire côté Apex pour envoyer les informations à notre service externe ?
Proposition de solution :
Transformer le contenu du fichier en base64 depuis le composant LWC
handleUploadFinished(event) {
this.loading = true;
// Get the list of uploaded files
const uploadedFiles = event.detail.files;
const reader = new FileReader();
reader.onload = (readerEvt) => {
let data = readerEvt.target.result;
this.attachedFile = data;
this.loading = false;
}
reader.readAsDataURL(uploadedFiles[0]);
}
Restitution du contenu binaire côté Apex
private static final String RN = '\r\n';
public static String contact(String left, String right) {
Blob bytesLeft = EncodingUtil.base64Decode(left);
Blob bytesRight = EncodingUtil.base64Decode(right);
String combined = EncodingUtil.convertToHex(bytesLeft) + EncodingUtil.convertToHex(bytesRight);
return EncodingUtil.base64Encode(EncodingUtil.convertFromHex(combined));
}
public static String createDatagram(String boundary, String field, String message) {
return createDatagram(boundary, field, message, false);
}
public static String createDatagram(String boundary, String field, String message, boolean footer) {
return createBase64Datagram(boundary, field, EncodingUtil.base64Encode(Blob.valueOf(message)), null, footer);
}
public static String createBase64Datagram(String boundary, String field, String encodedMessage, String filename, boolean footer) {
String header = 'Content-Disposition: form-data; name="' + field + '"';
if(String.isNotBlank(filename)) {
header += '; filename="' + filename + '"';
}
String frn = '';
if (encodedMessage.endsWith('==')) {
encodedMessage = encodedMessage.substring(0, encodedMessage.length() - 2) + '0K';
} else if (encodedMessage.endsWith('=')) {
encodedMessage = encodedMessage.substring(0, encodedMessage.length() - 1) + 'N';
frn = '\n';
} else {
frn = RN;
}
String headerPart = '--' +
boundary +
RN +
header;
String encodedHeader = EncodingUtil.base64Encode(Blob.valueOf(headerPart));
while (encodedHeader.endsWith('=')) {
headerPart += ' ';
encodedHeader = EncodingUtil.base64Encode(Blob.valueOf(headerPart));
}
String part1 = headerPart +
RN +
RN;
String temp = contact(EncodingUtil.base64Encode(Blob.valueOf(part1)), encodedMessage);
String lastPart = frn + (footer ? ('--' + boundary + '--') : '');
return contact(temp, EncodingUtil.base64Encode(Blob.valueOf(lastPart)));
}
- La méthode public static String contact(String left, String right) permet de concaténer deux textes en base64. Nous en avons besoin pour construire facilement notre requête.
- La méthode public static String createBase64Datagram(String boundary, String field, String encodedMessage, String filename, boolean footer) permet de créer une unité de donnée. Par exemple la partie du message et celle du fichier.
-
0K
(zéro K) correspond à l’encodage base64 de\r\n
-
N
correspond à l’encodage de\r
La construction de la requête sous forme de base64.
private String toBodyString(String boundary) {
String message = this.message;
String messageDatagram = createDatagram(boundary, 'message', message, String.isBlank(this.attachedFile));
String attachedFileDatagram = null;
if (String.isNotBlank(this.attachedFile)) {
String base64 = this.attachedFile;
String extension = 'png';
if(String.isNotBlank(base64)) {
if(base64.startsWith('data')) {
Integer index = base64.indexOf(',');
String preString = base64.substring(0, index);
base64 = base64.substring(index + 1);
String part1 = preString.split(';')[0];
extension = part1.split('/')[1];
}
}
String fileName = this.caseNumber + '-' + this.notificationId + '.' + extension;
attachedFileDatagram = createBase64Datagram(boundary, 'attachedFile', base64, fileName, true);
}
String bodyEncoded = messageDatagram;
if(attachedFileDatagram != null) {
bodyEncoded = contact(messageDatagram, attachedFileDatagram);
}
return bodyEncoded;
}
L’envoi des données au webservice
String boundary = '--------------------' + Integer.valueof((Math.random() * 1000)) + '-' + Integer.valueof((Math.random() * 1000));
String body = toBodyString(boundary);
Blob bodyBlob = EncodingUtil.base64Decode(body);
HttpRequest req = new HttpRequest();
req.setEndpoint('callout:mywebserviceNamedCredential/' + this.notificationId);
req.setHeader('Authorization', 'Bearer ' + token.access_token);
req.setHeader(CONTENT_TYPE, MULTIPART_FORM_DATA + '; boundary='+boundary);
req.setHeader(CASE_ID, this.caseId);
req.setMethod(POST);
req.setBodyAsBlob(bodyBlob);