Download Selected Salesforce Files as Zip File

Skuid Community,

We had a request to download selected files into a single zip file. We looked at solutions that involved some JavaScript libraries and some Apex. Based on a posting by another community member Nicholas L. we found that Salesforce’s page that allows you to download a file also supports downloading a Zip file with multiple selected files.

Here is the format of the URL. You just need the ContentVersionId for each selected file separated by a ‘/’:

/sfc/servlet.shepherd/version/download/068xxxxxxxxxxxxxxx/068yyyyyyyyyyyyyyy

You can find more details here: http://cropredysfdc.com/2016/03/16/download-multiple-contentversion-files-as-zip

Here is a sample Skuid page that contains a snippet to get the selected file ids and download the selected files as a Zip. It should work in any Salesforce org. This is a Version 1 Skuid page. The page loads a contact record and its related files. This ‘hack’ only works with Salesforce/Chatter Files.

To demonstrate this feature:

  • Install the page
  • Upload some files to the Files tab
  • Check the box next to the files that you want to download
  • Click the mass action 'Download Selected as Zip'
Thanks Nicholas L.!

Bill

<skuidpage showheader="true" personalizationmode="server" showsidebar="true" tabtooverride="Contact">
<models>
<model id="Contact" datasource="salesforce" createrowifnonefound="false" query="true" sobject="Contact" limit="1">
<fields>
<field id="FirstName"/>
<field id="LastName"/>
<field id="CreatedDate"/>
<field id="MailingCity"/>
<field id="MailingStreet"/>
<field id="MailingState"/>
<field id="MailingCountry"/>
<field id="MailingPostalCode"/>
<field id="LastModifiedDate"/>
<field id="Phone"/>
<field id="Email"/>
<field id="Id"/>
<field id="AccountId"/>
<field id="Account.Name"/>
<field id="Birthdate"/>
<field id="Description"/>
</fields>
<conditions>
<condition type="param" value="id" enclosevalueinquotes="true" field="Id" operator="="/>
</conditions>
<actions/>
</model>
<model id="ContentDocumentLink" datasource="salesforce" createrowifnonefound="false" query="true" sobject="ContentDocumentLink" limit="20">
<fields>
<field id="ContentDocumentId"/>
<field id="ContentDocument.Title"/>
<field id="LinkedEntityId"/>
<field id="LinkedEntity.Name"/>
<field id="ContentDocument.LatestPublishedVersionId"/>
<field id="ContentDocument.LatestPublishedVersion.Title"/>
<field id="Id"/>
<field id="ContentDocument.Id"/>
<field id="ContentDocument.LatestPublishedVersion.Id"/>
</fields>
<conditions>
<condition type="modelmerge" value="" model="Contact" enclosevalueinquotes="true" field="LinkedEntityId" operator="=" mergefield="Id" novaluebehavior="noquery" fieldtargetobjects="Contact"/>
</conditions>
<actions/>
</model>
<model id="LastUploaded" datasource="salesforce" createrowifnonefound="false" query="false" sobject="ContentDocumentLink" limit="1" orderby="SystemModstamp DESC">
<fields>
<field id="ContentDocumentId"/>
<field id="ContentDocument.Title"/>
<field id="LinkedEntityId"/>
<field id="LinkedEntity.Name"/>
<field id="ContentDocument.LatestPublishedVersionId"/>
<field id="ContentDocument.LatestPublishedVersion.Title"/>
<field id="Id"/>
<field id="ContentDocument.Id"/>
<field id="SystemModstamp"/>
</fields>
<conditions>
<condition type="modelmerge" value="" model="Contact" enclosevalueinquotes="true" field="LinkedEntityId" operator="=" mergefield="Id" novaluebehavior="noquery" fieldtargetobjects="Contact"/>
</conditions>
<actions/>
</model>
<model id="Selected" query="true" createrowifnonefound="true" datasource="Ui-Only" processonclient="true">
<fields>
<field id="CurrentIndex" displaytype="DOUBLE" label="CurrentIndex" length="3" defaultvaluetype="fieldvalue" ogdisplaytype="TEXT" precision="3" scale="0"/>
</fields>
<conditions/>
<actions/>
</model>
</models>
<components>
<pagetitle uniqueid="sk-2nnOO--83" model="Contact">
<maintitle>
<template>{{FirstName}} {{LastName}}</template>
</maintitle>
<subtitle>
<template>{{Model.label}}</template>
</subtitle>
<actions>
<action type="savecancel" uniqueid="sk-zZmyt-88" window="self">
<models>
<model>ContentDocumentLink</model>
</models>
<savehotkeys>
<hotkey key="s" modifiers="ctrl"/>
</savehotkeys>
</action>
</actions>
</pagetitle>
<tabset uniqueid="sk-2nnZAZ-122" renderas="" defertabrendering="true" rememberlastusertab="true">
<tabs>
<tab name="Demographics">
<components>
<grid uniqueid="sk-1JOn4K-289">
<divisions>
<division verticalalign="top" behavior="specified" width="250px">
<components>
<image source="url" uniqueid="sk-2om0aY-1786" datasource="salesforce" behavior="button" model="Contact" url="/sfc/servlet.shepherd/version/download/{{{Description}}}" alt="Click to select another image">
<styles>
<styleitem type="itemsize" width="auto" height="auto"/>
<styleitem type="border"/>
</styles>
<interactions>
<interaction type="tap">
<action type="requeryModel" model="ContentDocumentLink" behavior="standard"/>
<action type="showPopup">
<popup title="Select Image" width="70%">
<components>
<deck searchmethod="server" searchbox="false" columngutter=".75em" rowgutter=".75em" model="ContentDocumentLink" filtersposition="top" filterswidth="150px" showsavecancel="false" behavior="flex" verticalalign="top" ratio="1" minwidth="150px" uniqueid="sk-2oqyo7-658" buttonposition="" pagesize="5" emptysearchbehavior="query">
<components>
<wrapper uniqueid="sk-2or6aG-702">
<components>
<image source="url" uniqueid="sk-2or1Bk-683" datasource="salesforce" behavior="none" model="ContentDocumentLink" url="/sfc/servlet.shepherd/version/download/{{{ContentDocument.LatestPublishedVersionId}}}">
<styles>
<styleitem type="itemsize" width="custom">
<styles>
<styleitem property="width" value="350px"/>
</styles>
</styleitem>
<styleitem type="border"/>
</styles>
<conditions>
<condition type="contextrow" field="Id" mergefield="Id"/>
</conditions>
</image>
</components>
<styles>
<styleitem type="background"/>
<styleitem type="border"/>
<styleitem type="size" width="custom" height="collapse">
<styles>
<styleitem property="max-width" value="300px"/>
</styles>
</styleitem>
</styles>
</wrapper>
</components>
<massactions/>
<interactions>
<interaction type="tap" direction="either">
<action type="updateRow" fieldmodel="Contact" affectedrows="context" field="Description" enclosevalueinquotes="true" value="{{ContentDocument.LatestPublishedVersionId}}">
<models/>
<popup title="{{Model.label}}: {{Name}}" width="80%">
<components/>
</popup>
</action>
<action type="save" rollbackonanyerror="true">
<models>
<model>Contact</model>
</models>
<onerroractions>
<action type="blockUI" message="There was an error" timeout="3000"/>
<action type="unblockUI" message="There was an error" timeout="3000"/>
</onerroractions>
</action>
<action type="closeTopmostPopup"/>
</interaction>
</interactions>
<actions/>
<styles>
<styleitem type="border"/>
</styles>
<searchfields/>
</deck>
</components>
</popup>
</action>
</interaction>
</interactions>
</image>
<file storeas="contentdocumentwithrecord" displayas="filename" uniqueid="sk-2oxn1B-383" datasource="salesforce" model="Contact" label="New Contact Image">
<uploadsuccessactions>
<action type="requeryModel" model="LastUploaded" behavior="standard"/>
<action type="updateRow" fieldmodel="Contact" affectedrows="context" field="Description" enclosevalueinquotes="true" value="{{{$Model.LastUploaded.data.0.ContentDocument.LatestPublishedVersionId}}}"/>
<action type="save">
<models>
<model>Contact</model>
</models>
</action>
</uploadsuccessactions>
<uploadfailureactions/>
</file>
</components>
</division>
<division verticalalign="top" behavior="flex" ratio="1" minwidth="100px">
<components>
<basicfieldeditor uniqueid="sk-2nnOO--84" model="Contact" mode="read" buttonposition="" showsavecancel="false" layout="" showheader="true">
<columns>
<column width="50%">
<sections>
<section title="Basics" collapsible="no">
<fields>
<field id="FirstName" type="" uniqueid="sk-zZmzM-96" valuehalign=""/>
<field id="LastName" uniqueid="sk-zZmzP-98"/>
<field id="Phone" uniqueid="sk-zaxUD-370" cssclass="" snippet="businessPhoneRender"/>
<field id="Email" type="" uniqueid="sk-13zjI1-245" valuehalign=""/>
<field id="AccountId" uniqueid="sk-1VHq5D-261" pagesize="5" optionsource="" redirecttype="datasourcedefault">
<searchfields soslfields="All Fields" usesosl="true"/>
<filters/>
<renderconditions logictype="and" onhidedatabehavior="keep"/>
<enableconditions/>
</field>
<field id="Birthdate" uniqueid="sk-22tbDe-276"/>
</fields>
</section>
<section title="Image" collapsible="no">
<fields>
<field uniqueid="sk-2ovbdg-953" id="Description"/>
</fields>
</section>
</sections>
</column>
<column width="50%">
<sections>
<section title="Address" collapsible="no">
<fields>
<field id="MailingStreet" uniqueid="sk-zZmzp-115"/>
<field id="MailingCity" uniqueid="sk-zZmzq-117"/>
<field id="MailingState" uniqueid="sk-zZmzr-119"/>
<field id="MailingPostalCode" uniqueid="sk-zZmzv-121"/>
<field id="MailingCountry" uniqueid="sk-zZmzz-123"/>
</fields>
</section>
</sections>
</column>
</columns>
</basicfieldeditor>
</components>
</division>
</divisions>
<styles>
<styleitem type="background" bgtype="none"/>
</styles>
</grid>
</components>
</tab>
<tab name="Files" loadlazypanels="true">
<components>
<file datasource="salesforce" uniqueid="sk-1Yfw5z-376" model="Contact" displayas="filename" storeas="contentdocumentwithrecord">
<uploadsuccessactions>
<action type="requeryModels" behavior="standard" model="ContactFeed">
<models>
<model>ContentDocumentLink</model>
</models>
</action>
</uploadsuccessactions>
<uploadfailureactions/>
<renderconditions logictype="and"/>
</file>
<skootable uniqueid="sk-1MPZvX-293" model="ContentDocumentLink" mode="read" buttonposition="" createrecords="true" pagesize="10" showexportbuttons="false" searchbox="true" searchmethod="server" showerrorsinline="true" showsavecancel="false" showconditions="true" allowcolumnreordering="true" responsive="true" alwaysresetpagination="false">
<fields>
<field id="ContentDocumentId" uniqueid="fi-1MPhMg-312" hideable="true"/>
<field type="COMBO" hideable="true" uniqueid="fi-2ooSE_-1827" allowhtml="true">
<label>View</label>
<template>&amp;lt;a target="_blank" href="/sfc/servlet.shepherd/version/download/{{{ContentDocument.LatestPublishedVersionId}}}"&amp;gt;{{{ContentDocument.Title}}}&amp;lt;/a&amp;gt;</template>
</field>
<field type="IMAGE" hideable="true" uniqueid="fi-IA_-437">
<label>Image</label>
<url>/sfc/servlet.shepherd/version/renditionDownload?rendition=THUMB240BY180&amp;amp;versionId={{{ContentDocument.LatestPublishedVersion.Id}}}</url>
</field>
</fields>
<rowactions>
<action type="edit"/>
<action type="delete"/>
<action type="multi" label="Set {{ContentDocument.Title}} as Image" icon="sk-icon-personal-info">
<actions>
<action type="updateRow" fieldmodel="Contact" affectedrows="context" field="Description" enclosevalueinquotes="true" value="{{{ContentDocument.LatestPublishedVersionId}}}"/>
<action type="save" rollbackonanyerror="true">
<models>
<model>Contact</model>
</models>
<onerroractions>
<action type="blockUI" message="There was an error" timeout="3000"/>
<action type="unblockUI" message="There was an error" timeout="3000"/>
</onerroractions>
</action>
<action type="requeryModel" model="Contact" behavior="standard"/>
</actions>
</action>
</rowactions>
<massactions usefirstitemasdefault="true">
<action type="multi" label="Download Selected as Zip" icon="fa-download">
<actions>
<action type="custom" snippet="downloadSnippet"/>
</actions>
</action>
<action type="massupdate"/>
<action type="massdelete"/>
</massactions>
<views>
<view type="standard"/>
</views>
<actions defaultlabel="Global Actions" defaulticon="sk-icon-magic" usefirstitemasdefault="true"/>
</skootable>
</components>
<oninitialshowactions/>
<onshowactions/>
</tab>
</tabs>
</tabset>
</components>
<resources>
<labels/>
<css/>
<javascript>
<jsitem location="inlinesnippet" name="downloadSnippet" cachelocation="false">var params = arguments[0],
$ = skuid.$;
// Get the Ids of the selected items as an Array
var selectedItems = arguments[0].list.getSelectedItems();
var idsArray = skuid.$.map(selectedItems,function(item){ 
   return item.row.ContentDocument.LatestPublishedVersionId; 
});

