API de Java™ para la entrega de imágenes optimizadas para la web

AEM as a Cloud Service Aprenda a utilizar las API de Java™ de entrega de imágenes optimizadas para la web de para desarrollar experiencias web de alto rendimiento.

AEM Compatibilidad con as a Cloud Service entrega de imágenes optimizadas para la web que genera automáticamente representaciones web de imágenes optimizadas de los recursos. La entrega de imágenes optimizadas para la web se puede utilizar con tres enfoques principales:

  1. AEM Uso de componentes de Core WCM
  2. Crear un componente personalizado que AEM extiende el componente de imagen de Core WCM
  3. Cree un componente personalizado que utilice la API Java™ de AssetDelivery para generar direcciones URL de imagen optimizadas para la web.

AEM Este artículo explora el uso de las API de Java™ de imagen optimizada para la web en un componente personalizado, de una manera que permite que las API basadas en código funcionen tanto en el SDK as a Cloud Service AEM de la como en el SDK de la.

API de Java™

El API de AssetDelivery es un servicio OSGi que genera direcciones URL de entrega optimizadas para la web para los recursos de imagen. AssetDelivery.getDeliveryURL(...) las opciones permitidas son documentado aquí.

El AssetDelivery AEM El servicio OSGi solo se satisface cuando se ejecuta en el as a Cloud Service de la. AEM En el SDK de la, referencias a AssetDelivery Devolución del servicio OSGi null. AEM Es mejor utilizar de forma condicional la URL optimizada para la web cuando se ejecuta en un as a Cloud Service AEM y utilizar una URL de imagen de reserva en el SDK de la. Normalmente, la representación web del recurso es una reserva suficiente.

Uso de API en el servicio OSGi

Marcar elAssetDelivery AEM Haga referencia a como opcional en los servicios OSGi personalizados para que el servicio OSGi personalizado siga estando disponible en el SDK de la.

import com.adobe.cq.wcm.spi.AssetDelivery;
...
@Reference(cardinality = ReferenceCardinality.OPTIONAL)
private volatile AssetDelivery assetDelivery;

Uso de API en el modelo Sling

Marcar elAssetDelivery AEM Hacer referencia a como opcional en los modelos Sling personalizados para que el modelo Sling personalizado siga estando disponible en el SDK de la.

import com.adobe.cq.wcm.spi.AssetDelivery;
...
@OSGiService(injectionStrategy = InjectionStrategy.OPTIONAL)
private AssetDelivery assetDelivery;

Uso condicional de API

Devolver de forma condicional la URL de imagen optimizada para la web o la URL de reserva en función de la variable AssetDelivery Disponibilidad del servicio OSGi. AEM El uso condicional permite que el código funcione al ejecutarlo en el SDK de la.

if (assetDelivery != null ) {
    // When running on AEM as a Cloud Service use the real web-optimized image URL.
    return assetDelivery.getDeliveryURL(...);
} else {
    // When running on AEM SDK, use some fallback image so the experience does not break.
    // What the fallback is up to you!
    return getFallbackURL(...);
}

Código de ejemplo

El siguiente código crea un componente de ejemplo que muestra una lista de recursos de imagen mediante direcciones URL de imagen optimizadas para la web.

AEM Cuando el código se ejecuta en un as a Cloud Service de, las representaciones de imágenes web optimizadas para la web se utilizan en el componente personalizado.

AEM Imágenes optimizadas para la web sobre la as a Cloud Service

AEM as a Cloud Service admite la API de AssetDelivery, por lo que se utiliza la representación web optimizada para la web

AEM Cuando el código se ejecuta en el SDK de la, se utilizan las representaciones web estáticas menos óptimas, lo que permite que el componente funcione durante el desarrollo local.

AEM Imágenes de reserva optimizadas para la web en el SDK de la

AEM El SDK no admite la API AssetDelivery, por lo que se utiliza la representación web estática de reserva (PNG o JPEG)

La implementación se divide en tres partes lógicas:

  1. El WebOptimizedImage AEM El servicio OSGi actúa como un "proxy inteligente" para el servicio proporcionado por el usuario en el que se realiza la AssetDelivery AEM Servicio OSGi que puede gestionar la ejecución tanto en SDK as a Cloud Service AEM como en SDK de.
  2. El ExampleWebOptimizedImages El modelo Sling proporciona lógica empresarial para recopilar la lista de recursos de imagen y sus direcciones URL optimizadas para la web que se van a mostrar.
  3. El example-web-optimized-images AEM Componente, implementa el HTL para mostrar la lista de imágenes optimizadas para la web.

El código de ejemplo siguiente se puede copiar en la base de código y actualizar según sea necesario.

