Développer un programme de travail d’Asset Compute
Les programmes de travail Asset Compute sont l’essence d’un projet Asset Compute, car ils fournissent des fonctionnalités personnalisées qui exécutent ou orchestrent le travail effectué sur une ressource pour créer un rendu.
Le projet Asset Compute génère automatiquement un programme de travail simple qui copie le contenu binaire d’origine de la ressource dans un rendu nommé, sans aucune transformation. Dans ce tutoriel, nous allons modifier ce programme de travail afin de créer un rendu plus intéressant, qui tire parti de la puissance d’Asset Compute.
Nous allons créer un programme de travail Asset Compute qui génère un nouveau rendu d’image horizontal qui couvre l’espace vide à gauche et à droite du rendu de la ressource avec une version floue de la ressource. La largeur, la hauteur et le flou du rendu final peuvent être ajustés.
Flux logique de l’appel d’un programme de travail Asset Compute
Les programmes de travail Asset Compute implémentent l’API Worker du SDK Asset Compute dans la fonction renditionCallback(...)
:
- Entrée : fichier binaire d’origine d’une ressource AEM et paramètres de profil de traitement.
- Sortie : un ou plusieurs rendus à ajouter à la ressource AEM.
.
-
Le service de création AEM appelle le programme de travail Asset Compute, fournissant ainsi le contenu binaire d’origine (1a) de la ressource (paramètre
source
) et tous les paramètres (1b) définis dans le profil de traitement (paramètrerendition.instructions
). -
Le SDK Asset Compute orchestre l’exécution de la fonction
renditionCallback(...)
du programme de travail de métadonnées personnalisé Asset Compute et génère un nouveau rendu binaire, basé sur le fichier binaire d’origine de la ressource (1a) et tous les paramètres (1b).- Dans ce tutoriel, le rendu est créé « en cours », ce qui signifie que le programme de travail compose le rendu, mais le fichier binaire source peut également être envoyé à d’autres API de service web pour la génération du rendu.
-
Le programme de travail Asset Compute enregistre les données binaires du nouveau rendu dans
rendition.path
. -
Les données binaires écrites dans
rendition.path
sont transmises au service de création AEM via le SDK Asset Compute. Elles sont affichées sous la forme d’un rendu texte (4a) et (4b) est conservé dans le nœud de métadonnées de la ressource.
Le diagramme ci-dessus présente les préoccupations des développeurs et développeuses Asset Compute et le flux logique de l’appel des programmes de travail Asset Compute. Pour épancher votre soif de connaissances inextinguible, les détails internes de l’exécution d’Asset Compute sont disponibles, mais seuls les contrats publics de l’API de SDK Asset Compute peuvent être utilisés.
En quoi consistent les programmes de travail ?
Tous les programmes de travail Asset Compute suivent la même structure de base et le même contrat d’entrée/sortie.
'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() { ... }
Ouvrir le programme de travail index.js
.
- Assurez-vous que le projet Asset Compute est ouvert dans VS Code.
- Accédez au dossier
/actions/worker
. - Ouvrez le fichier
index.js
.
Il s’agit du fichier JavaScript du programme de travail que nous allons modifier dans ce tutoriel.
Installer et importer les modules npm de prise en charge
Basés sur Node.js, les projets Asset Compute bénéficient de la puissance de l’écosystème des modules npm. Pour tirer parti des modules npm, nous devons d’abord les installer dans notre projet Asset Compute.
Dans ce programmes de travail, nous utilisons jimp pour créer et manipuler l’image de rendu directement dans le code Node.js.
-
Ouvrez la ligne de commande à la racine de votre projet Asset Compute (cela peut être effectué dans VS Code via Terminal > Nouveau terminal) et exécutez la commande suivante :
code language-none $ npm install jimp
-
Importez le module
jimp
dans le code du programme de travail afin qu’il puisse être utilisé via l’objet JavaScriptJimp
.
Mettez à jour les directivesrequire
écrites au début de l’index.js
du programme de travail pour importer l’objetJimp
à partir du modulejimp
: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 });
Lire les paramètres
Les programmes de travail Asset Compute peuvent lire les paramètres qui peuvent être transmis via les profils de traitement définis dans le service de création AEM as a Cloud Service. Les paramètres sont transmis au programme de travail via l’objet rendition.instructions
.
Ils peuvent être lus en accédant à rendition.instructions.<parameterName>
dans le code du programme de travail.
Ici, nous lirons les valeurs SIZE
, BRIGHTNESS
et CONTRAST
du rendu configurable, en fournissant des valeurs par défaut si aucune n’a été fournie par le profil de traitement. Notez que les renditions.instructions
sont transmises en tant que chaînes lors de l’appel à partir des profils de traitement AEM as a Cloud Service. Veillez donc à ce qu’elles soient transformées en types de données corrects dans le code du programme de travail.
'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
}
Erreurs de génération errors
Les programmes de travail Asset Compute peuvent rencontrer des situations qui génèrent des erreurs. Le SDK Asset Compute d’Adobe fournit une suite d’erreurs prédéfinies qui peuvent être générées dans ce genre de situation. Si aucun type d’erreur spécifique n’est appliqué, l’GenericError
peut être utilisée, ou des ClientErrors
personnalisées spécifiques peuvent être définies.
Avant de commencer le traitement du rendu, vérifiez que tous les paramètres sont valides et pris en charge dans le contexte de ce programme de travail :
- Assurez-vous que les paramètres d’instruction de rendu de
SIZE
,CONTRAST
, etBRIGHTNESS
sont valides. Si ce n’est pas le cas, générez une erreur personnaliséeRenditionInstructionsError
.- Une classe
RenditionInstructionsError
personnalisée qui étendClientError
est définie au bas de ce fichier. L’utilisation d’une erreur personnalisée spécifique est utile lors de l’écriture de tests pour le programme de travail.
- Une classe
'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);
}
}
Créer le rendu
Avec les paramètres lus, assainis et validés, le code est écrit pour générer le rendu. Le pseudo-code pour la génération du rendu est le suivant :
-
Créez de nouveaux canevas
renditionImage
aux dimensions carrées spécifiées via le paramètresize
. -
Créez un objet
image
à partir du fichier binaire de la ressource source. -
Utilisez la bibliothèque Jimp pour transformer l’image :
- Recadrez l’image d’origine sur un carré centré.
- Coupez un cercle à partir du centre de l’image « rendue carrée ».
- Adaptez aux dimensions définies par la valeur de paramètre
SIZE
. - Ajustez le contraste en fonction de la valeur de paramètre
CONTRAST
. - Ajustez la luminosité en fonction de la valeur de paramètre
BRIGHTNESS
.
-
Placez l’
image
au milieu de l’renditionImage
avec un arrière-plan transparent. -
Écrivez la composition,
renditionImage
versrendition.path
, afin qu’elle puisse être réenregistrée dans AEM en tant que rendu de ressource.
Ce code utilise les API Jimp pour effectuer ces transformations d’image.
Les programmes de travail Assets Compute doivent terminer leur travail de manière synchrone, et le rendition.path
doit être entièrement réécrit avant la fin de l’renditionCallback
du programme de travail. Cela nécessite que les appels des fonctions asynchrones soient effectués de manière synchrone à l’aide de l’opérateur await
. Si vous ne connaissez pas les fonctions asynchrones JavaScript et que vous ne savez pas comment les exécuter de manière synchrone, familiarisez-vous avec l’opérateur d’attente de JavaScript.
L’index.js
du programme de travail terminé doit ressembler à ce qui suit :
'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);
}
}
Exécuter le programme de travail
Maintenant que le code du programme de travail est terminé, et qu’il a été enregistré et configuré dans le manifest.yml, il peut être exécuté à l’aide de l’outil de développement d’Asset Compute local pour afficher les résultats.
-
À partir de la racine du projet Asset Compute
-
Exécuter
aio app run
-
Attendez que l’outil de développement Asset Compute s’ouvre dans une nouvelle fenêtre.
-
Dans le menu déroulant Sélectionnez un fichier…, sélectionnez un exemple d’image à traiter.
- Sélectionnez un exemple de fichier image à utiliser comme fichier binaire de ressource source.
- S’il n’en existe aucun, appuyez sur le (+) à gauche, puis chargez un fichier d’exemple d’image et actualisez la fenêtre du navigateur des outils de développement.
-
Mettez à jour
"name": "rendition.png"
comme programme de travail pour générer un fichier PNG transparent.- Notez que ce paramètre « name » n’est utilisé que pour l’outil de développement et ne doit pas être utilisé.
code language-json { "renditions": [ { "worker": "...", "name": "rendition.png" } ] }
-
Appuyez sur Exécuter et attendez que le rendu soit généré.
-
La section Rendus prévisualise le rendu généré. Appuyez sur l’aperçu du rendu pour télécharger le rendu complet.
Exécuter le programme de travail avec des paramètres
Les paramètres, transmis via les configurations de profil de traitement, peuvent être simulés dans les outils de développement Asset Compute en les fournissant en tant que paires clé/valeur sur le paramètre de rendu JSON.
Par exemple, la fonction
crop(width, height)
de Jimp nécessite que ses paramètres soient des int
. Si parseInt(rendition.instructions.size)
n’est pas transformé en entier, l’appel à jimp.crop(SIZE, SIZE)
échoue, car les paramètres sont incompatibles avec le type « Chaîne ».Notre code accepte les paramètres pour les éléments suivants :
size
définit la taille du rendu (hauteur et largeur en tant qu’entiers) ;contrast
définit l’ajustement du contraste, qui doit être compris entre -1 et 1, sous la forme de flotteurs ;brightness
définit l’ajustement de la luminosité, qui doit être compris entre -1 et 1, sous la forme de flotteurs.
Ceux-ci sont lus dans le programme de travail index.js
via :
const SIZE = parseInt(rendition.instructions.size) || 800
const CONTRAST = parseFloat(rendition.instructions.contrast) || 0
const BRIGHTNESS = parseFloat(rendition.instructions.brightness) || 0
-
Mettez à jour les paramètres de rendu pour personnaliser la taille, le contraste et la luminosité.
code language-json { "renditions": [ { "worker": "...", "name": "rendition.png", "size": "450", "contrast": "0.30", "brightness": "0.15" } ] }
-
Appuyez à nouveau sur Exécuter.
-
Appuyez sur l’aperçu du rendu pour télécharger et consulter le rendu généré. Notez ses dimensions et la manière dont le contraste et la luminosité ont été modifiés par rapport au rendu par défaut.
-
Chargez d’autres images dans le menu déroulant Fichier source et essayez d’exécuter le programme de travail avec des paramètres différents.
index.js de programme de travail sur Github
L’index.js
final est disponible sur Github à :