4

I want to download all files associated with records (single or multiple records) of a particular object at one click, using apex.

Let's say that there is a custom object myVehicle which holds information such as - name, number (custom unique number), model, colour, brand, price, country and some attachments in many different formats - word, excel, picture, pdf, etc.

I want to create a visualforce page or similar where when user clicks on a button or link, all attachments of vehicles (not all, but based on filter criteria) gets downloaded. Currently we have achieved following -

An Apex Page -

<apex:outputLink value="{!URLFOR('/sfc/servlet.shepherd/version/download/' & vehIdspage & '?')}" target="_document">Attachments Download</apex:outputLink>

Controller Class -

for(myVehicle__c veh: [select Id,Name,country__c,Number__c from myVehicle__c ]){

    for(ContentDocumentLink conDoc:[SELECT Id,ContentDocumentId ,LinkedEntityId FROM ContentDocumentLink WHERE LinkedEntityId=: veh.Id]){
        for (ContentVersion docVersion : [Select Id, VersionData from ContentVersion where ContentDocumentId =:conDoc.ContentDocumentId ]) {

            string ContentDown= docVersion.Id;
            vehIds = conIds + docVersion.Id +'/';
        }
        vehIdspage=vehIds.removeEnd('/'); 
    }
}

with this code, we are able to download multiple files of one record and multiple files of many records in one zip file. But our data may reach to 5000-10,000 records, and each records may have N number of attachments. Constructing URL for these many records may become too large. And I think there might a limit to it.

Also we want to change title of files before downloading.

So it creates 2 questions -

  1. How can we overcome URL limitation ?
  2. And how can title of files be changed before downloading ?

And final question - is there any other alternatives for downloading files ?

1
  • 2
    You're using at least two queries per document, so you'll hit the SOQL limit way before you reach 5,000 records. You'll need an alternative solution if your goal is to download a mountain of files.
    – sfdcfox
    May 28 '20 at 17:48
3
+25

But our data may reach to 5000-10,000 records, and each records may have N number of attachments.

This statement rules out using Apex to provide a single download link for all the related files. There are simply too many to process in a single transaction. You will blow past either your SOQL, Heap, or CPU limits.

Your approach should change based on the final destination for all these files. I'd assume since you are potentially retrieving tens of thousands of files it isn't intended for immediate human consumption.

I'd recommend using the Bulk API from an external system to retrieve the required files. This could be combined with the REST API or Partner API SOQL query to find the required ContentVersion records. If you invert the SOQL query to start from the ContentVersion records you can avoid the need to run one query per ContentDocumentLink.

8
  • thanks for your opinion. I found a other way by using JSZip javascript labrary file to create zip files, via visualforce component by directly fetching file content from ContentVersion object. But again stuck to Heap size error while adding only few files in zip folder. ======= Also can you please provide any example or some reference to how to use Bulk API in achieving such functionality. ======= Is there any functionality to download multiple zip files instead of one to avoid CPU limit or Heap size error, on one click by user. Jun 4 '20 at 10:15
  • 1
    @PankajSingh, downloading multiple ZIP files at once instead of one big one does not get around the heap limitation - you're still storing those files in memory. I agree with Daniel's response that the Bulk API is likely the preferred way of downloading so much data in bulk. Salesforce is not designed to allow a single user to download so much at once. You've likely reached the limit of what is possible there. Look into bulk queries with the Bulk API.
    – nbrown
    Jun 4 '20 at 14:32
  • @Daniel @nbrown Lets say I found the record of contentVersion through API, how should I suppose to process it. As the response return from salesforce for HTTP request is in string format. which can't be converted to a file. Currently I'm sending a request via CURL to the URL - $instance_url/services/data/v48.0/query?q=SELECT Id,VersionData FROM ContentVersion which in return sends response in string format. But I need blob data to create a file. Please can you give an example, as how should I proceed with it ? Jun 8 '20 at 3:17
  • 1
    @PankajSingh, requests to the REST API return VersionData in a binary format if you hit the appropriate endpoint: /services/data/v48.0/sobjects/ContentVersion/<id>/VersionData. You can download the file with cURL, but you'll need to help your computer out in understanding what the binary data represents by outputting it as a file type with the appropriate extension (e.g. PDF, JPEG, TXT): curl <instance-url>/services/data/v48.0/sobjects/ContentVersion/<id>/VersionData -o myfile.jpeg The Content-Type header on the HTTP response will help discern how the data should be interpreted.
    – nbrown
    Jun 8 '20 at 13:04
  • @nbrown This is what I do get back in http header response - ( [0] => HTTP/1.1 200 OK [1] => Date: Tue, 09 Jun 2020 09:32:03 GMT [2] => Strict-Transport-Security: max-age=31536002; includeSubDomains [10] => Cache-Control: no-cache,must-revalidate,max-age=0,no-store,private [11] => Set-Cookie: BrowserId=5qo0Ee; domain=.salesforce.com; path=/; expires=Wed, 09-Jun-2021 09:32:03 GMT; Max-Age=31536000 [12] => Sforce-Limit-Info: api-usage=1464/5000000 [13] => Content-Type: application/octetstream [14] => Transfer-Encoding: chunked ) How can I process? Jun 9 '20 at 9:38

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.