With the Spring ’25 release, Salesforce finally introduced native support for ZIP file handling in Apex. This long-awaited feature allows any user to compress and extract ZIP files directly within Apex, without the need for third-party libraries or external services. In this blog, we will explore How to Zip & Unzip files natively in Salesforce.
But to make this even easier, I’ve built a ready-to-use ZIP utility using Apex & LWC—a simple, reusable wrapper that leverages the new native Compression namespace. You can deploy it directly into your sandbox and start zipping or unzipping files in minutes without leaving Salesforce.
Importance of native Zip support in Salesforce
In API version 56.0 or higher, Apex class has Compression Namespace , which allows to compress and extract files within apex using ZipWriter and ZipReader classes.
Key Benefits of having native Zip support:
- No hassle of leaving salesforce to compress or extract files.
- No need of Third-party libraries which add dependency and maintenance overhead to your application.
- Not using External services saves your execution times improving application performance
- Bundle & Send multiple documents as .zip file into your email attachments.
- Upload Zipped data for further processing into Salesforce.
How to Compress Files using Apex
Using ZipWriter class
In order to compress files within Salesforce, Apex provides ZipWriter class. Using methods of this ZipWriter class, you can add file entries and archive those files as a blob and then it can be saved as .zip file.
public static void doCompress(){
// instantiate Zipwriter class from Compression namespace
Compression.ZipWriter writer = new Compression.ZipWriter();
// add each file: Name & its content (as a blob) one by one
writer.addEntry('sampleFile1.txt', Blob.valueOf('this is test content 1'));
writer.addEntry('sampleFile2.txt', Blob.valueOf('this is test content 2'));
// archive above added files
Blob zipBlob = writer.getArchive();
string zipFileName = 'CompressedFiles.zip'; // name of zip file
ContentVersion cv = new ContentVersion(
Title = zipFileName,
PathOnClient = zipFileName,
VersionData = zipBlob
);
insert cv;
system.debug('new Zip file saved as ContentVersion');
}- As seen in above example code, ZipWriter class has a method called
addEntry()in which you can add your files one by one with parameters 1. fileName, 2. file data (as a blob) and 3. compression method optionally. - now using method,
getArchive(), we get a single blob file combining all those added files. Now, you can use this blob file to save it as .zip file using contentVersion, you can also save/send it as a attachment to email or records.
How to Extract Files using Apex
Using ZipReader class
In order to extract zip file from within Salesforce, Apex provides ZipReader class. Using methods of this ZipReader class, you can loop over each zipped file, extract its content and then you can save each file individually as contentVersion in Salesforce.
public static void doExtract(Id zipFileId) {
// to save all extracted files
List<ContentVersion> cvFiles = new List<ContentVersion>();
// find saved Zip file from contentVersion
ContentVersion cv = [SELECT VersionData FROM ContentVersion WHERE Id =:zipFileId];
Blob storedZipData = cv.VersionData;
// instantia ZipReader class from Compression namespace
Compression.ZipReader reader = new Compression.ZipReader(storedZipData);
//Loop over each zipEntry & extract it
for (Compression.ZipEntry entry : reader.getEntries()) {
String entryName = entry.getName();
// Skip folders (names ending with '/')
if (!entryName.endsWith('/')) {
//extract actual file data from zipEntry
Blob fileBlob = reader.extract(entry);
// Create a new ContentVersion for each extracted file
ContentVersion extractedFile = new ContentVersion(
Title = entryName,
PathOnClient = entryName,
VersionData = fileBlob
);
cvFiles.add(extractedFile);
System.debug('extracted fileName : ' + entryName);
}
}
insert cvFiles;
}- ZipReader is exactly opposite to ZipWriter. Unlike
addEntry(, ZipReader hasgetEntries(), which we can use to loop through each compressed file and extract it one by one. - As seen in above code, we are first getting Blob of Zipped Data from ContentVersion, and passing it to ZipReader.
- Now using ZipReader’s method
getEntries(), we extract each file entry one by one and then save each one into our org using ContentVersion.
Introducing Zip Utility
What is Zip Utility ?
The Zip Utility is custom-built tool in Salesforce that allows users to easily compress and extract files directly within Salesforce. You don’t need to worry about if your organization has restriction of using third-party tool for compress/extract files. You can do it now without leaving salesforce screen.
This Zip Utility can be deployed on Home page, Record page or any custom Lightning Pages. This Zip Utility is built using LWC and Apex.
How it works ?
Once your deploy this Zip Utility into your org, Simply Drop it on any Lightning Pages (Home page or Record page) and start using it. Here, we are using Zip Utility into our Home page.
- Compress files into a Zip file : – You can upload or drag/drop multiple files at once here. Once uploaded, you will see all your uploaded files below. Now when you click on compress button, you will see once compress zip file called ‘CompressedFiles.Zip‘ in table. Click on download button to get this zipped file in your computer.
- Extract Zip file : – You can upload or drag/drop a zip file onto zip utility and when you click on extract, you will see all your extract files in Table below. you can download each file in individually or click ok Download All button for all at once.
Building the Zip Utility with Apex & LWC
In this section, We will explore the process of building a Zip Utility which uses Apex and LWC.
By leveraging Apex for backend file handling (e.g., extracting and compressing ZIP files) and LWC for a responsive, user-friendly interface, the utility enables seamless file uploads, extractions, and compressions, all within the Salesforce environment. The files are stored as ContentVersion records.
Create Backend Logic with Apex
public with sharing class ZipFileController {
@AuraEnabled
public static List<ContentVersion> extractZip(Id zipFileId) {
ContentVersion cv = [SELECT VersionData FROM ContentVersion WHERE
ContentDocumentId = :zipFileId];
Blob zipBlob = cv.VersionData;
Compression.ZipReader reader = new Compression.ZipReader(zipBlob);
List<ContentVersion> extracted = new List<ContentVersion>();
for (Compression.ZipEntry entry : reader.getEntries()) {
if (!entry.getName().endsWith('/')) {
Blob fileBlob = reader.extract(entry);
ContentVersion file = new ContentVersion(
Title = entry.getName(),
PathOnClient = entry.getName(),
VersionData = fileBlob
);
extracted.add(file);
}
}
insert extracted;
return extracted;
}
@AuraEnabled
public static ContentVersion compressFiles(List<Id> fileIds) {
List<ContentVersion> versions = [SELECT PathOnClient, VersionData FROM
ContentVersion WHERE ContentDocumentId IN :fileIds];
Compression.ZipWriter writer = new Compression.ZipWriter();
for (ContentVersion cv : versions) {
writer.addEntry(cv.PathOnClient, cv.VersionData);
}
Blob zipBlob = writer.getArchive();
String fileName = 'CompressedFiles.zip';
ContentVersion zipCV = new ContentVersion(
Title = fileName,
PathOnClient = fileName,
VersionData = zipBlob
);
insert zipCV;
return zipCV;
}
}- This apex class called ZipFileController is the backbone of this Zip Utility. It has 2 main method to handle compress files and extract files.
- Both methods receive file Ids from LWC’s lightning-file-upload, then queries file data from ContentVersion and send it for further processing either for compression or extraction.
Create Frontend with LWC
Now, Lightning Web Component is built to provide User Interface to have file upload options and button actions like compress, extract and download. this LWC will use above zipFileController apex class to compress & extract uploaded files.
<template>
<lightning-card title="Zip & unZip Files" icon-name="utility:archive">
<div class="slds-p-around_medium">
<lightning-file-upload
label="Upload Files/ZIP"
name="fileUploader"
accept={acceptedFormats}
onuploadfinished={handleUpload}
multiple>
</lightning-file-upload>
<!-- Show Uploaded File Names -->
<template if:true={uploadedFileNames.length}>
<div class="slds-m-top_small">
<p class="slds-text-heading_small slds-m-bottom_xx-small">Uploaded files:</p>
<ul class="slds-list_dotted">
<template for:each={numberedFileNames} for:item="displayName">
<p key={displayName}>{displayName}</p>
</template>
</ul>
</div>
</template>
</div>
<!-- Action Buttons -->
<div class="slds-m-top_medium slds-p-around_medium">
<lightning-button label="Extract ZIP" onclick={handleExtract} disabled={isExtractDisabled} class="slds-m-auto slds-m-right_small"></lightning-button>
<lightning-button label="Compress Files" onclick={handleCompress} disabled={isCompressDisabled} class="slds-m-right_small"></lightning-button>
<lightning-button label="Download All" onclick={handleDownloadAll} disabled={isDownloadDisabled} class="slds-m-right_small"></lightning-button>
</div>
<!-- Spinner -->
<template if:true={isLoading}>
<div class="slds-m-top_medium slds-align_absolute-center">
<lightning-spinner alternative-text="Processing..." size="medium"></lightning-spinner>
</div>
</template>
<!-- Download Files from Table -->
<template if:true={fileList.length}>
<lightning-datatable
key-field="id"
data={fileList}
columns={columns}
hide-checkbox-column
onrowaction={handleRowAction}>
</lightning-datatable>
</template>
</lightning-card>
</template>- above html file of LWC, has five UI component to show, 1. File Uploader 2. Display uploaded files 3. List of Action buttons 4. Spinner while file processing 5. Display Result files in Table
import { LightningElement, track } from 'lwc';
import { ShowToastEvent } from 'lightning/platformShowToastEvent';
import extractZip from '@salesforce/apex/ZipFileController.extractZip';
import compressFiles from '@salesforce/apex/ZipFileController.compressFiles';
export default class ZipUtility extends LightningElement {
@track fileList = [];
@track uploadedFileIds = [];
@track uploadedFileNames = [];
isExtractDisabled = true;
isCompressDisabled = true;
isDownloadDisabled = true;
isLoading = false;
// Accepted file formats for upload
acceptedFormats = ['.zip', '.txt', '.docx', '.pdf', '.png', '.jpg',
'.jpeg', '.csv', '.xlsx', '.pptx'];
columns = [
{ label: 'File Name', fieldName: 'Title' },
{
label: 'Download',
type: 'button',
typeAttributes: {
label: 'Download',
name: 'download',
variant: 'brand'
}
}
];
handleUpload(event) {
const uploadedFiles = event.detail.files;
if (uploadedFiles.length === 0) {
this.showToast('Error', 'No files uploaded.', 'error');
return;
}
// Capture file names
this.uploadedFileNames = uploadedFiles.map(f => f.name);
this.uploadedFileIds = uploadedFiles.map(f => f.documentId);
this.fileList = [];
const isZip = this.isZipFileUploaded(uploadedFiles);
this.isExtractDisabled = !uploadedFiles.length || !isZip;
this.isCompressDisabled = isZip;
this.isDownloadDisabled = true;
this.showToast('Success', 'Files uploaded successfully.', 'success');
}
isZipFileUploaded(files) {
return files.length === 1 && files[0].name.toLowerCase().endsWith('.zip');
}
async handleExtract() {
this.isLoading = true;
try {
const result = await extractZip({ zipFileId: this.uploadedFileIds[0] });
this.fileList = result.map(file => ({ ...file, id: file.Id }));
this.isDownloadDisabled = false;
this.showToast('Success', 'ZIP extracted successfully.', 'success');
} catch (error) {
this.showToast('Error', error.body.message, 'error');
console.error(error);
} finally {
this.isLoading = false;
}
}
async handleCompress() {
this.isLoading = true;
try {
const result = await compressFiles({ fileIds: this.uploadedFileIds });
this.fileList = [{ ...result, id: result.Id }];
this.isDownloadDisabled = false;
this.showToast('Success', 'Files compressed successfully.', 'success');
} catch (error) {
this.showToast('Error', error.body.message, 'error');
console.error(error);
} finally {
this.isLoading = false;
}
}
handleDownloadAll() {
this.fileList.forEach(file => {
window.open(`/sfc/servlet.shepherd/version/download/${file.Id}`, '_blank');
});
}
handleRowAction(event) {
const action = event.detail.action;
const row = event.detail.row;
if (action.name === 'download') {
window.open(`/sfc/servlet.shepherd/version/download/${row.Id}`, '_blank');
}
}
showToast(title, message, variant) {
this.dispatchEvent(new ShowToastEvent({
title,
message,
variant,
mode: 'dismissable'
}));
}
get numberedFileNames() {
return this.uploadedFileNames.map((name, idx) => `${idx + 1}. ${name}`);
}
}- Above LWC controller has method to handle all 5 UI components.
<?xml version="1.0" encoding="UTF-8"?>
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata">
<apiVersion>62.0</apiVersion>
<isExposed>true</isExposed>
<targets>
<target>lightning__RecordPage</target>
<target>lightning__AppPage</target>
<target>lightning__HomePage</target>
</targets>
</LightningComponentBundle>Customization & enhancement
- Currently this zip utility only supports certain file format as shown in above .js file, you can add/remove file format as per your requirement.
- Currently this Zip utility does not have any restriction for file size but it can be enhanced to add limit file size.
- Instead of only offering the zip file for immediate download, you could also enhance it to send the zip file as an email attachment.
- We can also allow the user to specify the compression level of the zip file (e.g., low, medium, high).
Zip Utility Source Code
In order to use this Zip Utility in your Sandbox, find code from This Repo
For any further improvement or suggestion, You can fork this repo or feel free to reach out.
Consideration
- Ensure that you validate file types to prevent uploading unsupported or malicious files.
- Each time you upload or download files on Zip Utility, each files are saved in the salesforce as a ContentDocument & ContentVersion, which adds to total file size limit of the org. So it’s important to delete unwanted files afterwards.





Really insightful post on Apex ZIP compression! 💡
Understanding the difference between Deflated and Stored methods can make a big impact on performance and file size optimization in Salesforce. A must-read for developers working with large data workflows.
I also came across a detailed guide that explains this with practical examples—worth checking out: https://ayaninsights.com/guestblogs/zip-compression-in-apex/