AEM as a Cloud Serviceへのプログラマティックアセットのアップロード

aem-upload Node.js ライブラリを使用するクライアントアプリケーションを使用して、AEM as a Cloud Service環境にアセットをアップロードする方法について説明します。

学習内容

このチュートリアルでは、次の内容について説明します。

  • 直接バイナリアップロード アプローチを使用して、aem-upload Node.js ライブラリを使用してAEM as a Cloud Service環境(RDE、Dev、Stage、Prod)にアセットをアップロードする方法。
  • aem-asset-upload-sample アプリケーションを設定して実行し、AEM as a Cloud Service環境にアセットをアップロードする方法。
  • サンプルアプリケーションコードを確認し、実装の詳細を理解します。
  • AEM as a Cloud Service環境へのプログラマティック アセットのアップロードに関するベストプラクティスについて説明します。

直接バイナリアップロード​のアプローチについて

ダイレクトバイナリアップロード アプローチにより、ソースシステム からAEM as a Cloud Service環境のクラウドストレージ​に​ 事前署名されたURL ​を使用してファイルを直接アップロードできます。 これにより、AEMのJava プロセスを介してバイナリデータをルーティングする必要がなくなり、アップロードの高速化とサーバー負荷の軽減が実現します。

サンプルアプリケーションを実行する前に、直接バイナリアップロードフローについて説明します。

直接バイナリアップロードフローでは、バイナリデータは事前署名済みのURLを使用してクラウドストレージに直接アップロードされます。 AEM as a Cloud Serviceは、事前署名済みURLの生成や、アップロードの完了に関するAEM Asset Compute サービスへの通知など、軽量な処理を担当します。 次の論理フロー図は、直接バイナリアップロードフローを示しています。

直接バイナリアップロードフロー

aem アップロードライブラリ

aem-upload Node.js ライブラリは、直接バイナリアップロード アプローチの実装の詳細を抽象化します。 アップロードプロセスを調整するために2つのクラスを提供します。

  • FileSystemUpload - ローカルファイルシステムからファイルをアップロードする際に、ディレクトリ構造のサポートを含めて使用します
  • DirectBinaryUpload - ストリームやバッファーからのアップロードなど、バイナリ アップロード プロセスをより細かく制御するために使用します
CAUTION
Javaにaem-upload ライブラリに相当するものはありません。 ダイレクトバイナリアップロード アプローチを使用するには、クライアントアプリケーションをNode.jsで記述する必要があります。 詳しくは、Experience Manager Assets APIとオペレーション ​のページを参照してください。

サンプルアプリケーション

プログラムによるアセットのアップロードプロセスを学習するには、aem-asset-upload-sample アプリケーションを使用します。 サンプルアプリケーションでは、aem-upload ライブラリのFileSystemUploadDirectBinaryUploadの両方のクラスの使用について説明します。

前提条件

サンプルアプリケーションを実行する前に、次の前提条件を満たしていることを確認してください。

  • Rapid Development Environment (RDE)やDev EnvironmentなどのAEM as a Cloud Service オーサー環境。
  • Node.js (最新のLTS バージョン)
  • Node.jsとnpmの基本的な理解
CAUTION
AEM as a Cloud Service SDK(別名ローカルのAEM インスタンス)を使用して、プログラマティックアセットのアップロードプロセスをテストすることはできません。 Rapid Development Environment (RDE)やDev EnvironmentなどのAEM as a Cloud Service環境を使用する必要があります。

サンプルアプリケーションのダウンロード

  1. aem-asset-upload-sample アプリケーションのzip ファイルをダウンロードして抽出します。

    code language-bash
    $ unzip aem-asset-upload-sample.zip
    
  2. 抽出したフォルダーをお気に入りのコードエディターで開きます。

    code language-bash
    $ cd aem-asset-upload-sample
    $ code .
    
  3. コードエディターターミナルを使用して、依存関係をインストールします。

    code language-bash
    $ npm install
    

    ​ サンプルアプリケーション ​

サンプルアプリケーションの設定

