Hello friends, today we are going to discuss Salesforce and Box integration using Apex via Rest API. Mainly we use the Box platform to store files. We also create folders, share folders/files with the team, create team in Box is a cloud content management system.
How to integrate Box with Salesforce.
check this: Send SMS Via Twilio In Apex Rest API
Using Box, you can store your files in the cloud, decide who can view/edit your files, access them on the go. They secure storage of your data and advanced collaboration possibilities.
Highlights Points :
- We can eailsy upload the files upto 50MB above this use chunk api
- Use Rest API.
- We will do Authetication by grant_type=client_credentials, i mean username , password.
- We also share the files/folder by using email id.
Let’s start with an example.
Step 1: In this step, we will create a box account. You can also log in with Gmail.
Step 2: In this, we will set up the app, so we can get the client id and password for auth. if you can’t see the below screen then click here. Create a new app and select a custom app, also select grant_type=client_credentials for authentication.
Step 3: In this we will set two-step verification, by going account setting. click here . after this Authorize the app for this click here.
Step 4: In this step, we will create an apex class in which we will write the logic for getting access tokens, create a folder, upload files, and share folder/file. you can also do more things with Box API. In apex class, we can’t upload blob direct on Box so we will convert this to binary data and upload it in Box Storage.
BoxCtrl.cls :
public class BoxCtrl {
@AuraEnabled
public static String createFolder(String parentFolderId, String folderName){
String accessToken = getAccessToken();
System.debug(accessToken);
String body = '{ "name": "' + folderName + '", "parent": { "id": "' + parentFolderId + '" } }';
String endpoint = 'https://api.box.com/2.0/folders';
// define transaction variables
HttpRequest req = new HttpRequest();
HttpResponse res = new HttpResponse();
Http h = new Http();
req.setEndpoint(endpoint);
req.setMethod('POST');
req.setHeader('Content-Type','application/json');
req.setHeader('Authorization', 'Bearer ' + accessToken);
req.setHeader('Accept', 'application/json');
req.setBody(body);
System.debug(req.getBody());
//req.setTimeout(120000);
res = h.send(req);
if ((res.getStatusCode() == 200 || res.getStatusCode() == 201) && res.getBody() != null && res.getBody() != null) {
Map<String, Object> untypedMap = (Map<String, Object>)JSON.deserializeUntyped(res.getBody());
System.debug(untypedMap);
return 'Folder created successfully!';
}else{
throw newMessageException('Error while creating folder');
}
}
public static void moveFolder(String parentFolderId, String folderId){
String accessToken = getAccessToken();
System.debug(accessToken);
String body = '{ "parent": { "id": "' + parentFolderId + '" } }';
String endpoint = 'https://api.box.com/2.0/folders';
// define transaction variables
HttpRequest req = new HttpRequest();
HttpResponse res = new HttpResponse();
Http h = new Http();
req.setEndpoint(endpoint + '/' + folderId);
req.setMethod('PUT');
req.setHeader('Content-Type','application/json');
req.setHeader('Authorization', 'Bearer ' + accessToken);
req.setHeader('Accept', 'application/json');
req.setBody(body);
System.debug(req.getBody());
res = h.send(req);
if ((res.getStatusCode() == 200 || res.getStatusCode() == 201) && res.getBody() != null && res.getBody() != null) {
Map<String, Object> untypedMap = (Map<String, Object>)JSON.deserializeUntyped(res.getBody());
System.debug(untypedMap);
}
}
public static void shareFolder(String folderId, String shareToUserEmail){
String accessToken = getAccessToken();
String body = '{ "item": { "type": "folder", "id": "' + folderId + '" }, "accessible_by": { "type": "user", "login": "' + shareToUserEmail + '" }, "role": "editor" }';
String endpoint = 'https://api.box.com/2.0/collaborations';
// define transaction variables
HttpRequest req = new HttpRequest();
HttpResponse res = new HttpResponse();
Http h = new Http();
req.setEndpoint(endpoint);
req.setMethod('POST');
req.setHeader('Content-Type','application/json');
req.setHeader('Authorization', 'Bearer ' + accessToken);
req.setHeader('Accept', 'application/json');
req.setBody(body);
res = h.send(req);
if ((res.getStatusCode() == 200 || res.getStatusCode() == 201) && res.getBody() != null && res.getBody() != null) {
Map<String, Object> untypedMap = (Map<String, Object>)JSON.deserializeUntyped(res.getBody());
System.debug(untypedMap);
}
}
public static String getAccessToken(){
String username = 'xxxxxxxxxxxxxxxx';
String password = 'xxxxxxxxxxxxxxxx';
String endpoint = 'https://api.box.com/oauth2/token'; // be sure this is configured in "Remote Site Settings"
String boxSubjectId = '17943781903';
// define transaction variables
HttpRequest req = new HttpRequest();
HttpResponse res = new HttpResponse();
Http h = new Http();
// Configure the request
req.setEndpoint(endpoint);
req.setMethod('POST');
req.setHeader('Content-Type','application/x-www-form-urlencoded');
req.setTimeout(120000);
string payload = 'client_id=' + username + '&client_secret=' + password + '&grant_type=client_credentials&box_subject_type=user&box_subject_id=' + boxSubjectId;
req.setBody(payload);
res = h.send(req);
if ((res.getStatusCode() == 200 || res.getStatusCode() == 201) && res.getBody() != null && res.getBody() != null) {
Map<String, Object> untypedMap = (Map<String, Object>)JSON.deserializeUntyped(res.getBody());
if(untypedMap.containsKey('access_token')){
return (String) untypedMap.get('access_token');
}
return '';
}else{
return '';
}
}
//code to upload files to BOX.COM
@AuraEnabled
public static String uploadFile(String base64, String filename, String folderId){
String accessToken = getAccessToken();
if(base64!=null){
blob base64EncodeFile = base64EncodeFileContent(EncodingUtil.base64Decode(base64), filename);
String uploadEndPointURL='https://upload.box.com/api/2.0/files/content?parent_id='+folderId;
String boundary = '----------------------------741e90d31eff';
String body = '{"name":"' + filename + '", "parent":{"id":"' + folderId + '"}}';
HttpRequest req = new HttpRequest();
req.setBody(body);
req.setBodyAsBlob(base64EncodeFile);
req.setHeader('Content-Type','multipart/form-data; boundary='+boundary);
//req.setHeader('Content-Length',String.valueof(req.getBodyAsBlob().size()));
req.setHeader('Authorization', 'Bearer ' + accessToken);
req.setMethod('POST');
req.setEndpoint(uploadEndPointURL);
req.setTimeout(120000);
//Send request to Box
Http ht = new Http();
HTTPResponse res = ht.send(req);
System.debug('**Files upload Response: ' + res.getBody());
Integer uploadStatusCode=res.getStatusCode();
if(uploadStatusCode==201){
return 'File uploaded successfully.';
}else{
throw newMessageException('Error encountered. Status Code: ' + uploadStatusCode);
}
}else{
throw newMessageException('Please select file.');
}
}
private static AuraHandledException newMessageException(String message) {
AuraHandledException e = new AuraHandledException(message);
e.setMessage(message);
return e;
}
public static blob base64EncodeFileContent(Blob file_body, String file_name){
String boundary = '----------------------------741e90d31eff';
String header = '--'+boundary+'\nContent-Disposition: form-data; name="file"; filename="'+file_name+'";\nContent-Type: application/octet-stream';
String footer = '--'+boundary+'--';
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(file_body);
Blob bodyBlob = 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));
bodyBlob = EncodingUtil.base64Decode(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));
bodyBlob = EncodingUtil.base64Decode(headerEncoded+bodyEncoded+footerEncoded);
} else {
footer = '\r\n' + footer;
String footerEncoded = EncodingUtil.base64Encode(Blob.valueOf(footer));
bodyBlob = EncodingUtil.base64Decode(headerEncoded+bodyEncoded+footerEncoded);
}
return bodyBlob;
}
}
Step 5: In this step, we will create LWC Component for the user interface. I create 2 Card here so first you can create a folder and in the second one you can upload files. We will upload files to the apex In LWC by the custom way.
BoxAPILWC.HTML:
<template>
<!-- loader -->
<div if:true={showSpinner}>
<lightning-spinner
alternative-text="Loading..." variant="brand" class="slds-is-fixed">
</lightning-spinner>
</div>
<!------------->
<div class="slds-tabs_card">
<div class="slds-page-header">
<div class="slds-page-header__row">
<div class="slds-page-header__col-title">
<div class="slds-media">
<div class="slds-media__figure">
<span class="slds-icon_container slds-icon-standard-opportunity">
<lightning-icon icon-name="standard:recipe" alternative-text="recipe" title="recipe"></lightning-icon>
</span>
</div>
<div class="slds-media__body">
<div class="slds-page-header__name">
<div class="slds-page-header__name-title">
<h1>
<span>Box API Call From Apex</span>
<span class="slds-page-header__title slds-truncate" title="Recently Viewed">TechDicer</span>
</h1>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div> <br/>
<!-- create folder card -->
<lightning-card variant="Narrow" title="Box API For Create Folder" icon-name="standard:folder">
<div class="slds-var-p-around_small">
<lightning-layout multiple-rows>
<lightning-layout-item padding="around-small" size="12" medium-device-size="12" large-device-size="12">
<lightning-input name="newFolderName" class="fieldvalidate" type="text"
label="Folder Name" required>
</lightning-input>
</lightning-layout-item>
<lightning-layout-item size="12" class="slds-var-p-top_small">
<lightning-button class="slds-align_absolute-center" variant="brand" label="Create Folder"
onclick={handleCreateFolder}>
</lightning-button>
</lightning-layout-item>
</lightning-layout>
</div>
</lightning-card><br/>
<!-- Upload files card -->
<lightning-card variant="Narrow" title="Box API For Upload Files" icon-name="standard:file">
<div class="slds-var-p-around_small">
<lightning-layout multiple-rows>
<lightning-layout-item padding="around-small" size="12" medium-device-size="12" large-device-size="12">
<lightning-input name="folderName" class="fieldvalidateFile" type="tel"
label="Folder Id" onchange={handleFolderName}>
</lightning-input><br/>
<lightning-input label="File" name="file uploader"
onchange={handleFilesChange} type="file">
</lightning-input>
</lightning-layout-item>
<lightning-layout-item size="12" class="slds-var-p-top_small">
<lightning-button class="slds-align_absolute-center" variant="brand" label="Upload Files"
onclick={uploadFileToBox}>
</lightning-button>
</lightning-layout-item>
</lightning-layout>
</div>
</lightning-card>
</template>
BoxAPILWC.JS:
import { LightningElement, track } from 'lwc';
import createFolder from "@salesforce/apex/BoxCtrl.createFolder";
import uploadFile from '@salesforce/apex/BoxCtrl.uploadFile';
import {ShowToastEvent} from 'lightning/platformShowToastEvent';
export default class BoxAPILWC extends LightningElement {
@track fileName = '';
@track showSpinner = false;
content;
fileData;
newFolderName;
folderId;
handleCreateFolder(event){
const inputValidation = [...this.template.querySelectorAll('.fieldvalidate')]
.reduce((validSoFar, inputField) => {
inputField.reportValidity();
return validSoFar && inputField.checkValidity();
}, true);
if (inputValidation) {
//perform success logic
let inputFields = this.template.querySelectorAll('.fieldvalidate');
inputFields.forEach(inputField => {
if(inputField.name == "newFolderName"){
this.newFolderName = inputField.value;
}
});
this.handleApexCallCreateFolder();
}
}
handleApexCallCreateFolder(){
this.handleSpinner();
console.log(this.newFolderName);
createFolder({parentFolderId : '0', folderName : this.newFolderName})
.then(res=>{
this.ShowToast('Success!', 'Folder Create Successfully', 'success', 'dismissable');
}).catch(err=>{
this.ShowToast('Error!!', err.body.message, 'error', 'dismissable');
}).finally(() => {
this.handleSpinner();
})
}
handleFolderName(event){
this.folderId = event.target.value;
}
// getting file
handleFilesChange(event) {
if(event.target.files.length > 0) {
const file = event.target.files[0]
var reader = new FileReader()
reader.onload = () => {
var base64 = reader.result.split(',')[1]
this.fileData = {
'filename': file.name,
'base64': base64,
'recordId': this.folderId
}
console.log(this.fileData)
}
reader.readAsDataURL(file)
}
}
// Calling apex class to upload the file to box storage
uploadFileToBox() {
this.handleSpinner();
const {base64, filename, recordId} = this.fileData
uploadFile({ base64 : base64, filename:filename, folderId:recordId }).then(result=>{
this.fileData = null
let title = `${filename} uploaded successfully!!`;
this.ShowToast('Success!', title, 'success', 'dismissable');
}).catch(err=>{
this.ShowToast('Error!!', err.body.message, 'error', 'dismissable');
}).finally(() => {
this.handleSpinner();
})
}
handleSpinner(){
this.showSpinner = !this.showSpinner;
}
ShowToast(title, message, variant, mode){
const evt = new ShowToastEvent({
title: title,
message:message,
variant: variant,
mode: mode
});
this.dispatchEvent(evt);
}
}
Output :
36 comments
Not Working
Hi Ram, Can you please explain your issue.
CreateFolder not working
Can you please share me error? And are you giving correct parentFolderId. You can copy this folder Id by Box Admin
Not Working
“error”: “invalid_grant”,
“error_description”: “Grant credentials are invalid”
Check your username and password and also check is this authorized in Box
Hey Rijwan, Could you please tell me where are you authorizing the user?
I have authorized the user by Box Sandbox and you can check my all steps. Check steps 2 and 3. Can you please Share error?
I used the same code which you have shared above. I have created one app but that app is not authorized yet by the Box Admin. While hitting endpoint I am getting “System.HttpResponse[Status=Bad Request, StatusCode=400]”. Could you please let me know what mistake I am making here. Thanks
Read the document of box api app setup, search the issue what you are getting
public static string getAccessToken(){
String clientid = ‘*****’;
String clientpassword = ‘*****’;
string username = ‘*****’;
string password = ‘*****’;
String endpoint = ‘https://api.box.com/oauth2/token’;
String boxSubjectId = ‘19487182152’;
HttpRequest req = new HttpRequest();
HttpResponse res = new HttpResponse();
Http h = new Http();
// Configure the request
req.setEndpoint(endpoint);
req.setMethod(‘POST’);
req.setHeader(‘Content-Type’,’application/x-www-form-urlencoded’);
string payload = ‘grant_type=client_credentials&client_id=’+clientid+’&client_secret=’+clientpassword+’&username=’+username+’&password=’+password;
system.debug(‘payload–‘+payload);
req.setBody(payload);
res = h.send(req);
system.debug(‘res–‘+res);
if ((res.getStatusCode() == 200 || res.getStatusCode() == 201) && res.getBody() != null && res.getBody() != null) {
Map untypedMap = (Map)JSON.deserializeUntyped(res.getBody());
if(untypedMap.containsKey(‘access_token’)){
return (String) untypedMap.get(‘access_token’);
}
return ”;
}else{
return ”;
}
This code I am using, but getting “error”:”invalid_grant”,”error_description”:”Grant credentials are invalid”.
Can you please check your App in Box dashboard
What is this String boundary = ‘—————————-741e90d31eff’? From where you got this value?
I got this from a document
Could you please tell me will this code upload any file in readable format like png, csv, pdf, mov etc or only text file?
All files. is your box integrations working?
Yes, It is working, but I am not able to upload files larger than 7mb to box. How can we overcome this issue?
what is the error?
Hi can you share your error
string length exceeds maximum: 12000000.
Hi I think you need to use direct upload By LWC without the use of Apex class, Below is the link please check
https://techdicer.com/upload-image-by-http-callout-in-lwc/
Hi, Do you have any idea that how can I upload file to BOX directly from js by using fetch API or XMLHTTP. Thanks
Hi,
You can check my blog for upload images directly from JS
https://techdicer.com/upload-image-by-http-callout-in-lwc/
The assignment submission period was over and I was nervous, baccaratcommunity and I am very happy to see your post just in time and it was a great help. Thank you ! Leave your blog address below. Please visit me anytime.
Hi,
Thank you for your useful information.
I can not able to upload files larger to box. (10MB)
Error ‘Apex heap size too large’
Do you have any good solutions ?
Thanks
Hi Reinu, Apex has some limit but you can directly upload the images Via. LWC without the use of Apex,
https://techdicer.com/upload-image-by-http-callout-in-lwc/
Hey Rijwan.
I have two doubts
1) from the Apex code you posted , I just need to upload a file to Box folder
I have the folder id with me.
I have changed clientSecret clientId and BoxSubjectId as per your instructions
Now If I try to upload a file into the folder using its folder id , I am getting 400 bad request
2)Then I tried to upload file to its root folder by setting the parent id to 0 , I am getting “Files Uploaded Successfully” message , but the file is not shown in my Box Folder. Even if I upload the same file again , I am getting a 409 code with a message like “File name already exists” kind of message
Please help me dude!!!
Check Box setting , if file is uploading then file will be there.
By Box Setting , do you mean Account Settings or some other Settings?
Rijwan,
If possible could you please upload another version of this code where Authorization is done using JWT . I tried but I have some issues regarding Certificate Uploading in Salesforce
I want to upload multiple files, can you give me code for this functionality.
Hi @Minerva, you can do this call apex method in for loop in lwc
I have followed the same steps I worked for but after a few days, it is giving me invalid_grant.
Check box app , is this active or not .
I want to create one batch class that fetch related files from record and upload it to box folder, i tried it but it give heap size error.
This is due to the file size issue, did you assign this to any variable.