Servicio OSGi

El WebOptimizedImage El servicio OSGi se divide en una interfaz pública direccionable (WebOptimizedImage) y una implementación interna (WebOptimizedImageImpl). El WebOptimizedImageImpl AEM devuelve una URL de imagen optimizada para la web cuando se ejecuta en el SDK de la representación web as a Cloud Service AEM AEM y una URL de representación web estática en el SDK de la, lo que permite que el componente permanezca funcional en el SDK de la.

Interfaz

La interfaz define el contrato de servicio OSGi con el que pueden interactuar otros códigos, como los modelos Sling.

package com.adobe.aem.guides.wknd.core.images;

import org.apache.sling.api.resource.ResourceResolver;
import org.osgi.annotation.versioning.ProviderType;

import java.util.Map;

/**
 * OSGi Service that acts as a facade for the AssetDelivery API, such that a fallback can be automatically served on the AEM SDK.
 *
 * This service can be extended to provide additional functionality, such as srcsets, etc.
 */
@ProviderType
public interface WebOptimizedImage {
    /**
     * Returns the Web Optimized Image URL.
     * @param resourceResolver the user's resource resolver
     * @param path the path to the asset
     * @param options the options to pass to the AssetDelivery API
     * @return the Web Optimized Image URL
     */
    String getDeliveryURL(ResourceResolver resourceResolver, String path, Map<String, Object> options);
}

Implementación

AEM La implementación del servicio OSGi incluye una referencia opcional a la implementación de un servicio de AssetDelivery Servicio OSGi y lógica de reserva para seleccionar una URL de imagen adecuada cuando AssetDelivery es null AEM en el SDK de la. La lógica de reserva se puede actualizar según los requisitos.

package com.adobe.aem.guides.wknd.core.images.impl;

import com.adobe.aem.guides.wknd.core.images.WebOptimizedImage;
import com.adobe.cq.wcm.spi.AssetDelivery;
import com.day.cq.dam.api.Asset;
import com.day.cq.dam.api.Rendition;
import com.day.cq.dam.api.RenditionPicker;
import com.day.cq.dam.commons.util.DamUtil;
import org.apache.commons.lang3.StringUtils;
import org.apache.sling.api.resource.Resource;
import org.apache.sling.api.resource.ResourceResolver;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference;
import org.osgi.service.component.annotations.ReferenceCardinality;

import java.util.Map;
@Component
public class WebOptimizedImageImpl implements WebOptimizedImage {
    private static final String DEFAULT_FORMAT = "webp";
    @Reference(cardinality = ReferenceCardinality.OPTIONAL)
    private volatile AssetDelivery assetDelivery;

    /**
     * Returns the Web Optimized Image URL.
     * @param resourceResolver the user's resource resolver
     * @param path the path to the asset
     * @param options the options to pass to the AssetDelivery API
     * @return the Web Optimized Image URL
     */
    @Override
    public String getDeliveryURL(ResourceResolver resourceResolver, String path, Map<String, Object> options) {
        if (assetDelivery != null) {
            return getWebOptimizedUrl(resourceResolver, path, options);
        } else {
            return getFallbackUrl(resourceResolver, path);
        }
    }
    /**
     * Uses the AssetDelivery API to get the Web Optimized Image URL.
     * @param resourceResolver the user's resource resolver
     * @param path the path to the asset
     * @param options the options to pass to the AssetDelivery API
     * @return the Web Optimized Image URL
     */
    private String getWebOptimizedUrl(ResourceResolver resourceResolver, String path, Map<String, Object> options) {
        Resource resource = resourceResolver.getResource(path);
        Asset asset = DamUtil.resolveToAsset(resource);

        // These 3 options are required for the AssetDelivery API to work, else it will return null
        options.put("path", asset.getPath());
        options.put("format", StringUtils.defaultString((String) options.get("format"), DEFAULT_FORMAT));
        options.put("seoname", StringUtils.defaultString((String) options.get("seoname"), asset.getName()));

        // The resource only provides the security context into AEM so the asset's UUID can be looked up for the Web Optimized Image URL
        return assetDelivery.getDeliveryURL(resource, options);
    }

    /**
     * Fallback to the static web rendition if the AssetDelivery API is not available, meaning the code is running on the AEM SDK.
     * @param resourceResolver the user's resource resolver
     * @param path the path to the asset
     * @return the path to the web rendition
     */
    private String getFallbackUrl(ResourceResolver resourceResolver, String path) {
        Resource resource = resourceResolver.getResource(path);
        Asset asset = DamUtil.resolveToAsset(resource);

        return asset.getRendition(WebRenditionPicker).getPath();
    }

