Introduction
The Spring '24 release of Salesforce had some great new features and improvements, but for me some of the Beta/Developer Preview features were more interesting - for example the scratch org snapshots beta that I wrote about a few weeks ago.
I've been developing on the Salesforce platform for close to 16 years now, and I've lost count of the number of different approaches that I've taken to handle zip files. Sometimes on the server, sometimes on the client in JavaScript, but always using custom solutions that didn't perform fantastically well. The new functionality that is native to Apex won't solve this problem - governor limits still apply - that fact that it isn't implemented in Apex itself should mean that a little more processing can be wrung out of the transaction.
Trying it Out
This is a departure from previous developer preview functionality that I've tried in the past, as it's available in scratch orgs only. I'm onside with this approach as it feels like it will allow the Apex development team to be more agile and give us access to changes earlier. Anything that does go awry will disappear in a maximum of 30 days, so there won't be loads of developer editions left behind needing to be patched up. I'll take a bit of additional effort tracking down issues if it means I get my hands on things earlier.
You need to enable support for zip file processing via the features property in your scratch org definition, specifying ZipSupportInApex :
"features": ["EnableSetPasswordInApi", "ZipSupportInApex"],
Then create a new scratch org and you are off to the races.
What Worked
Creating a zip file and then extracting it works great - I did this through some execute anonymous Apex and was pleasantly surprised there were no further hoops to jump through. As this is in developer preview I didn't expend a huge amount of effort, just adding a single entry created from a String via a Blob:
public void SimpleZip() { Compression.ZipWriter writer=new Compression.ZipWriter(); writer.addEntry('zs.csv', Blob.valueOf('Name, Title\nKeir Bowden, CEO')); Blob zipFile=writer.getArchive(); ContentVersion contentVer = new ContentVersion( VersionData =zipFile, Title = 'zipsample.zip', Description = 'Zip Sample', PathOnClient = '/zipsample.zip' ); insert contentVer; }
Once I'd done this, I could see the zip and download it to extract files:
And to extract the file from the zip and debug the contents in Apex :
public void SimpleExtract() { ContentVersion contentVer=[select Id, VersionData, Title, Description, PathOnClient from ContentVersion where Title='zipsample.zip']; Blob zipBody = contentVer.VersionData; Compression.ZipReader reader = new Compression.ZipReader(zipBody); Compression.ZipEntry csvEntry = reader.getEntry('zs.csv'); Blob csv = reader.extract(csvEntry); System.debug('CSV body = ' + csv.toString()); }
Gave me the contents of the file that I'd created from my String :
What Didn't Work
@AuraEnabled(cacheable=true) public static List<Entry> GetZipEntries(Id contentVersionId) { List<Entry> entries=new List<Entry>(); try { ContentVersion contentVer=[select Id, VersionData, Title, Description, PathOnClient from ContentVersion where Id=:contentVersionId]; Blob zipBody = contentVer.VersionData; Compression.ZipReader reader = new Compression.ZipReader(zipBody); for (Compression.ZipEntry entry : reader.getEntries()) { System.debug('Entry = ' + entry.getName()); } } catch (Exception e) { System.debug(e); } return entries; }
public List<Entry> getEntries() { if (null==this.entries) { entries=new List<Entry>(); String cvId=ApexPages.currentPage().getParameters().get('cvid'); System.debug('Id = ' + cvId); ContentVersion contentVer=[select Id, VersionData, Title, Description, PathOnClient from ContentVersion where Id=:cvId]; Blob zipBody = contentVer.VersionData; Compression.ZipReader reader = new Compression.ZipReader(zipBody); for (Compression.ZipEntry zipEntry : reader.getEntries()) { System.debug('Processing entry'); Entry entry=new Entry(); entry.name=zipEntry.getName(); entry.method=zipEntry.getMethod().name(); entry.lastModifiedTime=zipEntry.getLastModifiedTime(); entries.add(entry); } } return entries; }