Skip to content

Instantly share code, notes, and snippets.

@douglascayers
Last active April 18, 2021 14:48
Show Gist options
  • Star 17 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save douglascayers/9316ac7badf990fbe4c31bd68448a040 to your computer and use it in GitHub Desktop.
Save douglascayers/9316ac7badf990fbe4c31bd68448a040 to your computer and use it in GitHub Desktop.
JavaScript snippet for uploading fille to Salesforce as ContentVersion and sharing to record as ContentDocumentLink via jsforce and jquery.
<apex:page>
<head>
<script src="{!$Resource.jquery224}"></script> <!-- https://jquery.com/ -->
<script src="{!$Resource.jsforce170}"></script> <!-- https://jsforce.github.io/ -->
<script>$j = jQuery.noConflict();</script>
</head>
<body>
<form>
<input type="file" id="fileInput"/>
</form>
</body>
<script>
var conn = new jsforce.Connection({ accessToken: '{!$Api.Session_Id}' });
/**
* Uses Chatter REST API to upload files up to 2GB.
*/
function uploadFile() {
// ... show waiting indicator
var fileInput = $j('#fileInput');
if ( $j.trim( fileInput.val() ).length == 0 ) {
// ... hide waiting indicator
return;
}
// https://developer.mozilla.org/en-US/docs/Web/API/FileReader/readAsDataURL
var file = fileInput[0].files[0];
var fileName = getFileNameWithExtension( file );
var reader = new FileReader();
reader.addEventListener('load', function() {
saveFile( fileName, reader.result, '{!record.id}' );
});
reader.readAsDataURL( file );
}
/**
* Saves the file asynchronously to Salesforce and shares it to the record.
*
* @param fileName
* Name of the file, for best results should include extension (e.g. 'file.txt')
* @param base64
* Base64 encoded file data
* @param recordId
* The Salesforce record to share the file to
*
* Returns a promise per the jsforce library.
*/
function saveFile( fileName, base64, recordId ) {
var path = '/services/data/v40.0';
return conn.requestPost( path + '/composite/', {
'allOrNone' : true,
'compositeRequest' : [
{
'method' : 'POST',
'url' : path + '/sobjects/ContentVersion',
'referenceId' : 'newFile',
'body' : {
'Title' : fileName,
'PathOnClient' : fileName,
'VersionData' : base64,
'FirstPublishLocationId' : recordId
}
}
]
})
.then( validateCompositeResponse )
.then( function( response ) {
// get new file id
var contentVersionId = null;
for ( var i = 0; i < response.compositeResponse.length; i++ ) {
if ( response.compositeResponse[i].referenceId === 'newFile' ) {
contentVersionId = response.compositeResponse[i].body.id;
break;
}
}
// ... do something with the new file id
}).catch( function( err ) {
console.error( err );
// ... show error to user
}).then( function() {
// this last 'then' after the 'catch' acts like a finally block of try..catch..finally
// https://stackoverflow.com/questions/35999072/what-is-the-equivalent-of-bluebird-promise-finally-in-native-es6-promises
// ... hide waiting indicator
});
}
/**
* Gets the name with extension of a file input reference, like 'file.txt'.
* Replaces any other periods (.) with underscores (_) otherwise Salesforce
* ignores all text in the filename after the first dot.
*
* @param file
* js reference of a file retrieved from an html input element
* e.g. fileInput.files[0]
*/
function getFileNameWithExtension( file ) {
var extIndex = file.name.lastIndexOf( '.' );
var extension = file.name.substring( extIndex );
var fileName = file.name.substring( 0, extIndex );
fileName = fileName.replace( /\./g, '_' );
fileName += extension;
return fileName;
}
/**
* Checks each sub-response for an error code
* and throws error if finds one, else does nothing.
*/
function validateCompositeResponse( response ) {
return new Promise( function( resolve, reject ) {
console.log('validating composite response');
for ( var i = 0; i < response.compositeResponse.length; i++ ) {
var body = response.compositeResponse[i].body[0];
// ignore the 'processing halted' messages as that just indicates
// that sub-request wasn't the one that blew up, keep looking
if ( body && body.errorCode && body.errorCode != 'PROCESSING_HALTED' ) {
reject( body.message );
}
}
resolve( response );
});
}
</script>
</apex:page>
@spacemishka
Copy link

Hello,

i have problem with your script. During the upload of an image, there are few bytes added to the file and so the file gets corrupt.

Can you give me an advise?

thanks
Peter

@chunwu
Copy link

chunwu commented Nov 26, 2018

Hello,

i have problem with your script. During the upload of an image, there are few bytes added to the file and so the file gets corrupt.

Can you give me an advise?

thanks
Peter

The documentation says:

Note: The file's result results in a string that cannot be directly decoded as Base64. To retrieve only the Base64 encoded string, you must remove data:/;base64, from the string.

So you need to strip that prefix:

var data = reader.result.split(',')[1];
saveFile( fileName, data, '{!record.id}' );

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment