開發Asset compute工作人員

asset compute背景工作是Asset compute專案的核心,因為提供可執行或協調資產所執行工作以建立新轉譯的自訂功能。

asset compute專案會自動產生一個簡單背景工作,將資產的原始二進位檔複製到指定的轉譯中,不需進行任何轉換。 在本教學課程中,我們將修改此背景工作,以製作更有趣的轉譯,以說明Asset compute背景工作的強大功能。

我們將建立一個Asset compute背景工作,產生新的水準影像轉譯,在資產轉譯的左側和右側覆蓋空白空間,但資產版本模糊。 最終格式副本的寬度、高度和模糊都是參數化的。

asset compute工作器調用的邏輯流

asset compute背景工作實作Asset computeSDK背景工作API合約,位於 renditionCallback(...) 功能,其概念上:

  • 輸入: AEM資產的原始二進位和處理設定檔參數
  • 輸出: 要新增一或多個轉譯至AEM資產

asset compute工作邏輯流

  1. AEM製作服務會叫用Asset compute背景工作,提供資產的 (1a) 原始二進位(source 參數),和 (1b) 處理設定檔中定義的任何參數(rendition.instructions 參數)。

  2. asset computeSDK可協調自訂Asset compute中繼資料背景工作的執行 renditionCallback(...) 函式,根據資產的原始二進位格式副本產生新的二進位格式副本 (1a) 和任何參數 (1b).

    • 在本教學課程中,會建立「正在處理中」的轉譯,這表示工作程式會製作轉譯,但也可以將來源二進位檔傳送至其他Web服務API,以產生轉譯。
  3. asset compute工作器會將新格式副本的二進位資料儲存至 rendition.path.

  4. 寫入的二進位資料 rendition.path 會透過Asset computeSDK傳輸至AEM作者服務,並公開為 (4a) 文字轉譯及 (4b) 保存至資產的中繼資料節點。

上圖闡述了Asset compute開發人員所關心的問題,以及Asset compute工作人員調用的邏輯流程。 對於好奇的人, asset compute執行的內部詳細資訊 不過,只有公開Asset computeSDK API合約可依賴。

工人解剖

所有Asset compute工人都遵循相同的基本結構和投入/產出合同。

'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() { ... }

開啟工作索引.js

自動產生的index.js

  1. 確認Asset compute專案在VS程式碼中開啟
  2. 導覽至 /actions/worker 資料夾
  3. 開啟 index.js 檔案

這是我們在本教學課程中將修改的背景JavaScript檔案。

安裝和匯入支援的npm模組

以Node.js為基礎的Asset compute專案,可善用強大的 npm模組生態系統. 若要運用npm模組,我們必須先將它們安裝至我們的Asset compute專案。

在這個工人中,我們利用 jimp 直接在Node.js程式碼中建立和操控轉譯影像。

警告

並非所有npm模組都支援Asset compute操作資產。 不支援依賴應用程式(如ImageMagick或其他OS相關庫)存在的npm模組。 最好限制為僅限JavaScriptnpm模組的使用。

  1. 在Asset compute專案的根目錄中開啟命令列(可透過以VS程式碼執行) 終端機>新終端機)並執行命令:

    $ npm install jimp
    
  2. 匯入 jimp 模組放入背景工作程式碼中,以便透過 Jimp JavaScript物件。
    更新 require 在工人的 index.js 若要匯入 Jimp 物件 jimp 模組:

    '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
    });
    

讀取參數

asset compute背景工作可讀取參數,這些參數可透過AEMas a Cloud Service製作服務中定義的處理設定檔傳入。 參數會透過 rendition.instructions 物件。

可透過存取 rendition.instructions.<parameterName> 在背景代碼中。

在此處,我們將閱讀可設定轉譯的 SIZE, BRIGHTNESSCONTRAST,若未透過處理設定檔提供任何值,則會提供預設值。 請注意 renditions.instructions 從AEMas a Cloud Service處理設定檔叫用時,會以字串形式傳入,因此請確定這些設定檔在背景程式碼中已轉換為正確的資料類型。

'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
}

擲回錯誤

asset compute工作人員可能會遇到導致錯誤的情況。 AdobeAsset computeSDK提供 一組預先定義的錯誤 在遇到這種情況時可以拋出。 若未套用特定錯誤類型,則 GenericError 可使用,或特定自訂 ClientErrors 可定義。

開始處理轉譯之前,請檢查以確保此工作器的上下文中所有參數都有效且受支援:

  • 確保的格式副本指令參數 SIZE, CONTRAST,和 BRIGHTNESS 有效。 否則,傳回自訂錯誤 RenditionInstructionsError.
    • 自訂 RenditionInstructionsError 擴展類 ClientError 是在此檔案底部定義。 使用特定的自訂錯誤,在 寫入測試 為工人。
'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);
    }
}

建立轉譯

在讀取、淨化和驗證參數後,會寫入程式碼以產生轉譯。 產生轉譯的虛擬碼如下:

  1. 建立新 renditionImage 透過 size 參數。
  2. 建立 image 來源資產的二進位物件
  3. 使用 Jimp 轉換影像的程式庫:
    • 將原始影像裁切到正方形
    • 從「方形」影像的中心切開圓
    • 縮放以符合由 SIZE 參數值
    • 根據 CONTRAST 參數值
    • 根據 BRIGHTNESS 參數值
  4. 放置已轉換的 image 進入 renditionImage 有透明背景
  5. 寫作, renditionImage to rendition.path 如此一來,資產就能以資產轉譯形式儲存回AEM。

此程式碼採用 Jimp API 執行這些影像轉換。

asset compute工作者必須同步完成工作, rendition.path 必須在工人之前寫回 renditionCallback 完成。 這需要使用 await 運算元。 如果您不熟悉JavaScript非同步函式,以及如何讓這些函式以同步方式執行,請熟悉 JavaScript的await運算子.

已完成的工作人員 index.js 看起來應該像這樣:

'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);
    }
}

運行工作

現在工作程式碼已完成,且先前已在 manifest.yml,則可使用本機Asset compute開發工具執行,以查看結果。

  1. 從Asset compute專案的根目錄

  2. 執行 aio app run

  3. 等待Asset compute開發工具在新視窗中開啟

  4. 選擇檔案…… 下拉式清單,選取要處理的範例影像

    • 選取要作為源資產二進位檔的範例影像檔案
    • 如果尚未存在,請點選 (+) 上傳 樣本影像 檔案,然後刷新「開發工具」瀏覽器窗口
  5. 更新 "name": "rendition.png" 作為生成透明PNG的工作器。

    • 請注意,此「name」參數僅用於開發工具,不應依賴。
    {
        "renditions": [
            {
                "worker": "...",
                "name": "rendition.png"
            }
        ]
    }
    
  6. 點選 執行 並等待格式副本產生

  7. 轉譯 區段會預覽產生的轉譯。 點選轉譯預覽以下載完整轉譯

    預設PNG轉譯

使用參數運行工作器

透過處理設定檔設定傳入的參數,可在「Asset compute開發工具」中模擬,方法是在轉譯參數JSON上提供索引鍵/值配對。

警告

在本機開發期間,當從AEM以Cloud Service處理設定檔的形式傳入為字串時,可以使用各種資料類型來傳入值,因此請務必視需要剖析正確的資料類型。
比如,Jimp crop(width, height) 函式需要其參數 ints.若 parseInt(rendition.instructions.size) 不會剖析為int,則對的呼叫 jimp.crop(SIZE, SIZE) 失敗,因為參數是不相容的「字串」類型。

我們的程式碼接受下列項目的參數:

  • size 會將轉譯的大小(高度和寬度定義為整數)
  • contrast 定義對比度調整,必須介於–1和1之間,作為浮點
  • brightness 定義亮度調整,必須介於–1和1之間,作為浮點

這些在工作人員中讀 index.js 透過:

  • const SIZE = parseInt(rendition.instructions.size) || 800
  • const CONTRAST = parseFloat(rendition.instructions.contrast) || 0
  • const BRIGHTNESS = parseFloat(rendition.instructions.brightness) || 0
  1. 更新轉譯參數以自訂大小、對比度和亮度。

    {
        "renditions": [
            {
                "worker": "...",
                "name": "rendition.png",
                "size": "450",
                "contrast": "0.30",
                "brightness": "0.15"
            }
        ]
    }
    
  2. 點選 執行 an

  3. 點選轉譯預覽以下載並檢閱產生的轉譯。 請注意其尺寸,以及與預設轉譯相比對比度和亮度的變更方式。

    參數化PNG轉譯

  4. 將其他影像上傳至 源檔案 下拉式清單,然後嘗試使用不同參數對工作人員執行作業!

在Github上工作index.js

最後 index.js 可在Github上取得,網址為:

疑難排解

本頁內容