AEM Cloud Service - Assets Processing Profile and Post Processing workflow to generate JPEG Assets


Adobe Experience Manager 2021.6.5586.20210628T210726Z-210600 (June 28, 2021)

Create Low res JPEG images from High quality Master assets (eg. psd, tif) uploaded to AEM, so the jpeg images can be browsed using AEM Desktop app and placed in 3rd party apps like Figma.

Demo | Package Install | Github


1) Create a Process Step apps.experienceaem.assets.core.workfows.ConvertRenditionToAssetProcess used in a Post Processing workflow for creating the JPEG asset from rendition added by a Processing Profile. It's worth noting that streaming of binaries in AEM cloud services is not considered a best practice, the rendition.getBinary() call in below code does not stream binary but gets a pointer to the binary in blob store. Also when processing the created asset set runPostProcess to false so the post processing workflow does not run on generated assets...

package apps.experienceaem.assets.core.workfows;

import com.adobe.granite.asset.api.AssetRelation;
import com.adobe.granite.workflow.WorkflowException;
import com.adobe.granite.workflow.WorkflowSession;
import com.adobe.granite.workflow.exec.WorkItem;
import com.adobe.granite.workflow.exec.WorkflowData;
import com.adobe.granite.workflow.exec.WorkflowProcess;
import com.adobe.granite.workflow.metadata.MetaDataMap;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

        service = WorkflowProcess.class,
        property = { "process.label=Experience AEM Convert Rendition to Asset, Workflow Process Step" }
public class ConvertRenditionToAssetProcess implements WorkflowProcess {
    private static final Logger log = LoggerFactory.getLogger(ConvertRenditionToAssetProcess.class);

    private static final String RELATION_SOURCES = "sources";
    private static final String RELATION_DERIVED = "derived";
    private static final String DAM_DERIVED_ASSET = "dam:derivedAsset";
    private static final String PROCESS_ASSET_SERVLET = "/bin/asynccommand";
    private static final String PROCESS_ASSET_OPERATION = "PROCESS";
    private static final String PROCESS_ASSET_PROFILE = "full-process";

    private static String FIGMA_REND_NAME = "figma";

    private MimeTypeService mimeTypeService;

    private SlingRequestProcessor slingRequestProcessor;

    private RequestResponseFactory requestResponseFactory;

    public void execute(final WorkItem workItem, final WorkflowSession workflowSession, final MetaDataMap args)
            throws WorkflowException {
        String assetPath = getPayloadPath(workItem.getWorkflowData());

            ResourceResolver resolver = workflowSession.adaptTo(ResourceResolver.class);

            Resource assetRes = resolver.getResource(assetPath);

            if(assetRes == null){
                log.warn("Resource not found - " + assetRes);

            String assetNameNoExt = assetRes.getName().substring(0, assetRes.getName().lastIndexOf("."));

            if(assetNameNoExt.endsWith(("-") + FIGMA_REND_NAME)){
                log.warn("Dont create Figma assets out of rendition for - " + assetPath);

            Asset asset = assetRes.adaptTo(Asset.class);

            Rendition rendition = asset.getRendition(FIGMA_REND_NAME + ".jpeg");

            if (rendition == null) {
                log.warn("Unable to get rendition " + FIGMA_REND_NAME + " of asset " + assetPath);

            Asset derivedAsset = createFigmaAsset(resolver, assetRes, rendition);

            if(derivedAsset == null){
                log.warn("Error creating Figma asset for : " + assetPath);


            processNewAsset(resolver, derivedAsset.getPath());
        }catch(Exception e){
            log.error("Error occured while converting rendtion to asset for payload - " + assetPath, e);

    private Asset createFigmaAsset(ResourceResolver resolver, Resource assetRes, Rendition rendition){
        Asset asset = assetRes.adaptTo(Asset.class);

        String assetNameNoExt = assetRes.getName().substring(0, assetRes.getName().lastIndexOf("."));

        String newAssetPath = createAssetPath(assetRes.getParent(), assetNameNoExt, rendition.getName());

        String mimeType = mimeTypeService.getMimeType(rendition.getName());

        AssetManager assetManager = resolver.adaptTo(AssetManager.class);

        Asset derivedAsset = assetManager.createOrReplaceAsset(newAssetPath, rendition.getBinary(), mimeType, false);

        if (derivedAsset == null) {
            return null;

        addRelation(derivedAsset, RELATION_SOURCES, asset.getPath());

        addRelation(asset, RELATION_DERIVED, newAssetPath);

        return  derivedAsset;

    private void processNewAsset(ResourceResolver resolver, String assetPath){"Starting Reprocess of Asset : " + assetPath);

        Map<String, Object> requestParams = new HashMap<String, Object>();
        requestParams.put("operation", PROCESS_ASSET_OPERATION);
        requestParams.put("profile-select", PROCESS_ASSET_PROFILE);
        requestParams.put("runPostProcess", "false"); // donot run the post process, might resulting in a processing loop
        requestParams.put("description", "Processing created figma asset - " + assetPath);
        requestParams.put("asset", assetPath);

        HttpServletRequest request = requestResponseFactory.createRequest("POST", PROCESS_ASSET_SERVLET, requestParams);
        ByteArrayOutputStream bos = new ByteArrayOutputStream();

        HttpServletResponse response = this.requestResponseFactory.createResponse(bos);

        try {
            slingRequestProcessor.processRequest(request, response, resolver);
        }catch (Exception e){
            log.error("Error occured while processing the new figma asset - " + assetPath);

    private void addRelation(Asset asset, String name, String relatedAssetPath) {
        com.adobe.granite.asset.api.Asset graniteAsset = asset.adaptTo(com.adobe.granite.asset.api.Asset.class);
        Iterator<? extends AssetRelation> relationItr = graniteAsset.listRelations(name);

        while(relationItr.hasNext()) {
            AssetRelation relation =;

            if( (relation == null) || relation.getAsset().getPath().equalsIgnoreCase(relatedAssetPath)){

        graniteAsset.addRelation(name, relatedAssetPath);

    private static String createAssetPath(Resource parent, String assetNameNoExt, String renditionName) {
        return parent.getPath() + "/" + assetNameNoExt + "-" + renditionName;

    private String getPayloadPath(WorkflowData wfData) {
        String payloadPath = null;

        if (wfData.getPayloadType().equals("JCR_PATH")) {
            payloadPath = (String)wfData.getPayload();

        return payloadPath;


2) Create a regular workflow model Experience AEM - Asset Create - Post Process and copy the Smart Tagging step and Workflow Completed steps from Assets Cloud Post Processing workflow...

3) Add the apps.experienceaem.assets.core.workfows.ConvertRenditionToAssetProcess step just before Workflow Completed step...

4) Create a Processing Profile to generate the rendition of Master asset...


5) Set the Processing Profile and Post processing workflow in parent folder properties...

No comments:

Post a Comment