
The Salesforce Spring ’25 release brings a host of exciting features for developers, with the Compression namespace standing out as a game-changer for optimizing file storage and data transfer. Introduced in Spring ’24 and now generally available in Spring ’25, this namespace allows developers to create and extract zip archives natively in Apex, addressing long-standing challenges around storage limits and performance. In this blog, we dive into the technical details of attachment compression, explore its benefits, and provide a working code example to help you get started.
Why Attachment Compression Matters
Salesforce Files and attachments are critical for managing documents, but they come with storage constraints. Compressing files reduces storage costs and speeds up data transfer, which is especially valuable for organizations handling large volumes of attachments, such as nonprofits or businesses sharing documents with vendors. The Compression namespace empowers developers to:
- Reduce Storage Costs: Smaller file sizes mean less storage consumption, saving money in Salesforce.
- Enhance Performance: Compressed files transfer faster, improving user experience during uploads or downloads.
- Automate Workflows: Combine compression with Flows or Apex to streamline file management processes.
With Spring ’25, the Compression namespace is fully supported, allowing developers to specify compression methods and levels for optimal results. However, be mindful of Apex heap size limits, which vary based on the compression method and file size. Testing is crucial to determine the maximum file size your org can handle.
Getting Started with the Compression Namespace
The Compression namespace provides two key classes: Compression.ZipWriter for creating zip archives and Compression.ZipReader for extracting them. In this blog, we focus on ZipWriter to compress multiple attachments associated with a Salesforce record into a single zip file, which is then saved as a new Salesforce File.
Key Features of Compression.ZipWriter
- Add Files: Add files to the zip archive as blobs with custom paths.
- Set Compression Levels: Uses the Level enum values to indicate the compression level as BEST_COMPRESSION, BEST_SPEED, DEFAULT_LEVEL, or NO_COMPRESSION.
- Finalize Archives: Generate the zip as a blob for storage or further processing.
Example Use Case
Imagine a scenario where a nonprofit needs to archive all attachments related to a Contact record into a single zip file, delete the original files, and store the zip in Salesforce. This optimizes storage and simplifies file sharing. Below is a working Apex class to achieve this.
Working Code Example
The following Apex class, FileCompressorService, compresses all attachments linked to a given record ID into a zip file and saves it as a Salesforce File. The code uses Compression.ZipWriter and handles ContentDocumentLinks to retrieve and process files.
public with sharing class FileCompressorService {
// Method to compress all files related to a given Contact record
public static void compressFiles(Id recordId) {
// Fetch the Contact record to get the name for naming the zip file
Contact contact = [SELECT Id, Name FROM Contact WHERE Id =:recordId];
try {
// Fetch all the files linked to the given record
List<ContentDocumentLink> docLinks = [
SELECT ContentDocumentId, ContentDocument.Title, ContentDocument.LatestPublishedVersionId
FROM ContentDocumentLink
WHERE LinkedEntityId = :recordId
];
// Exit if no files found
if (docLinks.isEmpty()) {
System.debug(‘No files found for record ID: ‘ + recordId);
return;
}
// Create a new ZipWriter instance
Compression.ZipWriter zipWriter = new Compression.ZipWriter();
// Collect the IDs of the latest versions of the linked files
Set<Id> versionIds = new Set<Id>();
for (ContentDocumentLink link : docLinks) {
versionIds.add(link.ContentDocument.LatestPublishedVersionId);
}
// Retrieve the content versions using the collected version IDs
List<ContentVersion> contentVersions = [
SELECT Id, Title, FileExtension, VersionData
FROM ContentVersion
WHERE Id IN :versionIds
];
// Add each file to the zip archive with its proper name and content
for (ContentVersion cv : contentVersions) {
String fileName = cv.Title + (String.isNotBlank(cv.FileExtension) ? ‘.’ + cv.FileExtension : ”);
zipWriter.addEntry(fileName, cv.VersionData);
}
// Get the final zipped Blob
Blob zipBlob = zipWriter.getArchive();
// Create a new ContentVersion for the zipped file
ContentVersion zipVersion = new ContentVersion();
zipVersion.ContentLocation = ‘S’; // Stored in Salesforce
zipVersion.PathOnClient = ‘CompressedFilesOf_’ + contact.Name + ‘.zip’;
zipVersion.Title = ‘CompressedFilesOf_’ + contact.Name;
zipVersion.VersionData = zipBlob;
insert zipVersion;
// Link the newly created zip file back to the original record
ContentDocumentLink cdl = new ContentDocumentLink();
cdl.ContentDocumentId = [SELECT ContentDocumentId FROM ContentVersion WHERE Id = :zipVersion.Id].ContentDocumentId;
cdl.LinkedEntityId = recordId;
cdl.ShareType = ‘V’; // Viewer access
insert cdl;
// Prepare the original documents for deletion
List<ContentDocument> docsToDelete = new List<ContentDocument>();
for (ContentDocumentLink link : docLinks) {
docsToDelete.add(new ContentDocument(Id = link.ContentDocumentId));
}
// Delete the original files if there are any
if (!docsToDelete.isEmpty()) {
delete docsToDelete;
}
System.debug(‘Zip file created and linked successfully.’);
} catch (Exception e) {
// Handle and throw user-friendly error
System.debug(‘Error during compression: ‘ + e.getMessage());
throw new AuraHandledException(‘Compression failed: ‘ + e.getMessage());
}
}
}
Testing the Code
To test, execute the following in the Developer Console’s Anonymous Window:
Id recordId = ‘003xxxxxxxxxxxxAAA’; // Replace with a Contact or other record ID
FileCompressorService.compressFiles(recordId);
Before Compression:
After Compression:
Ensure the record has associated files (e.g., uploaded via the Files related list). After execution, check the Files related list for the new zip file (CompressedFiles_contact.Name.zip) and verify the original files are deleted.
Best Practices
- Heap Size Limits: Test with large files to ensure compliance with Apex heap size limits (6 MB synchronous, 12 MB asynchronous). Adjust compression levels if needed.
- Error Handling: Wrap code in try-catch blocks to handle exceptions gracefully, especially for large datasets.
- Batch Processing: For records with many attachments, consider a Batch Apex job to avoid governor limits.
- Compression Levels: Use “BEST_SPEED” for speed or “BEST_COMPRESSION” for maximum compression, depending on your use case.
Conclusion
The Compression namespace in Salesforce Spring ’25 empowers developers to optimize file storage and performance with native zip archive creation. By integrating this feature into your Apex code or Flows, you can build efficient, scalable solutions for file management. The provided FileCompressorService class is a starting point—adapt it to your org’s needs and explore the full potential of this powerful feature.
References:
- https://developer.salesforce.com/docs/atlas.en-us.apexref.meta/apexref/apex_class_compression_zipwriter.htm
- https://help.salesforce.com/s/articleView?id=release-notes.rn_apex_compression.htm&release=248&type=5