asset compute 작업자 개발

asset compute 작업자는 자산에서 새 변환을 만들기 위해 수행한 작업을 수행하거나 조정하는 사용자 정의 기능을 제공하는 Asset compute 프로젝트의 핵심입니다.

asset compute 프로젝트는 변형 없이 자산의 원래 바이너리를 명명된 변환으로 복사하는 간단한 작업자를 자동으로 생성합니다. 이 자습서에서는 Asset compute 작업자의 기능을 보여주기 위해 이 작업자를 수정하여 보다 흥미로운 표현물을 만들어 보겠습니다.

자산의 흐릿한 버전이 있는 자산 표현물의 왼쪽 및 오른쪽에 있는 빈 공간을 포함하는 새 가로 이미지 표현물을 생성하는 Asset compute 작업자를 만듭니다. 최종 표현물의 폭, 높이 및 흐림 효과가 매개 변수화됩니다.

asset compute 작업자 호출의 논리 흐름

asset compute 작업자는 개념적으로 renditionCallback(...) 함수에서 Asset compute SDK 작업자 API 계약을 구현합니다.

  • 입력: AEM 자산의 원래 이진 및 처리 프로필 매개 변수입니다
  • 출력: AEM 자산에 추가할 하나 이상의 표현물

asset compute 작업자 논리 흐름

  1. AEM 작성자 서비스는 Asset compute 작업자를 호출하여 자산의 (1a) 원래 이진(source 매개 변수) 및 처리 프로필에 정의된 모든 매개 변수를 제공합니다(rendition.instructions 매개 변수).____

  2. asset compute SDK는 사용자 지정 Asset compute 메타데이터 작업자의 renditionCallback(...) 함수 실행을 오케스트레이션하고, 자산의 원래 이진 (1a) 및 모든 매개 변수 (1b)​에 따라 새 이진 변환을 생성합니다.

    • 이 자습서에서는 변환을 "진행 중"으로 만듭니다. 이는 작업자가 변환을 구성하지만 렌디션 생성을 위해 소스 바이너리를 다른 웹 서비스 API로 전송할 수도 있음을 의미합니다.
  3. asset compute 작업자가 새 표현물의 이진 데이터를 rendition.path에 저장합니다.

  4. rendition.path에 작성된 이진 데이터는 Asset compute SDK를 통해 AEM 작성자 서비스로 전송되고, (4a) 텍스트 표현물과 (4b)(4b) 자산의 메타데이터 노드로 노출됩니다.

위의 다이어그램은 Asset compute 개발자 대면 관련 문제와 Asset compute 작업자 호출을 설명하는 논리 흐름을 보여줍니다. 궁금하신 경우 Asset compute 실행에 대한 내부 세부 정보를 사용할 수 있지만 공개 Asset compute SDK 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 모듈 설치 및 가져오기

Node.js를 기반으로 하는 Asset compute 프로젝트는 강력한 npm 모듈 에코시스템을 통해 이점을 얻을 수 있습니다. npm 모듈을 활용하려면 먼저 Asset compute 프로젝트에 모듈을 설치해야 합니다.

이 작업자는 zip을 활용하여 Node.js 코드에서 직접 표현물 이미지를 만들고 조작합니다.

경고

자산 조작에 대한 모든 npm 모듈이 Asset compute에서 지원되는 것은 아닙니다. ImageMagick 또는 기타 OS 종속 라이브러리와 같은 응용 프로그램이 있는 npm 모듈은 지원되지 않습니다. JavaScript 전용 npm 모듈 사용을 제한하는 것이 가장 좋습니다.

  1. asset compute 프로젝트의 루트에서 명령줄을 열고(이 작업은 터미널 > New Terminal​을 통해 VS 코드에서 수행할 수 있음) 명령을 실행합니다.

    $ npm install jimp
    
  2. Jimp JavaScript 개체를 통해 사용할 수 있도록 jimp 모듈을 작업자 코드로 가져옵니다.
    작업자의 index.js 맨 위에 있는 require 지시어를 업데이트하여 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 작업자는 AEM에서 Cloud Service 작성자 서비스로 정의된 처리 프로필을 통해 전달할 수 있는 매개 변수를 읽을 수 있습니다. 매개 변수는 rendition.instructions 개체를 통해 작업자에 전달됩니다.

작업자 코드에서 rendition.instructions.<parameterName>에 액세스하여 읽을 수 있습니다.

여기에서는 구성 가능한 표현물의 SIZE, BRIGHTNESSCONTRAST에서 읽어볼 수 있으며, 처리 프로필을 통해 제공된 것이 없는 경우 기본값을 제공합니다. AEM에서 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
}

오류 발생

asset compute 작업자는 오류가 발생하는 상황이 발생할 수 있습니다. Adobe Asset compute SDK는 사전 정의된 오류 세트를 제공하며, 이 오류는 이러한 상황이 발생할 때 발생할 수 있습니다. 특정 오류 유형이 적용되지 않으면 GenericError을 사용하거나 특정 사용자 지정 ClientErrors을 정의할 수 있습니다.

변환 처리를 시작하기 전에 이 작업자 컨텍스트에서 모든 매개 변수가 유효하고 지원되는지 확인합니다.

  • SIZE, CONTRASTBRIGHTNESS에 대한 변환 명령 매개 변수가 유효한지 확인합니다. 그렇지 않으면 사용자 지정 오류 RenditionInstructionsError를 실행합니다.
    • ClientError을(를) 확장하는 사용자 지정 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. 작성된 항목 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" 매개 변수는 개발 도구용으로만 사용되며 의존해서는 안 됩니다.
    {
        "renditions": [
            {
                "worker": "...",
                "name": "rendition.png"
            }
        ]
    }
    
  6. 실행​을 탭하고 표현물이 생성될 때까지 기다립니다

  7. 표현물 섹션은 생성된 표현물을 미리 봅니다. 변환 미리 보기를 탭하여 전체 변환을 다운로드합니다

    기본 PNG 표현물

매개 변수로 작업자 실행

처리 프로필 구성을 통해 전달된 매개 변수는 변환 매개 변수 JSON에서 키/값 쌍으로 제공하여 Asset compute 개발 도구에서 시뮬레이션할 수 있습니다.

경고

로컬 개발 중에 AEM에서 Cloud Service 처리 프로필로 전달된 경우 다양한 데이터 유형을 사용하여 값을 전달할 수 있으므로 필요한 경우 올바른 데이터 유형을 구문 분석해야 합니다.
예를 들어 Jimp의 crop(width, height) 함수에는 해당 매개 변수가 int'여야 합니다. parseInt(rendition.instructions.size)를 int로 구문 분석하지 않으면 매개 변수가 호환되지 않는 'String' 유형이 되므로 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. 실행​을 다시 누릅니다

  3. 변환 미리 보기를 탭하여 생성된 변환을 다운로드하고 검토합니다. 기본 표현물과 비교하여 해당 차원과 대비 및 명도가 어떻게 변경되었는지 확인합니다.

    매개 변수화된 PNG 표현물

  4. 다른 이미지를 소스 파일 드롭다운에 업로드하고 다른 매개 변수로 작업자를 실행해 보십시오!

Github의 Worker index.js

최종 index.js은 Github에서 사용할 수 있습니다.

문제 해결

이 페이지에서는