開發Asset compute背景工作

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

asset compute專案會自動產生簡易的背景工作,將資產的原始二進位檔複製到已命名的轉譯中,不需要進行任何轉換。 在本教學課程中,我們將修改此背景工作以製作更有趣的轉譯,以說明Asset compute背景工作的力量。

我們將建立Asset compute背景工作,此背景工作會產生新的水準影像轉譯,以模糊版本的資產遮蓋資產轉譯左側的空白空間。 最終轉譯的寬度、高度和模糊皆已引數化。

asset compute工作者引動過程的邏輯流程

asset compute背景工作在renditionCallback(...)函式中實作Asset computeSDK背景工作API合約,其概念為:

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

Asset compute工作者邏輯流程

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

  2. asset computeSDK會根據資產的原始二進位​ (1a) ​和任何引數​ (1b),協調執行自訂Asset compute中繼資料背景工作程式的renditionCallback(...)函式,產生新的二進位轉譯。

    • 在本教學課程中,轉譯是在「處理中」建立的,這表示工作者會撰寫轉譯,不過來源二進位檔也可以傳送至其他Web服務API以產生轉譯。
  3. asset compute背景工作會將新轉譯的二進位資料儲存至rendition.path

  4. 寫入至rendition.path的二進位資料會透過Asset computeSDK傳輸至AEM Author Service,並公開為​ (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檔案

這是我們將在本教學課程中修改的Worker 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物件使用。
    更新背景工作index.js頂端的require指示詞,以從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背景工作可讀取引數,這些引數可透過AEM as a Cloud Service作者服務中定義的處理設定檔來傳入。 引數會透過rendition.instructions物件傳遞至背景工作。

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

我們將在此讀取可設定轉譯的SIZEBRIGHTNESSCONTRAST,如果未透過處理設定檔提供預設值,則會提供預設值。 請注意,從AEM as a Cloud Service處理設定檔叫用時,renditions.instructions會以字串形式傳入,因此請確定它們已轉換為背景工作程式碼中的正確資料型別。

'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

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

  • 請確定SIZECONTRASTBRIGHTNESS的轉譯指示引數有效。 如果沒有,則擲回自訂錯誤RenditionInstructionsError
'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. 以透過size引數指定的平方尺寸建立新的renditionImage畫布。

  2. 從來源資產的二進位檔建立image物件

  3. 使用​ Jimp ​資料庫來轉換影像:

    • 將原始影像裁切成正方形
    • 從「平方」影像中心剪下圓形
    • 縮放以符合SIZE引數值所定義的尺寸
    • 根據CONTRAST引數值調整對比
    • 根據BRIGHTNESS引數值調整亮度
  4. 將轉換後的image放在具有透明背景的renditionImage的中央

  5. 將構成專案renditionImage寫入rendition.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);
    }
}

執行背景工作

現在Worker程式碼已完成,而且先前已在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)的呼叫會失敗,因為引數是不相容的'String'型別。

我們的程式碼接受下列的引數:

  • 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. 將其他影像上傳到​ Source檔案 ​下拉式清單,然後嘗試使用其他引數針對這些影像執行背景工作!

Github上的背景工作索引.js

Github上的最終index.js位於:

疑難排解

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