Entwickeln eines Asset Compute-Sekundärs
Asset Compute-Sekundäre bilden den Kern eines Asset Compute-Projekts, da sie eine benutzerdefinierte Funktion bereitstellen, mit der die an einem Asset durchgeführten Arbeiten zur Erstellung einer neuen Ausgabedarstellung ausgeführt oder orchestriert werden.
Das Asset Compute-Projekt generiert automatisch einen einfachen Sekundär, der die ursprüngliche Binärdatei des Assets in eine benannte Ausgabedarstellung kopiert, ohne dass Transformationen erfolgen. In diesem Tutorial werden wir diesen Sekundär ändern, um eine interessantere Ausgabedarstellung zu erstellen und damit die Macht von Asset Compute-Sekundären zu veranschaulichen.
Wir erstellen einen Asset Compute-Sekundär, der eine neue horizontale Bilddarstellung generiert, die leeren Raum links und rechts neben der Asset-Ausgabedarstellung mit einer verschwommenen Version des Assets abdeckt. Die Breite, Höhe und Unschärfe der endgültigen Ausgabedarstellung sind parametrisiert.
Logischer Ablauf eines Asset Compute-Sekundäraufrufs
Asset Compute-Sekundäre implementieren den API-Vertrag für den Asset Compute-SDK-Sekundär in der Funktion renditionCallback(...)
, für die konzeptionell Folgendes gilt:
- Eingabe: Die originale Binärdatei und die Verarbeitungsprofil-Parameter eines AEM Assets
- Ausgabe: Eine oder mehrere Ausgabedarstellungen, die dem AEM Asset hinzugefügt werden sollen
-
Der AEM-Author-Service ruft den Asset Compute-Sekundär auf und stellt (1a) die originale Binärdatei (Parameter
source
) und (1b) alle im Verarbeitungsprofil definierten Parameter (Parameterrendition.instructions
) bereit. -
Das Asset Ccompute-SDK orchestriert die Ausführung der Funktion
renditionCallback(...)
des benutzerdefinierten Asset Compute-Metadaten-Sekundärs, wodurch eine neue binäre Ausgabedarstellung entsteht, die auf der ursprünglichen Binärdatei des Assets (1a) und allen Parametern (1b) basiert.- In diesem Tutorial wird die Ausgabedarstellung „in Bearbeitung“ erstellt, d. h., der Sekundär stellt die Ausgabedarstellung zusammen. Die Quellbinärdatei kann jedoch zur Ausgabedarstellungsgenerierung auch an andere Web-Dienst-APIs gesendet werden.
-
Der Asset Compute-Sekundär speichert die Binärdaten der neuen Ausgabedarstellung in
rendition.path
. -
Die Binärdaten, die in
rendition.path
geschrieben sind, werden über das Asset Compute-SDK an den AEM-Author-Service übertragen und (4a) als Textausgabe angezeigt und (4b) im Metadatenknoten des Assets beibehalten.
Das obige Diagramm zeigt die Asset Compute-Entwickleranliegen und den logischen Fluss des Asset-Compute-Sekundäraufrufs. Für Neugierige sind die internen Details der Asset Compute-Ausführung verfügbar, jedoch sind nur die öffentlichen API-Verträge für Asset Compute-SDK zuverlässig.
Anatomie eines Sekundärs
Alle Asset Compute-Sekundäre folgen der gleichen Grundstruktur und dem gleichen Eingabe-/Ausgabevertrag.
'use strict';
// Any npm module imports used by the worker
const { worker, SourceCorruptError } = require('@adobe/asset-compute-sdk');
const fs = require('fs').promises;
/**
Exports the worker implemented by a custom rendition callback function, which parametrizes the input/output contract for the worker.
+ `source` represents the asset's original binary used as the input for the worker.
+ `rendition` represents the worker's output, which is the creation of a new asset rendition.
+ `params` are optional parameters, which map to additional key/value pairs, including a sub `auth` object that contains Adobe I/O access credentials.
**/
exports.main = worker(async (source, rendition, params) => {
// Perform any necessary source (input) checks
const stats = await fs.stat(source.path);
if (stats.size === 0) {
// Throw appropriate errors whenever an erring condition is met
throw new SourceCorruptError('source file is empty');
}
// Access any custom parameters provided via the Processing Profile configuration
let param1 = rendition.instructions.exampleParam;
/**
Perform all work needed to transform the source into the rendition.
The source data can be accessed:
+ In the worker via a file available at `source.path`
+ Or via a presigned GET URL at `source.url`
**/
if (success) {
// A successful worker must write some data back to `renditions.path`.
// This example performs a trivial 1:1 copy of the source binary to the rendition
await fs.copyFile(source.path, rendition.path);
} else {
// Upon failure an Asset Compute Error (exported by @adobe/asset-compute-commons) should be thrown.
throw new GenericError("An error occurred!", "example-worker");
}
});
/**
Optionally create helper classes or functions the worker's rendition callback function invokes to help organize code.
Code shared across workers, or to complex to be managed in a single file, can be broken out across supporting JavaScript files in the project and imported normally into the worker.
**/
function customHelperFunctions() { ... }
Öffnen der Sekundärdatei index.js
- Stellen Sie sicher, dass das Asset Compute-Projekt in VS Code geöffnet ist.
- Navigieren Sie zum Ordner
/actions/worker
. - Öffnen Sie die Datei
index.js
.
Dies ist die JavaScript-Sekundärdatei, die wir in diesem Tutorial ändern werden.
Installieren und Importieren von unterstützenden npm-Modulen
Da Asset Compute-Projekte auf Node.js basieren, profitieren sie vom leistungsstarken npm-Modul-Ökosystem. Um npm-Module zu nutzen, müssen wir sie zunächst in unserem Asset Compute-Projekt installieren.
In diesem Sekundär nutzen wir jimp, um das Ausgabedarstellungsbild direkt im Code von Node.js zu erstellen und zu bearbeiten.
-
Öffnen Sie die Befehlszeile im Stammverzeichnis Ihres Asset Compute-Projekts (dies kann im VS-Code über Terminal > Neues Terminal durchgeführt werden) und führen Sie den folgenden Befehl aus:
code language-none $ npm install jimp
-
Importieren Sie das
jimp
-Modul in den Code des Sekundärs, damit es über dasJimp
-JavaScript-Objekt genutzt werden kann.
Aktualisieren Sie dierequire
-Anweisungen am Anfang derindex.js
des Sekundärs, um dasJimp
-Objekt aus demjimp
-Modul zu importieren:code language-javascript 'use strict'; const Jimp = require('jimp'); const { worker, SourceCorruptError } = require('@adobe/asset-compute-sdk'); const fs = require('fs').promises; exports.main = worker(async (source, rendition, params) => { // Check handle a corrupt input source const stats = await fs.stat(source.path); if (stats.size === 0) { throw new SourceCorruptError('source file is empty'); } // Do work here });
Lesen der Parameter
Asset Compute-Sekundäre können Parameter lesen, die über Verarbeitungsprofile übergeben werden können, die im Author-Service von AEM as a Cloud Service definiert sind. Die Parameter werden über das Objekt rendition.instructions
an den Sekundär übertragen.
Diese können durch Zugreifen auf rendition.instructions.<parameterName>
im Code des Sekundärs gelesen werden.
Hier geben wir die einstellbaren Werte für SIZE
, BRIGHTNESS
und CONTRAST
der Ausgabedarstellung ein, wobei wir Standardwerte angeben, wenn keine über das Verarbeitungsprofil bereitgestellt wurden. Beachten Sie, dass renditions.instructions
als Zeichenfolgen übergeben werden, wenn sie über Verarbeitungsprofile von AEM as a Cloud Service aufgerufen werden. Stellen Sie daher sicher, dass sie in die richtigen Datentypen im Code des Sekundärs umgewandelt werden.
'use strict';
const Jimp = require('jimp');
const { worker, SourceCorruptError } = require('@adobe/asset-compute-sdk');
const fs = require('fs').promises;
exports.main = worker(async (source, rendition, params) => {
const stats = await fs.stat(source.path);
if (stats.size === 0) {
throw new SourceCorruptError('source file is empty');
}
// Read in parameters and set defaults if parameters are provided
// Processing Profiles pass in instructions as Strings, so make sure to parse to correct data types
const SIZE = parseInt(rendition.instructions.size) || 800;
const CONTRAST = parseFloat(rendition.instructions.contrast) || 0;
const BRIGHTNESS = parseFloat(rendition.instructions.brightness) || 0;
// Do work here
}
Ausgabe von Fehlern errors
Asset Compute-Sekundäre können auf Fehlersituationen stoßen. Das Adobe Asset Compute-SDK bietet eine Suite vordefinierter Fehler, die bei solchen Situationen ausgelöst werden können. Wenn kein bestimmter Fehlertyp zutrifft, kann der GenericError
verwendet werden oder es können spezifische benutzerdefinierte ClientErrors
definiert werden.
Bevor Sie mit der Verarbeitung der Ausgabedarstellung beginnen, überprüfen Sie, ob alle Parameter im Kontext dieses Sekundärs gültig sind und unterstützt werden:
- Stellen Sie sicher, dass die Anweisungsparameter für
SIZE
,CONTRAST
undBRIGHTNESS
der Ausgabedarstellung gültig sind. Wenn nicht, wird ein benutzerdefinierter FehlerRenditionInstructionsError
ausgegeben.- Eine benutzerdefinierte
RenditionInstructionsError
-Klasse, dieClientError
erweitert, wird am Ende dieser Datei definiert. Die Verwendung eines spezifischen, benutzerdefinierten Fehlers ist nützlich beim Schreiben von Tests für den Sekundär.
- Eine benutzerdefinierte
'use strict';
const Jimp = require('jimp');
// Import the Asset Compute SDK provided `ClientError`
const { worker, SourceCorruptError, ClientError } = require('@adobe/asset-compute-sdk');
const fs = require('fs').promises;
exports.main = worker(async (source, rendition, params) => {
const stats = await fs.stat(source.path);
if (stats.size === 0) {
throw new SourceCorruptError('source file is empty');
}
// Read in parameters and set defaults if parameters are provided
const SIZE = parseInt(rendition.instructions.size) || 800;
const CONTRAST = parseFloat(rendition.instructions.contrast) || 0;
const BRIGHTNESS = parseFloat(rendition.instructions.brightness) || 0;
if (SIZE <= 10 || SIZE >= 10000) {
// Ensure size is within allowable bounds
throw new RenditionInstructionsError("'size' must be between 10 and 1,0000");
} else if (CONTRAST <= -1 || CONTRAST >= 1) {
// Ensure contrast is valid value
throw new RenditionInstructionsError("'contrast' must between -1 and 1");
} else if (BRIGHTNESS <= -1 || BRIGHTNESS >= 1) {
// Ensure contrast is valid value
throw new RenditionInstructionsError("'brightness' must between -1 and 1");
}
// Do work here
}
// Create a new ClientError to handle invalid rendition.instructions values
class RenditionInstructionsError extends ClientError {
constructor(message) {
// Provide a:
// + message: describing the nature of this erring condition
// + name: the name of the error; usually same as class name
// + reason: a short, searchable, unique error token that identifies this error
super(message, "RenditionInstructionsError", "rendition_instructions_error");
// Capture the strack trace
Error.captureStackTrace(this, RenditionInstructionsError);
}
}
Erstellen der Ausgabedarstellung
Mit den gelesenen, bereinigten und validierten Parametern wird ein Code geschrieben, um die Ausgabedarstellung zu generieren. Der Pseudo-Code für die Generierung von Ausgabedarstellungen lautet wie folgt:
-
Erstellen Sie eine neue
renditionImage
-Arbeitsfläche in quadratischen Dimensionen, die über diesize
-Parameter definiert werden. -
Erstellen Sie ein
image
-Objekt aus der Binärdatei des Quell-Assets -
Verwenden Sie die Jimp-Bibliothek, um das Bild zu transformieren:
- Schneiden Sie das Originalbild auf ein zentriertes Quadrat zu
- Schneiden Sie einen Kreis aus der Mitte des quadratischen Bildes aus
- Skalieren Sie es so, dass es in die Abmessungen passt, die durch den
SIZE
-Parameterwert definiert werden - Passen Sie den Kontrast basierend auf dem
CONTRAST
-Parameterwert an - Passen Sie die Helligkeit basierend auf dem
BRIGHTNESS
-Parameterwert an
-
Platzieren Sie das umgewandelte
image
in die Mitte desrenditionImage
mit transparentem Hintergrund -
Schreiben Sie das zusammengesetzte
renditionImage
in denrendition.path
, sodass es als Asset-Ausgabedarstellung wieder in AEM gespeichert werden kann.
Dieser Code verwendet die Jimp-APIs, um diese Bildtransformationen durchzuführen.
Asset Compute-Sekundäre müssen ihre Arbeit synchron beenden und der rendition.path
muss vollständig zurückgeschrieben worden sein, bevor der renditionCallback
des Sekundärs beendet ist. Dies erfordert, dass asynchrone Funktionsaufrufe mithilfe des await
-Operators stattfinden. Wenn Sie nicht mit asynchronen JavaScript-Funktionen vertraut sind und nicht wissen, wie Sie diese synchron ausführen lassen können, sollten Sie sich mit dem Await-Operator von JavaScript vertraut machen.
Die abgeschlossene index.js
Sekundärs sollte wie folgt aussehen:
'use strict';
const Jimp = require('jimp');
const { worker, SourceCorruptError, ClientError } = require('@adobe/asset-compute-sdk');
const fs = require('fs').promises;
exports.main = worker(async (source, rendition, params) => {
const stats = await fs.stat(source.path);
if (stats.size === 0) {
throw new SourceCorruptError('source file is empty');
}
// Read/parse and validate parameters
const SIZE = parseInt(rendition.instructions.size) || 800;
const CONTRAST = parseFloat(rendition.instructions.contrast) || 0;
const BRIGHTNESS = parseFloat(rendition.instructions.brightness) || 0;
if (SIZE <= 10 || SIZE >= 10000) {
throw new RenditionInstructionsError("'size' must be between 10 and 1,0000");
} else if (CONTRAST <= -1 || CONTRAST >= 1) {
throw new RenditionInstructionsError("'contrast' must between -1 and 1");
} else if (BRIGHTNESS <= -1 || BRIGHTNESS >= 1) {
throw new RenditionInstructionsError("'brightness' must between -1 and 1");
}
// Create target rendition image
let renditionImage = new Jimp(SIZE, SIZE, 0x0);
// Read and perform transformations on the source binary image
let image = await Jimp.read(source.path);
// Crop a circle from the source asset, and then apply contrast and brightness
image.crop(
image.bitmap.width < image.bitmap.height ? 0 : (image.bitmap.width - image.bitmap.height) / 2,
image.bitmap.width < image.bitmap.height ? (image.bitmap.height - image.bitmap.width) / 2 : 0,
image.bitmap.width < image.bitmap.height ? image.bitmap.width : image.bitmap.height,
image.bitmap.width < image.bitmap.height ? image.bitmap.width : image.bitmap.height
)
.circle()
.scaleToFit(SIZE, SIZE)
.contrast(CONTRAST)
.brightness(BRIGHTNESS);
// Place the transformed image onto the transparent renditionImage to save as PNG
renditionImage.composite(image, 0, 0)
// Write the final transformed image to the asset's rendition
await renditionImage.writeAsync(rendition.path);
});
// Custom error used for renditions.instructions parameter checking
class RenditionInstructionsError extends ClientError {
constructor(message) {
super(message, "RenditionInstructionsError", "rendition_instructions_error");
Error.captureStackTrace(this, RenditionInstructionsError);
}
}
Ausführen des Sekundärs
Nachdem der Code des Sekundärs jetzt vollständig ist und zuvor in manifest.yml registriert und konfiguriert wurde, kann er mit dem lokalen Asset Compute-Entwicklungs-Tool ausgeführt werden, um die Ergebnisse anzuzeigen.
-
Navigieren Sie zum Stammverzeichnis des Asset Compute-Projekts.
-
Führen Sie
aio app run
aus. -
Warten Sie, bis das Asset Compute-Entwicklungs-Tool in einem neuen Fenster geöffnet wird.
-
Wählen Sie in der Dropdown-Liste Datei auswählen… ein Beispielbild für die Verarbeitung.
- Wählen Sie eine Beispielgrafikdatei zur Verwendung als Quell-Asset-Binärdatei.
- Wenn noch keine vorhanden ist, tippen Sie auf das (+) auf der linken Seite, laden Sie ein Beispielbild hoch und aktualisieren Sie das Browser-Fenster des Entwicklungs-Tools.
-
Aktualisieren Sie
"name": "rendition.png"
, da dieser Sekundär ein transparentes PNG generiert.- Beachten Sie, dass dieser „name“-Parameter nur für das Entwicklungs-Tool verwendet wird und sich nicht darauf verlassen werden sollte.
code language-json { "renditions": [ { "worker": "...", "name": "rendition.png" } ] }
-
Tippen Sie auf Ausführen und warten Sie, bis die Ausgabedarstellung generiert wird
-
Der Abschnitt Ausgabedarstellungen zeigt eine Vorschau der generierten Ausgabedarstellung an. Tippen Sie auf die Vorschau der Ausgabedarstellung, um die vollständige Ausgabedarstellung herunterzuladen.
Ausführen des Sekundärs mit Parametern
Parameter, die über Verarbeitungsprofilkonfigurationen übergeben werden, können in Asset Compute-Entwicklungs-Tools simuliert werden, indem sie als Schlüssel/Wert-Paare in der JSON der Ausgabedarstellungsparameter angegeben werden.
Zum Beispiel erfordert die
crop(width, height)
-Funktion von Jimp, dass ihre Parameter int
s sind. Wenn parseInt(rendition.instructions.size)
nicht auf ein int geparst wird, schlägt der Aufruf von jimp.crop(SIZE, SIZE)
fehl, da die Parameter den inkompatiblen Typ „Zeichenfolge“ aufweisen.Unser Code akzeptiert Parameter für:
size
definiert die Größe der Ausgabedarstellung (Höhe und Breite als Ganzzahlen)contrast
definiert die Kontrastanpassung, muss zwischen -1 und 1 liegen, als Fließkommazahl.brightness
definiert die Helligkeitseinstellung, muss zwischen -1 und 1 liegen, als Fließkommazahl.
Diese werden in der index.js
des Sekundärs gelesen über:
const SIZE = parseInt(rendition.instructions.size) || 800
const CONTRAST = parseFloat(rendition.instructions.contrast) || 0
const BRIGHTNESS = parseFloat(rendition.instructions.brightness) || 0
-
Aktualisieren Sie die Parameter der Ausgabedarstellung, um Größe, Kontrast und Helligkeit anzupassen.
code language-json { "renditions": [ { "worker": "...", "name": "rendition.png", "size": "450", "contrast": "0.30", "brightness": "0.15" } ] }
-
Tippen Sie erneut auf Ausführen
-
Tippen Sie auf die Vorschau der Ausgabedarstellung, um die generierte Ausgabedarstellung herunterzuladen und zu überprüfen. Achten Sie auf die Dimensionen und darauf, wie Kontrast und Helligkeit im Vergleich zur Standarddarstellung geändert wurden.
-
Laden Sie andere Bilder in die Quelldatei-Dropdown-Liste hoch und versuchen Sie, für diese den Sekundär mit verschiedenen Parametern auszuführen.
index.js des Sekundärs auf Github
Die endgültige index.js
ist auf Github verfügbar unter: