The mandatory final step of any upload — whether you used a single direct upload or the multipart flow. Marks the file record's upload as finished (clears its "Work In Progress" state) so it shows up as a normal, finished file.
Call this only after the file's bytes have actually finished uploading to storage. Calling it early marks an empty or partial file as "done" — anyone who downloads it afterward will see corrupted or missing content, with no warning that anything went wrong.
How to call this method
global static List<ResultObject> completeUpload(List<Id> wipIds)
global static List<ResultObject> completeUpload(List<Id> wipIds, List<String> versionIdList)
Parameters
|
Parameter |
Type |
Required |
Description |
|---|---|---|---|
|
|
|
Yes |
The WIP file record Id(s) to finalize — the |
|
|
|
No |
Only for version-enabled buckets — one storage version Id per entry in |
Return value
Returns List<ResultObject>, one per Id in wipIds, in the same order:
|
Field |
Description |
|---|---|
|
|
|
|
|
Populated when |
What errorMessage can say
|
Message |
Cause |
Fix |
|---|---|---|
|
"Wip Id is null" |
One of the entries in |
Don't pass null Ids — filter them out first. |
|
"No such wip file with provided id" |
An Id in |
Double-check you're passing the |
|
"There is no wip file(s)" |
Every entry in |
Pass at least one non-null Id. |
|
"Insufficient Privileges. You do not have access to update operation." |
The running user doesn't have update access to the file object. |
Grant update permission on the object, or run as a user who has it. |
|
Error code |
You passed a |
Omit |
A partial failure (some files finalized, others not) does not throw an exception — check every status.
What this also does
-
If this org has previews/thumbnails enabled for the object and file type, completing the upload also kicks off preview/thumbnail generation for the file.
-
If this was a new-version upload, the previous version's "is latest version" flag is cleared automatically.
⚠ Before you call this
-
Don't call this before the storage upload has actually finished. Nothing stops you from calling it early — it will happily mark an unfinished file as complete.
-
This is required after both upload paths: the simple direct-upload path, and right after
completeMultiPartUploadon the large-file path. It's easy to forget the second case when you're calling storage directly from client-side code, since at that point you may never come back to Salesforce in the same flow.
Example
List<ResultObject> results = SDriveTools.completeUpload(new List<Id>{ info.wipFileId });
for (ResultObject r : results) {
if (r.status == 'fail') {
System.debug(LoggingLevel.ERROR, 'Failed to finalize upload: ' + r.errorMessage);
}
}
With a version-enabled bucket
Read x-amz-version-id off the response from uploading the file's bytes, and pass it through:
// res is the HttpResponse from uploading info's bytes to storage (see
// "Uploading a file end-to-end" for that step)
String versionId = res.getHeader('x-amz-version-id');
List<ResultObject> results = SDriveTools.completeUpload(
new List<Id>{ info.wipFileId },
new List<String>{ versionId }
);
Verifying the upload succeeded
Neither completeUpload nor the upload step itself automatically confirms the bytes arrived intact — that's on you to check, if you want the extra assurance. Two options, both optional:
-
Compute the file's MD5 checksum yourself before uploading, then compare it against the
ETagheader S3 returns — either from the upload response directly (if you setsuccess_action_status: '201'when uploading, S3's response body is XML containing theETag), or by callinggetAmazonHeadersafterward and readingETagfrom the returned headers. -
If they match, the upload arrived byte-for-byte as sent.