Creating a ZIP of assets in AEM / Java.
AEM OOTB provides a feature to download assets and their renditions in a ZIP from the assets console. However, there might be a requirement of processing assets from multiple folder locations (running approval workflows, using writeback process, or editing assets) and then creating a single zip of all the assets for storage, downloading, sharing, etc where direct use of AEM collections cannot be used or the use of AEM collections become cumbersome.
The above requirement could be implemented by a trigger event with a servlet where we could create the ZipOutputStream using the response. However, if the number of assets is high or the asset sizes are on the higher side, the process could take a while or even fail which might not be an ideal experience for the end user in some cases.
In this article, the mentioned solution could be implemented using a sling job (recommended) or a workflow or even a servlet as per the requirement.
Considerations:
1. Add all required asset paths in an arraylist → “servingAssetPathsList”.
2. We are getting the “original” rendition here. In case you need to add other renditions, use → requestedAsset.getRendition(“rendition_name”).
3. Update the buffer size as required.
4. The resourceResolver used to create the session, for saving the ZIP file should have read, write permissions at the location.
Here’s the code snippet:
import com.day.cq.dam.api.Asset;
import com.day.cq.dam.api.Rendition;
import javax.jcr.*;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
Node zipsRootNode = JcrUtils.getOrCreateByPath("/var/zipRootPath", JcrResourceConstants.NT_SLING_FOLDER, JcrResourceConstants.NT_SLING_FOLDER, session, true);
Node zipNode = zipsRootNode.addNode("zipFileName", JcrConstants.NT_FILE);
Node zipContentNode = zipNode.addNode(JcrConstants.JCR_CONTENT, JcrConstants.NT_RESOURCE);
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
ZipOutputStream zipOutStream = new ZipOutputStream(byteArrayOutputStream);
for(String assetPath: servingAssetPathsList) {
if(null != resolver.getResource(assetPath)) {
Resource assetResource = resolver.getResource(assetPath);
if(null != assetResource.adaptTo(Asset.class)) {
Asset requestedAsset = assetResource.adaptTo(Asset.class);
if(null != requestedAsset.getOriginal() && null != requestedAsset.getOriginal().getStream()) {
InputStream inputStream = requestedAsset.getOriginal().getStream();
ZipEntry entry = new ZipEntry(requestedAsset.getName());
zipOutStream.putNextEntry(entry);
byte[] bytes = new byte[2048];
int length;
while ((length = inputStream.read(bytes, 0, 2048)) != -1) {
zipOutStream.write( bytes, 0, length );
}
zipOutStream.closeEntry();
inputStream.close();
}
}
}
}
zipOutStream.finish();
zipOutStream.close();
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
Binary binary = session.getValueFactory().createBinary(byteArrayInputStream);
zipContentNode.setProperty(JcrConstants.JCR_DATA, binary);
session.save();
byteArrayInputStream.close();
byteArrayOutputStream.close();
I hope this was helpful for your use case. Please leave a clap if you like it, and I’ll look forward to the comments for improvements in the solution.