サンプルアプリケーションを実行する前に、AEM オーサーのURL、認証方法、アセットフォルダーパスなど、必要なAEM as a Cloud Service環境の詳細で設定する必要があります。

aem-upload Node.js ライブラリでサポートされている認証方法は、複数あります。 次の表は、サポートされている​ 認証方法 ​とその目的をまとめたものです。

基本認証
ローカル開発トークン ​
​ サービス資格情報
OAuth S2S
OAuth Web アプリ ​
OAuth SPA
サポートされていますか?
目的
ローカル開発
ローカル開発
実稼動
該当なし
該当なし
該当なし

サンプルアプリケーションを設定するには、次の手順に従います。

  1. env.example ファイルを.env ファイルにコピーします。

    code language-bash
    $ cp env.example .env
    
  2. .env ファイルを開き、AEM_URL環境変数をAEM as a Cloud Service オーサーURLで更新します。

  3. 次のオプションから認証方法を選択し、対応する環境変数を更新します。

基本認証

基本認証を使用するには、AEM as a Cloud Service環境でユーザーを作成する必要があります。

  1. AEM as a Cloud Serviceにログインします。

  2. ツール > セキュリティ > ユーザー​に移動し、作成 ボタンをクリックします。

    ​ ユーザーの作成

  3. ユーザーの詳細を入力

    ​ ユーザーの詳細

  4. グループ」タブで、DAM ユーザー グループを追加します。 「保存して閉じる」ボタンをクリックします。

    DAM ユーザーグループを追加

  5. 作成したユーザーのユーザー名とパスワードを使用して、AEM_USERNAMEおよびAEM_PASSWORD環境変数を更新します。

ローカル開発トークン

ローカル開発トークンを取得するには、AEM Developer Consoleを使用する必要があります。 生成されるトークンは、JSON Web Token (JWT)タイプです。

  1. Adobe Cloud Managerにログインし、目的の​ Environment ​の詳細ページに移動します。 「」をクリックします…」 Developer Console​を選択します。

    デベロッパーコンソール

  2. AEM Developer Consoleにログインし、新しいコンソール ボタンを使用して、新しいコンソールに切り替えます。

  3. ツール セクションから、統合​を選択し、ローカルトークンを取得 ボタンをクリックします。

    ​ ローカルトークンを取得

  4. トークン値をコピーし、トークン値でAEM_BEARER_TOKEN環境変数を更新します。

ローカル開発トークンは24時間有効で、トークンを生成したユーザーに対して発行されます。

サービス資格情報

サービス資格情報を取得するには、AEM Developer Consoleを使用する必要があります。 これは、jwt-auth npm モジュールを使用してJSON Web Token (JWT) タイプのトークンを生成するために使用されます。

  1. Adobe Cloud Managerにログインし、目的の​ Environment ​の詳細ページに移動します。 「」をクリックします…」 Developer Console​を選択します。

    デベロッパーコンソール

  2. AEM Developer Consoleにログインし、新しいコンソール ボタンを使用して、新しいコンソールに切り替えます。

  3. ツール」セクションから、統合​を選択し、新しいテクニカルアカウントを作成 ボタンをクリックします。

    ​ サービス資格情報の取得

  4. 表示」オプションをクリックして、サービス資格情報JSONをコピーします。

    ​ サービス資格情報

  5. サンプルアプリケーションのルートにservice-credentials.json ファイルを作成し、サービス資格情報JSONをファイルに貼り付けます。

  6. Service-credentials.json ファイルへのパスを使用して、AEM_SERVICE_CREDENTIALS_FILE環境変数を更新します。

  7. アセットをAEM as a Cloud Service環境にアップロードするために必要な権限がサービス資格情報ユーザーに付与されていることを確認します。 詳しくは、AEM ページでのアクセスの設定を参照してください。

次に、3つの認証方法をすべて設定した完全なサンプル .env ファイルを示します。

# AEM Environment Configuration
# Copy this file to .env and fill in your AEM as a Cloud Service details

# AEM as a Cloud Service Author URL (without trailing slash)
# Example: https://author-p12345-e67890.adobeaemcloud.com
AEM_URL=https://author-p63947-e1733365.adobeaemcloud.com

# Upload Configuration
# Target folder in AEM DAM where assets will be uploaded
TARGET_FOLDER=/content/dam

# DirectBinaryUpload Remote URLs (required for DirectBinaryUpload example)
# URLs for remote files to upload in the DirectBinaryUpload example
# These demonstrate uploading from remote sources (URLs, CDNs, APIs)
REMOTE_FILE_URL_1=https://placehold.co/600x400/red/white?text=Adobe+Experience+Manager+Assets

################################################################
# Authentication - Choose one of the following methods:
################################################################

# Method 1: Service Credentials (RECOMMENDED for production)
# Download service credentials JSON from AEM Developer Console and save it locally
# Then provide the path to the file here
AEM_SERVICE_CREDENTIALS_FILE=./service-credentials.json

# Method 2: Bearer Token Authentication (for manual testing)
AEM_BEARER_TOKEN=eyJhbGciOiJSUzI1NiIsIng1dSI6Imltc19uYTEta2V5LWF0LTEuY2VyIiwia2lkIjoiaW1zX25hM....fsdf-Rgt5hm_8FHutTyNQnkj1x1SUs5OkqUfJaGBaKBKdqQ

# Method 3: Basic Authentication (for development/testing only)
AEM_USERNAME=asset-uploader-local-user
AEM_PASSWORD=asset-uploader-local-user

# Optional: Enable detailed logging
DEBUG=false

サンプルアプリケーションの実行

サンプルアプリケーションでは、サンプルアセットをAEM as a Cloud Serviceにアップロードする3つの異なる方法を紹介しています。

  1. FileSystemUpload - ディレクトリ構造をサポートし、自動フォルダー作成を行うローカル ファイルシステムからファイルをアップロードします
  2. DirectBinaryUpload - ​ リモートファイル ​をアップロードします。 ファイルバイナリは、AEM as a Cloud Service環境にアップロードする前にメモリにバッファリングされます。
  3. バッチアップロード – 自動再試行ロジックとエラー回復を使用して、ローカルファイルシステムから複数のファイルを一括でアップロードします。 バックグラウンドでは、FileSystemUpload クラスを使用して、ローカルファイルシステムからファイルをアップロードします。

アップロードするアセットはsample-assets フォルダーにあり、imgvideodoc個のサブフォルダーに、いくつかのサンプルアセットが含まれています。

  1. サンプルアプリケーションを実行するには、次のコマンドを使用します。
$ npm start
  1. 次の選択肢から目的のオプション number​を入力します。
╔════════════════════════════════════════════════════════════╗
║      AEM Asset Upload Sample Application                   ║
║      Demonstrating @adobe/aem-upload library               ║
╚════════════════════════════════════════════════════════════╝

Choose an upload method:

1. FileSystemUpload - Upload files from local filesystem with auto-folder creation
2. DirectBinaryUpload - Upload from remote URLs/streams to AEM
3. Batch Upload - Upload multiple files in batches with retry logic
4. Exit

次のタブに、各アップロードメソッドのAEM as a Cloud Service環境でのサンプルアプリケーションの実行、出力、アップロードされたアセットを示します。

FileSystemUpload
  1. FileSystemUpload オプションのサンプル アプリケーション出力:
code language-bash
...
Upload Summary:
──────────────────────────────────────────────────
Total files: 5
Successful: 5
Failed: 0
Total time: 2.67s
──────────────────────────────────────────────────
✓
All files uploaded successfully!
  1. AEM as a Cloud Service環境でFileSystemUpload オプションを使用してAssetsがアップロードされました:

    FileSystemUpload クラスを使用してAEM as a Cloud Service環境にアセットをアップロードしました

DirectBinaryUpload
  1. DirectBinaryUpload オプションのサンプル アプリケーション出力:
code language-bash
...
Upload Summary:
──────────────────────────────────────────────────
Total files: 1
Successful: 1
Total time: 561ms
──────────────────────────────────────────────────

✅ Successfully uploaded to AEM: https://author-p63947-e1733365.adobeaemcloud.com/ui#/aem/assets.html/content/dam?appId=aemshell
  → remote-file-1.png
    Source: https://placehold.co/600x400/red/white?text=Adobe+Experience+Manager+Assets
✓
All files uploaded successfully!
  1. AEM as a Cloud Service環境でDirectBinaryUpload オプションを使用してAssetsがアップロードされました:

DirectBinaryUpload クラスを使用してAEM as a Cloud Service環境にアセットをアップロードしました

バッチアップロード
  1. Batch Upload オプションのサンプル アプリケーション出力:
code language-bash
...
ℹ Found 4 item(s) to upload in batches (directories + files)
ℹ Batch size: 2 (small for demo, use 10-50 for production)

...

✓ Batch 2 completed in 2.79s

Upload Summary:
──────────────────────────────────────────────────
Total files: 5
Successful: 5
Failed: 0
Total time: 4.50s
──────────────────────────────────────────────────
✓
All files uploaded successfully!
  1. AEM as a Cloud Service環境でBatch Upload オプションを使用してAssetsがアップロードされました:

BatchUpload クラスを使用してAEM as a Cloud Service環境にアセットをアップロードしました

サンプルアプリケーションコードの確認

サンプルアプリケーションの主なエントリポイントはindex.js ファイルです。 選択をユーザーに促し、選択した例を実行するpromptUser関数が含まれています。

/**
 * Prompts user for choice and executes the selected example
 */
function promptUser() {
  rl.question(chalk.bold('Enter your choice (1-4): '), async (answer) => {
    console.log('');

    try {
      switch (answer.trim()) {
        case '1':
          console.log(chalk.bold.green('\n▶ Running FileSystemUpload Example...\n'));
          await filesystemUpload.main();
          break;

        case '2':
          console.log(chalk.bold.green('\n▶ Running DirectBinaryUpload Example...\n'));
          await directBinaryUpload.main();
          break;

        case '3':
          console.log(chalk.bold.green('\n▶ Running Batch Upload Example...\n'));
          await batchUpload.main();
          break;

        case '4':
          rl.close();
          return;

        default:
          console.log(chalk.red('\n✗ Invalid choice. Please enter 1, 2, 3, or 4.\n'));
      }

      // After example completes, ask if user wants to continue
      rl.question(chalk.bold('\nPress Enter to return to menu or Ctrl+C to exit...'), () => {
        displayMenu();
        promptUser();
      });

    } catch (error) {
      console.error(chalk.red('\n✗ Error:'), error.message);
      rl.question(chalk.bold('\nPress Enter to return to menu...'), () => {
        displayMenu();
        promptUser();
      });
    }
  });
}

完全なコードについては、サンプルアプリケーションのindex.js ファイルを参照してください。

次のタブは、各アップロードメソッドの実装の詳細を示しています。

FileSystemUpload

FileSystemUpload クラスは、ディレクトリ構造のサポートと自動フォルダー作成を使用して、ローカルファイルシステムからファイルをアップロードするために使用されます。

code language-javascript
...
// Initialize FileSystemUpload
const upload = new FileSystemUpload();

const startTime = Date.now();
const spinner = createSpinner('Preparing upload...');

// Upload options for this specific upload
// For FileSystemUpload, the url should include the target folder path
const fullUrl = `${options.url}${targetFolder}`;

const uploadOptions = new FileSystemUploadOptions()
  .withUrl(fullUrl)
  .withDeepUpload(true);  // Enable recursive upload of subdirectories

// Add HTTP options including headers (auth is already in headers from config)
uploadOptions.withHttpOptions({
  headers: {
    ...options.headers,
    'X-Upload-Source': 'FileSystemUpload-Example'
  }
});

spinner.stop();

// Attach progress event handlers to the upload instance
handleUploadProgress(upload);

// Perform the upload and wait for completion
// Upload the contents (subdirectories and files) not the parent folder
const uploadResult = await upload.upload(uploadOptions, uploadPaths);
const totalTime = Date.now() - startTime;

// Analyze results using shared function
const analysis = analyzeUploadResult(uploadResult);

// Display summary
displayUploadSummary(analysis, totalTime);
...

完全なコードについては、サンプルアプリケーションのexamples/filesystem-upload.js ファイルを参照してください。

DirectBinaryUpload

DirectBinaryUpload クラスは、リモート ファイルをAEM as a Cloud Service環境にアップロードするために使用されます。

code language-javascript
...
/**
 * Creates upload file objects for DirectBinaryUpload from remote URLs
 * @param {Array<Object>} remoteFiles - Array of objects with url, fileName, targetFolder
 * @returns {Array<Object>} Array of upload file objects
 */
async function createUploadFilesFromUrls(remoteFiles) {
  const uploadFiles = [];

  for (const remoteFile of remoteFiles) {
    logInfo(`Fetching: ${remoteFile.fileName} from ${remoteFile.url}`);
    try {
      const fileBuffer = await fetchRemoteFile(remoteFile.url);
      uploadFiles.push({
        fileName: remoteFile.fileName,
        fileSize: fileBuffer.length,
        blob: fileBuffer,  // DirectBinaryUpload uses 'blob' for buffers
        targetFolder: remoteFile.targetFolder,
        targetFile: `${remoteFile.targetFolder}/${remoteFile.fileName}`,
        sourceUrl: remoteFile.url  // Track source URL for display in summary
      });
      logSuccess(`Downloaded: ${remoteFile.fileName} (${formatBytes(fileBuffer.length)})`);
    } catch (error) {
      logError(`Failed to fetch ${remoteFile.fileName}: ${error.message}`);
    }
  }

  return uploadFiles;
}

...

    // Initialize DirectBinaryUpload
    const upload = new DirectBinaryUpload();

    // Fetch remote files and create upload objects
    const uploadFiles = await createUploadFilesFromUrls(remoteFiles);

...

    // Upload options for each file
    const uploadOptions = new DirectBinaryUploadOptions()
        .withUrl(fullUrl)
        .withUploadFiles([uploadFile]);

    // Add HTTP options (auth is already in headers from config)
    uploadOptions
        .withHttpOptions({
        headers: {
            ...options.headers,
            'X-Upload-Source': 'DirectBinaryUpload-Example'
        }
        })
        .withMaxConcurrent(5);

    // Upload individual file and wait for completion
    const uploadResult = await upload.uploadFiles(uploadOptions);

完全なコードについては、サンプルアプリケーションのexamples/direct-binary-upload.js ファイルを参照してください。

バッチアップロード

ファイルをバッチに分割し、自動再試行ロジックとエラー回復を使用してファイルをバッチにアップロードします。 バックグラウンドでは、FileSystemUpload クラスを使用して、ローカルファイルシステムからファイルをアップロードします。