    /**
     * Picks the web rendition of the asset.
     */
    private static final RenditionPicker WebRenditionPicker = new RenditionPicker() {
        @Override
        public Rendition getRendition(Asset asset) {
            return asset.getRenditions().stream().filter(rendition -> StringUtils.startsWith(rendition.getName(), "cq5dam.web.")).findFirst().orElse(asset.getOriginal());
        }
    };
}

Modelo Sling

El ExampleWebOptimizedImages El modelo Sling se divide en una interfaz pública accesible (ExampleWebOptimizedImages) y una implementación interna (ExampleWebOptimizedImagesImpl);

El ExampleWebOptimizedImagesImpl El modelo Sling recopila la lista de recursos de imagen que se van a mostrar e invoca el recurso personalizado WebOptimizedImage Servicio OSGi para obtener la URL de imagen optimizada para la web. AEM Dado que este modelo Sling representa un componente de la, tiene los métodos habituales como isEmpty(), getId(), y getData() sin embargo, estos métodos no son directamente relevantes para utilizar imágenes optimizadas para la web.

Interfaz

La interfaz define el contrato del modelo Sling con el que puede interactuar otro código, como HTL.

package com.adobe.aem.guides.wknd.core.models;

import com.adobe.cq.wcm.core.components.models.datalayer.ComponentData;
import com.fasterxml.jackson.annotation.JsonProperty;

import java.util.List;

public interface ExampleWebOptimizedImages {

    /**
     * @return a list of web optimized images for the component to display. Each item in the list has necessary information to render the image.
     */
    List<Img> getImages();

    /**
     * @return true if this component has no images to display.
     */
    boolean isEmpty();

    /**
     * @return String representing the unique identifier of the ExampleWebOptimizedImages component on a page
     */
    String getId();

    /**
     * @return JSON data to populate the data layer
     */
    @JsonProperty("dataLayer")
    default ComponentData getData() {
        return null;
    }

    /**
     * Describes a web optimized image.
     */
    interface Img {
        /**
         * @return the URL to the web optimized rendition of the image.
         */
        String getSrc();

        /**
         * @return the alt text of the web optimized image.
         */
        String getAlt();

        /**
         * @return the height of the web optimized image.
         */
        String getHeight();
        /**
         * @return the width of the web optimized image.
         */
        String getWidth();
    }
}

Implementación

El modelo Sling utiliza el personalizado WebOptimizeImage El servicio OSGi recopila las direcciones URL de imagen optimizadas para la web de los recursos de imagen que muestra su componente.

En este ejemplo, se utiliza una consulta simple para recopilar recursos de imagen.

package com.adobe.aem.guides.wknd.core.models.impl;

import com.adobe.aem.guides.wknd.core.images.WebOptimizedImage;
import com.adobe.aem.guides.wknd.core.models.ExampleWebOptimizedImages;
import com.adobe.cq.wcm.core.components.models.datalayer.ComponentData;
import com.adobe.cq.wcm.core.components.models.datalayer.builder.DataLayerBuilder;
import com.adobe.cq.wcm.core.components.util.ComponentUtils;
import com.day.cq.dam.api.Asset;
import com.day.cq.dam.commons.util.DamUtil;
import com.day.cq.wcm.api.Page;
import com.day.cq.wcm.api.components.ComponentContext;
import org.apache.sling.api.SlingHttpServletRequest;
import org.apache.sling.api.resource.Resource;
import org.apache.sling.models.annotations.DefaultInjectionStrategy;
import org.apache.sling.models.annotations.Model;
import org.apache.sling.models.annotations.Required;
import org.apache.sling.models.annotations.injectorspecific.OSGiService;
import org.apache.sling.models.annotations.injectorspecific.ScriptVariable;
import org.apache.sling.models.annotations.injectorspecific.Self;

import java.util.*;

@Model(
        adaptables = {SlingHttpServletRequest.class},
        adapters = {ExampleWebOptimizedImages.class},
        resourceType = {ExampleWebOptimizedImagesImpl.RESOURCE_TYPE},
        defaultInjectionStrategy = DefaultInjectionStrategy.OPTIONAL
)
public class ExampleWebOptimizedImagesImpl implements ExampleWebOptimizedImages {

    protected static final String RESOURCE_TYPE = "wknd/components/example-web-optimized-images";

    private static final int MAX_RESULTS = 10;

    @Self
    @Required
    private SlingHttpServletRequest request;

    @OSGiService
    private WebOptimizedImage webOptimizedImage;

    @ScriptVariable
    private Page currentPage;

    @ScriptVariable
    protected ComponentContext componentContext;

    private List<Img> images;

