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

開啟背景工作程式index.js

自動產生的index.js

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

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

安裝並匯入支援的npm模組

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

在此背景工作流程中,我們運用 jimp 直接在Node.js程式碼中建立和操作轉譯影像。

WARNING
asset compute並非所有用於資產操控的npm模組都受支援。 不支援依賴應用程式(例如ImageMagick或其他與作業系統相關的程式庫)存在性的npm模組。 最好限製為僅使用JavaScript npm模組。
  1. 在Asset compute專案的根目錄中開啟命令列(這可以在VS Code中完成,透過 終端機>新終端機)並執行命令:

    code language-none
    $ npm install jimp
    
  2. 匯入 jimp 將模組放入背景工作程式碼中,以便透過 Jimp javascript物件。
    更新 require 位於工作程式頂端的指示 index.js 匯入 Jimp 物件來自 jimp 模組:

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

讀取引數

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

存取即可讀取這些專案 rendition.instructions.<parameterName> 在背景工作程式碼中。

我們將在此閱讀可設定轉譯的 SIZEBRIGHTNESSCONTRAST,若未透過處理設定檔提供預設值,則會提供此值。 請注意 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
}

擲回錯誤 errors

asset compute背景工作程式可能會遇到導致錯誤的情況。 AdobeAsset computeSDK提供 一組預先定義的錯誤 遇到這類情況時可以擲回的錯誤。 如果沒有特定的錯誤型別, GenericError 可使用,或特定自訂 ClientErrors 可定義。

開始處理轉譯之前,請檢查以確定所有引數在此背景中是否有效及受支援:

  • 確定下列專案的轉譯指令引數: SIZECONTRAST、和 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. 撰寫構成內容, renditionImagerendition.path 所以它可以儲存回AEM做為資產轉譯。

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

asset compute工作者必須同步完成工作,而且 rendition.path 必須在工作者的 renditionCallback 完成。 這要求非同步函式呼叫須使用 await 運運算元。 如果您不熟悉JavaScript非同步函式,也不熟悉如何讓這些函式以同步方式執行,請熟悉 JavaScript的等待運運算元.

完成的工作者 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」引數僅用於開發工具,不應依賴。
    code language-json
    {
        "renditions": [
            {
                "worker": "...",
                "name": "rendition.png"
            }
        ]
    }
    
  6. 點選 執行 並等待轉譯產生

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

    預設PNG轉譯

使用引數執行背景工作

若是透過「處理設定檔」設定傳入引數,您可在Asset compute開發工具中,藉由在轉譯引數JSON上提供索引鍵/值組來模擬這些引數。

WARNING
在本機開發期間,值可在從AEM以字串形式傳入(即Cloud Service處理設定檔)時,使用各種資料型別傳入,因此請務必視需要剖析正確的資料型別。
例如,Jimp crop(width, height) 函式要求其引數 int的。如果 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. 更新轉譯引數以自訂大小、對比和亮度。

    code language-json
    {
        "renditions": [
            {
                "worker": "...",
                "name": "rendition.png",
                "size": "450",
                "contrast": "0.30",
                "brightness": "0.15"
            }
        ]
    }
    
  2. 點選 執行 再次

  3. 點選轉譯預覽以下載並檢閱產生的轉譯。 請注意它的尺寸,以及相較於預設轉譯,對比度和亮度是如何變更的。

    引數化的PNG轉譯

  4. 上傳其他影像至 來源檔案 下拉式清單,然後嘗試使用不同的引數對其執行背景工作!

Github上的背景工作索引.js

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

疑難排解

recommendation-more-help
4859a77c-7971-4ac9-8f5c-4260823c6f69