Download Selected Salesforce Files as Zip File

Bill McCulloughBill McCullough 💎💎
edited October 15, 2019 in General
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>&lt;a target="_blank" href="/sfc/servlet.shepherd/version/download/{{{ContentDocument.LatestPublishedVersionId}}}"&gt;{{{ContentDocument.Title}}}&lt;/a&gt;</template>
</field>
<field type="IMAGE" hideable="true" uniqueid="fi-IA_-437">
<label>Image</label>
<url>/sfc/servlet.shepherd/version/renditionDownload?rendition=THUMB240BY180&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>





Comments

  • edited October 15, 2019
    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.
  • Bill McCulloughBill McCullough 💎💎
    edited July 31, 2019
    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
Sign In or Register to comment.

Howdy, Stranger!

It looks like you're new here. If you want to get involved, click one of these buttons!