First step in building a custom (non-S-Drive-UI) file upload. For each file you want to upload, this method validates it, creates a "Work In Progress" (WIP) Salesforce record for it, and returns a signed policy your code then uses to upload the actual file bytes directly to the storage bucket (S3/GCS).
This does not upload any file bytes itself. You (or your client-side code) still have to send the file to storage, and then call completeUpload to mark it finished.
This is one piece of a multi-step process — see Uploading a file end-to-end for how this fits together with the rest.
How to call this method
global static List<UploadRequestInfo> initializeUpload(String objectId, List<SObject> attachments, Map<String,String> policyMap)
global static List<UploadRequestInfo> initializeUpload(String objectId, List<SObject> attachments, List<Map<String,String>> policyMapList)
global static List<UploadRequestInfo> initializeUpload(String objectId, List<SObject> attachments, List<Map<String,String>> policyMapList, List<String> idToUploadNewVersionList)
All three do the same underlying work; pick based on how much control you need:
-
The first, if every file in this batch should share the same extra storage-policy conditions (or you don't need any).
-
The second, if each file needs its own policy conditions.
-
The third, if you're uploading new versions of files that already exist, rather than brand-new files.
Parameters
|
Parameter |
Type |
Required |
Description |
|---|---|---|---|
|
|
|
Usually yes |
The Id of the parent record these files belong to — e.g. an Account, Case. Leave blank for SDrive Tab (cg__S3Object__c) files. |
|
|
|
Yes |
One not-yet-inserted record per file, all of the same SObject type (e.g. all |
|
|
|
No |
Extra conditions to add to the storage-service upload policy for each file (e.g. custom form fields your bucket's policy requires). Pass |
|
|
|
No |
One existing file record Id per entry in |
Return value
Returns List<UploadRequestInfo>, one entry per item in attachments, in the same order. The fields you'll use:
|
Field |
Description |
|---|---|
|
|
The Id of the WIP Salesforce record this method just inserted — save it, you'll need it for |
|
|
The storage key (path) this file will live at once uploaded. |
|
|
Echoed back from what you provided. |
|
|
Where to send the upload request. |
|
|
The signed values you include as form fields when uploading directly to the bucket. |
|
|
|
|
|
The maximum number of parts allowed if you use the multipart flow for this file (10,000 for S3 — see initializeMultiPartUpload for how this differs on GCS). |
|
|
Whether the bucket this file is going into has versioning enabled. |
|
|
Whether to use the accelerated S3 endpoint. |
Common errors
All of these come back as SDriveException.
|
Cause |
Fix |
|---|---|
|
The file name or file size field on one of your |
Set both fields on every record before calling. |
|
The file name starts with a space or a dot, or contains any of `\ / : * ? " < > | |
Rename the file so it doesn't start with a space/dot and avoids those characters. |
|
The file size is 0. |
Only upload files with a size greater than 0. |
|
The file exceeds this org's configured maximum file size. |
Upload a smaller file, or raise the |
|
A non-WIP file already has this name in the destination folder — or another user has a WIP (in-progress) upload with this same name started in the last 12 hours. (Error code |
Use a different file name, or delete/finish the conflicting file first. |
|
The name conflict is with a folder, not a file. (Error code |
Rename the file, or the folder. |
|
|
Pass a well-formed Id. |
|
You're uploading multiple files in one call, and their parent lookup fields don't all match. (Error code |
Every record in |
|
The running user doesn't have create access to the file object. |
Grant create permission on the object, or run as a user who has it. |
Setting a custom Content-Disposition (or other) policy condition
The signed policy initializeUpload builds only allows the form fields it explicitly sets — that's why, for example, sending a Content-Disposition field when you upload (see the form-fields table on Uploading a file end-to-end) requires you to add a matching condition yourself via policyMap:
```apex Map<String,String> policyMap = new Map<String,String>{ '$Content-Disposition' => 'attachment; filename' };
List<UploadRequestInfo> uploadInfos = SDriveTools.initializeUpload(caseId, attachments, policyMap); ```
A key starting with $ becomes a "starts-with" condition in the signed policy — it allows any Content-Disposition value beginning with attachment; filename (so you can append the actual file name when you send the form field), rather than requiring an exact match. A key without the $ prefix requires an exact match instead.
Before you call this
-
This does not upload any bytes. After calling it, your code — typically client-side JavaScript, since Apex callouts have size and time limits unsuitable for large files — must send the actual file content to the bucket using the returned
policy/signature/etc., and only then callcompleteUpload. -
For small files, one direct request to
bucketName/s3Endpointusing the returned policy is enough. For larger files, use the multipart flow instead — see initializeMultiPartUpload. -
Throws
SDriveExceptionif: the file name is invalid, the file size is 0, the file exceeds the org's configured maximum file size, a file with the same name already exists in the destination folder (error code409) and duplicates aren't allowed for that object, or the running user doesn't have create access to the file object.
Example
// Build one not-yet-inserted file record per file to upload
cg__CaseFile__c file1 = new cg__CaseFile__c(
cg__File_Name__c = 'invoice.pdf',
cg__File_Size_in_Bytes__c = 482913,
cg__Case__c = caseId,
cg__Parent_Folder_Id__c = null // null = uploading to the case's root, not a subfolder
);
List<SObject> attachments = new List<SObject>{ file1 };
List<UploadRequestInfo> uploadInfos = SDriveTools.initializeUpload(
caseId,
attachments,
(Map<String,String>) null
);
UploadRequestInfo info = uploadInfos[0];
System.debug('WIP record created: ' + info.wipFileId);
System.debug('Upload the file bytes to: ' + info.s3Endpoint + '/' + info.bucketName);
System.debug('Using storage key: ' + info.fileLocation);
// Next: upload the actual bytes using info.policy / info.signature / etc.,
// then call SDriveTools.completeUpload(new List<Id>{ info.wipFileId }).
// See "Uploading a file end-to-end" for the full request this requires.
If something goes wrong
If the storage upload fails after this method has already created the WIP record, don't leave it stranded — call cancelUpload (documented separately) with the wipFileId to clean it up.