Download Invoice PDF's On List View without saving to Notes and Attachment as a zip file

Kudos to @SFDCGOD for suggesting this. I was so intrigued by his suggestion, I gave it a try.

First, I created resources for each of the Javascript files (FileSaver.js and jszip.js) and then created a simple Visualforce page referencing them so I could use it as a listview button:

<apex:page standardController="c2g__codaInvoice__c" recordSetVar="invoices" extensions="InvoiceDownload">
    <apex:includeScript value="{!$Resource.FileSaver}"/>
    <apex:includeScript value="{!$Resource.jszip}"/>

    <apex:form >
        <apex:pageBlock title="File Download">
            <apex:pageMessage severity="INFO" title="Invoice Download" strength="2" summary="Invoices Downloading" detail="Downloaded {!NumberOfFiles} file."/>
            <apex:pageBlockButtons >
                <apex:commandButton action="{!cancel}" value="Close"/>
            </apex:pageBlockButtons>
        </apex:pageBlock>
    </apex:form>    

    <script> 
    var download = function() {
        var files = JSON.parse('{!FilesToDownload}');
        var zip = new JSZip();
        for(fileName in files){
            var fileBase64String = files[fileName];
            zip.file(fileName, fileBase64String, { base64: true });                
        }
        saveAs(zip.generate({type:"blob"}), 'Invoices Download.zip', false);
    }
    download();
    </script></apex:page>

Then a I created a simple Controller to generate the Invoice(s) selected as PDFs converted to Base64 so that jszip can store PDFs:

public class InvoiceDownload 
{
    private List<c2g__codaInvoice__c> selected;
    public InvoiceDownload(ApexPages.StandardSetController ssc)
    {
        this.selected = [select name from c2g__codaInvoice__c where id in :ssc.getSelected()];
    }

    public Integer getNumberOfFiles()
    {
        return this.selected.size();
    }

    public String getFilesToDownload()
    {
        Map<String,String> files = new Map<String,String>();
        for(c2g__codaInvoice__c invoice : this.selected)
        {
            Blob pdf = new PageReference('/apex/SalesInvoiceCustomized?id='+invoice.Id).getContentAsPDF();
            files.put(invoice.Name+'.pdf', EncodingUtil.base64Encode(pdf));
        }
        return JSON.serialize(files);
    }
}

It works a treat, I only have 35 invoices in my DE org but no problems encountered with that number (PDFs are ~14k in size each). Beware though, this is Browser specific - I tested against Chrome, Firefox and Safari on Mac and it didn't work with Safari. Also tested on Chrome and IE on Windows 10 which also worked (I'm afraid they are all the browsers I have access to).


You can do following to achieve this:

Add following javascript in your page

  1. FileSaver.min.js
  2. Blob.js
  3. jszip.min.js

  4. Pass All Id's to controller and just get there data by getContetnAsPdf one by one (Just create then Attachment object,don't insert them)

  5. Save them to a javascript array
  6. Convert them to zip file in javascript
  7. Download them using FileSaver.js

Let me know if you found it difficult to implement

Thanks


fantastic work done by @SFDCGOD and @Phil Hawthorn

Finally I got the result of downloading the zip files without saving to anywhere.

Special thanks to @Phil Hawthorn because I followed your methods. I customized for my Onclick javascript button and Werbservice methods instead of visualforce button.

Here is my final result. First I created Apex global class with Webservice method that returns the files

global class InvoiceDownload {

    //Private List<Invoice__c> selected;
    private static String API_STATUS_NORMAL = '200';

    webservice static String getFilesToDownload(string sfdcId)
    {
        List<id> ids = new List<id>();
        system.debug('SFDCid'+sfdcId);
        string[] idsArray = sfdcId.split(',');
        for(integer i=0; i<idsArray.size();i++)
        {
           ids.add(idsArray[i]);
        }
        List<Invoice__c> selected = [select name from Invoice__c where id in : ids];
        Map<String,String> files = new Map<String,String>();
        for(Invoice__c inv : selected)
        {
            Blob pdf = new PageReference('/apex/InvoicePDF?id='+inv.Id).getContentAsPDF();
            files.put(inv.Name+'.pdf', EncodingUtil.base64Encode(pdf));
        }
        return JSON.serialize(files);
    }
}

And I called the class and webservice method to the JSON.response method in an Onclick Javascript button.

{!REQUIRESCRIPT("/resource/JSzipfileInvoice")}
{!REQUIRESCRIPT("/resource/filesaver")} 
{!REQUIRESCRIPT("/soap/ajax/30.0/connection.js")} 
{!REQUIRESCRIPT("/soap/ajax/30.0/apex.js")} 

var download = function() {
        var records = {!GetRecordIds($ObjectType.Invoice__c)}; 
        var SelectedIds=''; 
        var fileDatafinal; 
        for(var i=0;i < records.length; i++) 
           { 
           SelectedIds+=records[i]+","; 
           } 
        SelectedIds=SelectedIds.substring(0,SelectedIds.length - 1);
        if(SelectedIds.length > 0){ 
           alert(' Please wait until end of the process '); 
           var response = sforce.apex.execute("InvoiceDownload","getFilesToDownload",{sfdcId:SelectedIds});
           var files = JSON.parse(response);        
           var zip = new JSZip();
           for(fileName in files){
              var fileBase64String = files[fileName];
              zip.file(fileName, fileBase64String, { base64: true });
            }
            saveAs(zip.generate({type:"blob"}), 'Invoice Download.zip', false);
            confirm('Please confirm to download');
        }
        else
        { 
          alert('Please select atlease one record');
        }
}
download();

Thanks to all of your responses.