code language-javascript
...
async function uploadInBatches(paths, options, targetFolder, batchSize = 2) {
  const allResults = [];
  const totalPaths = paths.length;
  const totalBatches = Math.ceil(totalPaths / batchSize);

  logInfo(`Processing ${totalPaths} item(s) in ${totalBatches} batch(es)`);

  for (let i = 0; i < totalPaths; i += batchSize) {
    const batchNumber = Math.floor(i / batchSize) + 1;
    const batch = paths.slice(i, i + batchSize);

    console.log(`\n${'='.repeat(50)}`);
    logInfo(`Batch ${batchNumber}/${totalBatches} - Uploading ${batch.length} item(s)`);
    console.log('='.repeat(50));

    const batchStartTime = Date.now();
    let retryCount = 0;
    const maxRetries = 3;
    let batchResults = null;

    // Retry logic for failed batches
    while (retryCount <= maxRetries) {
      try {
        // Create a fresh upload instance for each retry to avoid duplicate event listeners
        const upload = new FileSystemUpload();

        const fullUrl = `${options.url}${targetFolder}`;

        const uploadOptions = new FileSystemUploadOptions()
          .withUrl(fullUrl)
          .withDeepUpload(true);  // Enable recursive upload of subdirectories

        // Add HTTP options including headers (auth is already in headers from config)
        uploadOptions.withHttpOptions({
          headers: {
            ...options.headers,
            'X-Upload-Source': 'Batch-Upload-Example',
            'X-Batch-Number': batchNumber
          }
        });

        // Track progress - attach listeners to upload instance
        upload.on('foldercreated', (data) => {
          logSuccess(`Created folder: ${data.folderName} at ${data.targetFolder}`);
        });

        let currentFile = '';
        upload.on('filestart', (data) => {
          currentFile = data.fileName;
          logInfo(`Starting: ${currentFile}`);
        });

        upload.on('fileprogress', (data) => {
          const percentage = ((data.transferred / data.fileSize) * 100).toFixed(1);
          process.stdout.write(
            `\r  Progress: ${percentage}% - ${formatBytes(data.transferred)}/${formatBytes(data.fileSize)}`
          );
        });

        upload.on('fileend', (data) => {
          process.stdout.write('\n');
          logSuccess(`Completed: ${data.fileName}`);
        });

        upload.on('fileerror', (data) => {
          // Only show in DEBUG mode (may be retries)
          if (process.env.DEBUG === 'true') {
            process.stdout.write('\n');
            const errorMsg = data.error?.message || data.message || 'Unknown error';
            logWarning(`Error (may retry): ${data.fileName} - ${errorMsg}`);
          }
        });

        // Perform upload and wait for batch completion
        const uploadResult = await upload.upload(uploadOptions, batch);

        const batchEndTime = Date.now();
        const batchTime = batchEndTime - batchStartTime;

        logSuccess(`Batch ${batchNumber} completed in ${formatTime(batchTime)}`);

        // Extract detailed results from the upload result
        batchResults = uploadResult.detailedResult || [];
        break; // Success, exit retry loop

      } catch (error) {
        retryCount++;
        if (retryCount <= maxRetries) {
          logWarning(`Batch ${batchNumber} failed. Retry ${retryCount}/${maxRetries}...`);
          await new Promise(resolve => setTimeout(resolve, 2000 * retryCount)); // Exponential backoff
        } else {
          logError(`Batch ${batchNumber} failed after ${maxRetries} retries: ${error.message}`);
          // Mark all files in batch as failed
          batchResults = batch.map(file => ({
            fileName: path.basename(file),
            error: error,
            success: false
          }));
        }
      }
    }

    if (batchResults) {
      allResults.push(...batchResults);
    }
  }

  return allResults;
}

完全なコードについては、サンプルアプリケーションのexamples/batch-upload.js ファイルを参照してください。

また、サンプルアプリケーションのREADME.md ファイルには、サンプルアプリケーションの詳細ドキュメントが含まれています。

ベストプラクティス

  1. 適切な認証方法を選択:
    本番環境のサービス資格情報、ローカル開発トークン、およびBasic認証は、開発/テストにのみ使用します。 アセットをAEM as a Cloud Service環境にアップロードするために必要な権限がサービス資格情報ユーザーに付与されていることを確認します。

  2. 適切なアップロード方法を選択:
    自動フォルダー作成機能を備えたローカルファイルにはFileSystemUpload、きめ細かい制御を備えたストリーム/バッファー/リモート URLにはDirectBinaryUpload、再試行ロジックを必要とする1000以上のファイルを備えた実稼動環境用のバッチアップロードパターンを使用できます。

  3. 構造DirectBinaryUpload ファイル オブジェクトを正しく作成
    必須フィールド { fileName, fileSize, blob: buffer, targetFolder }でblob プロパティ(バッファではない)を使用し、DirectBinaryUploadはフォルダーを自動作成しないことを覚えておいてください。

  4. 参照としてのサンプル アプリケーション:
    サンプルアプリケーションは、プログラマティックアセットアップロードプロセスの実装の詳細に関する優れたリファレンスです。 実装の出発点として使用できます。

recommendation-more-help
experience-manager-learn-help-assets