    // XPath query to find image assets to display
    private static final String XPATH_QUERY = "/jcr:root/content/dam/wknd-shared/en/adventures//element(*, dam:Asset) [ (jcr:contains(jcr:content/metadata/@dc:format, 'image/')) ]";
    @Override
    public List<Img> getImages() {

        if (images == null) {
            images = new ArrayList<>();

            // Set the AssetDelivery options to request a web-optimized rendition.
            // These options can be set as required by the implementation (Dialog, pass in from HTL via @RequestAttribute)
            final Map<String, Object> options = new HashMap<>();
            options.put("format", "webp");
            options.put("preferwebp", "true");
            options.put("width", "350");
            options.put("height", "350");

            final Iterator<Resource> results = request.getResourceResolver().findResources(XPATH_QUERY, "xpath");

            while (results.hasNext() && images.size() < MAX_RESULTS) {
                Resource resource = results.next();
                Asset asset = DamUtil.resolveToAsset(resource);

                // Get the image URL; the web-optimized rendition on AEM as a Cloud Service, or the static web rendition fallback on AEM SDK
                final String url = webOptimizedImage.getDeliveryURL(request.getResourceResolver(), resource.getPath(), options);

                // Add the image to the list that is passed to the HTL component to display
                // We'll add some extra attributes so that the HTL can display the image in a performant, SEO-friendly, and accessible way
                // ImgImpl can be extended to add additional attributes, such as srcset, etc.
                images.add(new ImgImpl(url, asset.getName(), (String) options.get("height"), (String) options.get("width")));
            }
        }

        return this.images;
    }

    @Override
    public boolean isEmpty() {
        return getImages().isEmpty();
    }

    @Override
    public String getId() {
        return ComponentUtils.getId(request.getResource(), currentPage, componentContext);
    }

    @Override
    public ComponentData getData() {
        if (ComponentUtils.isDataLayerEnabled(request.getResource())) {
            return DataLayerBuilder.forComponent()
                    .withId(() -> getId())
                    .withType(() -> RESOURCE_TYPE)
                    .build();
        }
        return null;
    }

    class ImgImpl implements Img {
        private final String url;
        private final String alt;
        private final int height;
        private final int width;

        public ImgImpl(String url, String alt, String height, String width) {
            this.url = url;
            this.alt = alt;
            this.height = Integer.parseInt(height);
            this.width = Integer.parseInt(width);
        }

        @Override
        public String getSrc() {
            return url;
        }

        @Override
        public String getAlt() {
            return alt;
        }

        @Override
        public String getHeight() {
            return height + "px";
        }

        @Override
        public String getWidth() {
            return width + "px";
        }
    }
}

AEM Componente

AEM Un componente de está enlazado al tipo de recurso de Sling de WebOptimizedImagesImpl Implementación del modelo Sling y es responsable de mostrar la lista de imágenes.

El componente recibe una lista de Img objetos mediante getImages() AEM que incluyen las imágenes WEBP optimizadas para la web cuando se ejecutan en el as a Cloud Service de la . El componente recibe una lista de Img objetos mediante getImages() que incluyen imágenes web estáticas PNG/JPEG AEM al ejecutarse en el SDK de la.

HTL

El HTL utiliza el WebOptimizedImages Modelo Sling y procesa la lista de Img objetos devueltos por getImages().

<style>
    .cmp-example-web-optimized-images__list {
        width: 100%;
        list-style: none;
        padding: 0;
        display: flex;
        flex-wrap: wrap;
        justify-content: space-between;
        gap: 2rem;
    }

    .cmp-example-web-optimized-images-list__item {
        margin: 0;
        padding: 0;
    }
</style>

<div data-sly-use.exampleImages="com.adobe.aem.guides.wknd.core.models.ExampleWebOptimizedImages"
     data-sly-use.placeholderTemplate="core/wcm/components/commons/v1/templates.html"
     data-sly-test.hasContent="${!exampleImages.empty}"
     data-cmp-data-layer="${exampleImages.data.json}">

    <h3>Example web-optimized images</h3>

    <ul class="cmp-example-web-optimized-images__list"
        data-sly-list.item="${exampleImages.images}">
        <li class="cmp-example-web-optimized-images-list__item">
            <img class="cmp-example-web-optimized-images__image"
                 src="${item.src}"
                 alt="${item.alt}"
                 width="${item.width}"/>
        </li>
    </ul>
</div>
<sly data-sly-call="${placeholderTemplate.placeholder @ isEmpty=!hasContent, classAppend='cmp-example-web-optimized-images'}"></sly>
recommendation-more-help
4859a77c-7971-4ac9-8f5c-4260823c6f69