1. ヘルプ
  2. 実装(全般)
  3. ユースケース別の実装方法

WRITE APIでリッチエディタにコンテンツを登録する際、imgタグ内で外部URLの画像を指定する方法は?

→ 外部URLの画像をimgタグ内で直接指定することはできません。マネジメントAPI等を利用し、microCMSにアップロードした画像のURLを利用する方法をご案内します。

WRITE APIでリッチエディタにコンテンツを登録する際、imgタグのsrc属性に指定できる値はmicroCMSのドメインのURL(https://images.microcms-assets.io/~)のみとなります。

ですので、直接外部のURLの画像を指定することはできません。

画像 - リッチエディタのWRITE API|microCMSドキュメント

代替手段

マネジメントAPIを用いて、外部URLの画像を一度microCMSにアップロードし、取得したURLを用いて置換するなどの方法で対応することは可能です。

一例として、特定のHTMLファイル(imgタグで外部のURLのメディアを読み込んでいる)を読み込み、その内容をmicroCMSの任意のAPIで用意しているリッチエディタにアップロードする方法を考えます。

具体的な処理の流れは以下のとおりです。

  1. 特定のHTMLファイルを読み込む(今回は同階層にある sample.html を対象にしています)
  2. 1で読み込んだHTMLファイル内で使われている画像を洗い出す
  3. 2で洗い出した画像のURLを元に、マネジメントAPIの画像アップロードAPIでアップロードする
  4. 3のレスポンスとして得られるmicroCMSドメインのURLを用いて、1のHTMLファイルの画像タグの内容を書き換える
  5. コンテンツAPIでコンテンツをアップロードする

なお、手順1で読み込むHTMLファイル(body要素以下)は以下のようなものを想定しています。このHTMLにおける、article要素の中身をリッチエディタのコンテンツに登録するイメージです。

<body>
  <article>
    <p>
      <img src="https://placehold.jp/400x400.png" alt="サンプル画像1">
    </p>
    <p>
      <img src="https://placehold.jp/500x500.png" alt="サンプル画像2">
    </p>
    <p>
      <img src="https://placehold.jp/600x600.png" alt="サンプル画像3">
    </p>
  </article>
</body>

 

上記の前提を踏まえ、処理を実装したコードの例(Node.js)を示します。

import fs from "fs/promises";
import * as cheerio from 'cheerio';
import { createClient, createManagementClient } from 'microcms-js-sdk';

// クライアント(コンテンツAPI)
const contentClient = createClient({
  serviceDomain: 'some-domain',
  apiKey: 'some-api-key',
});

// クライアント(マネジメントAPI)
const managementClient = createManagementClient({
  serviceDomain: 'some-domain',
  apiKey: 'some-api-key',
});

// 画像をアップロードし、新旧それぞれのURLを返却する関数
const uploadImage = async (imgSrc) => {
  try {
    const { url } = await managementClient.uploadMedia({ data: imgSrc });
    return { oldUrl: imgSrc, newUrl: url };
  } catch (err) {
    console.error(`Upload failed for ${imgSrc}:`, err);
  }
};

// ディレイを実行する関数
const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));

// メイン処理
const main = async () => {
  try {
    // 手順1: HTMLファイルを読み込む
    const data = await fs.readFile('sample.html', 'utf8');
    const $ = cheerio.load(data);

    // 手順2: HTMLファイルのbodyタグ内で使われている画像を洗い出す
    const imgTags = $('body img');

    // 手順3: imgタグのURLをマネジメントAPIでアップロードする
    //  マネジメントAPIのレートリミットに抵触しないよう、直列でリクエストし、リクエスト間では1秒ディレイさせる
    const uploadResults = [];
    for (let i = 0; i < imgTags.length; i++) {
      const imgSrc = $(imgTags[i]).attr('src');
      if (imgSrc) {
        const result = await uploadImage(imgSrc);
        if (result) uploadResults.push(result);

        await delay(1000);
      }
    }

    // 手順4: 元のURLからmicroCMSのドメインのURLに置換
    uploadResults.forEach(({ oldUrl, newUrl }) => {
      $(`img[src="${oldUrl}"]`).attr('src', newUrl);
    });

   // articleタグの中身を抽出(WRITE APIに対応しているタグのみを送信するため)
    const updatedHtml = $('body > article').html().replace(/"/g, "'");

    // 手順5: コンテンツAPIでアップロードする
    const response = await contentClient.create({
      endpoint: 'some-endpoint',
      content: { body: updatedHtml }, // "body"フィールドはリッチエディタ
    });
    console.log(`Content created with ID: ${response.id}`);
  } catch (err) {
    console.error('Error:', err);
  }
};

main();

なお上記のコードは、例としてシンプルにするために、以下のような前提で記述しています。

  • 同階層にある、単一のhtmlファイルに対する処理としています
  • htmlファイル内のimgタグでは絶対パスのURLが指定されていることを想定しています。相対パスが指定される可能性がある場合は、ドメイン部分を補完するなどを考慮して実装してください
  • 画像の重複は考慮していません
    • HTML内で同じ画像が使われている場合も、別の画像として扱われます