// Pass these Ids over to our Visualforce Page
window.top.location = 
    " /sfc/servlet.shepherd/version/download/"
    + idsArray.join("/");

//    /sfc/servlet.shepherd/version/download/{{#$Model.ContentDocumentLink.data}}{{{ContentDocument.LatestPublishedVersionId}}}/{{/$Model.ContentDocumentLink.data}}</jsitem>
</javascript>
<actionsequences uniqueid="sk-2RUGHd-473"/>
</resources>
<styles>
<styleitem type="background" bgtype="none"/>
</styles>
<interactions/>
</skuidpage>

This is fantastic. Any idea of maximum size or file number limits? I would need to export hundreds of files that conform to specific parameters. I can get the file Id list using Skuid and your Snippet, but the list of Id’s sent into the URL would be hundreds of files long and the total download would be hundreds of MB.

Raymond,

In the blog posting ( http://cropredysfdc.com/2016/03/16/download-multiple-contentversion-files-as-zip) the blogger claimed that he tested it with 400MB of files.  I think you are good on the max size of the resulting zip file.

You may have a problem with the length of the URL when you concatenate hundreds of Content Version Ids.  I would definitely test this with multiple browsers to know what is supported.

Thanks,

Bill

You can try File ZIPO, a native appexchange application that can help you to Zip multiple Salesforce files & attachments from the Salesforce org and download them in: Single folder or Multiple folder (Parent / Child Structure)

here’s link - https://appexchange.salesforce.com/appxListingDetail?listingId=a0N3u00000OMscFEAT

You can try Satrang Mass File Download AppExchange App to address your requirements of downloading selected files into a single zip file. The app contains a TAB and Lightning Components for downloading purposes.

With the help of this AppExchange App,

  • You can download selected files and attachments into a single zip file
  • The app also supports downloading Static Resources and Documents.
  • You can also download files in a single zip folder or in records named subfolders.
  • You get a CSV mapping file along with the downloaded files, which will help migrate files and future references.

If you have a custom requirement, we are more than happy to hear that.

URL - Satrang Mass File Download

Disclaimer: I work at Satrang Technologies, the publisher of this Mass File Download AppExchange App.

1 Like

This topic was automatically closed after 20 hours. New replies are no longer allowed.