imgタグのsrcに、外部リソースのリンクや、インラインイメージを使う必要があり、
Wicketでどう書くのかを調べてみました。

org.apache.wicket.markup.html.image パッケージ配下のクラス

名称 説明
ContextImage.java コンテキストルートからの相対パスで、画像パスを与えて動作するコンポーネント
ContextPathGenerator.java ContextImage.javaで使用しているfileパス生成クラス
ExternalImage.java 外部サイトの画像パスを与えて動作するコンポーネント
ExternalSource.java sourceタグ向けのコンポーネント
Image.java 画像パスとしてResourceReferenceを与えて、動作するコンポーネント
InlineImage.java 画像をインライン表示するコンポーネント
NonCachingImage.java Image.javaの継承クラス画像パスにパラメータを付与してキャッシュされないようにする
Picture.java Image.javaの継承クラス Pictureタグ向け
Source.java
package.html パッケージHTML

imgタグで外部リソースと、インラインイメージを使うので、
ExternalImage.java と、 InlineImage.java を使えそうです。


ExternalImage.java を使う

使用前のコード

そもそも、ExternalImageの存在を知らず、以下のようなコードで、
AttributeModifier.replace("src", sourceUrl)を使って、
画像のリンクを書き換えていました。
※これはこれで、動作は問題ありませんでした。

  • HTML抜粋
    <div class="row voffset10">
        <div class="col-md-3 image-box">
            <div class="overlay-container">
                <img alt="Artist image" class="fit" style="">
                <a class="overlay">
                    <i class="fa fa-search-plus"></i>
                    <span>Artist Name</span>
                </a>
            </div>
        </div>
    </div>
  • JAVAコードの抜粋
        // Get Festival Id
        List objects = getObjects();
        IDataProvider dataProvider = new ListDataProvider(objects);

        GridView gridView = new GridView("artistContainers", dataProvider) {

            private static final long serialVersionUID = 8042417654748558983L;

            @Override
            protected void populateEmptyItem(Item item) {
                item.setVisible(false);
            }

            @Override
            protected void populateItem(Item item) {
                DataObject itemObject = item.getObject();
                //do something...
                //画像パスの取得
                Document doc = coll.findOneCCImageByEchonestId(itemObject.getId());
                String sourceUrl = doc.getString("url");
                // new WebComponent
                WebComponent webComponent = new WebComponent("artistImage");
                webComponent.add(AttributeModifier.replace("src", sourceUrl));
                item.add(webComponent);
            }
        };
        gridView.setColumns(4);

使用後のコード

HTMLに変更はありません。JAVA側の記述を以下の通り変更しました。
ExternalImage image = new ExternalImage("artistImage", sourceUrl); で、
sourceUrl に画像パスを設定することができます。

        // Get Festival Id
        List objects = getObjects();
        IDataProvider dataProvider = new ListDataProvider(objects);

        GridView gridView = new GridView("artistContainers", dataProvider) {

            private static final long serialVersionUID = 8042417654748558983L;

            @Override
            protected void populateEmptyItem(Item item) {
                item.setVisible(false);
            }

            @Override
            protected void populateItem(Item item) {
                DataObject itemObject = item.getObject();
                //do something...
                //画像パスの取得
                Document doc = coll.findOneCCImageByEchonestId(itemObject.getId());
                String sourceUrl = doc.getString("url");
                // new ExternalImage
                ExternalImage image = new ExternalImage("artistImage", sourceUrl);
                item.add(image);
            }
        };
        gridView.setColumns(4);

InlineImage.javaを使う

使用前のコード

HTML記述は同じです。 inlineイメージの場合も、
AttributeModifier.replace("src", sourceUrl)を使って、
画像のリンクを記述できます。

  • HTML抜粋
    <div class="row voffset10">
        <div class="col-md-3 image-box">
            <div class="overlay-container">
                <img alt="Artist image" class="fit" style="">
                <a class="overlay">
                    <i class="fa fa-search-plus"></i>
                    <span>Artist Name</span>
                </a>
            </div>
        </div>
    </div>
  • JAVA記述

データURIスキーム文字列を作成し、
AttributeModifier.replace("src", sb.toString()) で設定します。

        @Override
        protected void populateItem(Item item) {
            DataObject itemObject = item.getObject();
            //do something...
            //画像パスの取得
            Document doc = coll.findOneByMbid(itemObject.getMbid());
            Binary binary = doc.get(Keys.IMAGE_BINARY, Binary.class);
            // バイト配列→BASE64へ変換する
            Base64.Encoder encoder = Base64.getEncoder();
            byte[] encoded = encoder.encode(binary.getData());
            String base64Image = new String(encoded);
            WebComponent webComponent = new WebComponent("artistImage");
            // データURIスキームとして文字列を作成
            StrBuilder sb = new StrBuilder();
            sb.append("data:data:image/png;base64,");
            sb.append(base64Image);
            webComponent.add(AttributeModifier.replace("src", sb.toString()));
            item.add(webComponent);
        }

使用後のコード

InlineImage.javaは、コンストラクタ引数で、
PackageResourceReferenceを求める実装になっており、
byte配列から、PackageResourceReferenceに変換する術が分からなかったので、
InlineImage.javaを参考に、同名のbyte配列をInlineイメージにするコンポーネントを作成してみました。

  • InlineImage.java
package xyz.monotalk.festivals4partypeople.web.wicket.markup.html.image;

import com.google.common.base.Strings;
import org.apache.wicket.markup.ComponentTag;
import org.apache.wicket.markup.html.WebComponent;
import org.apache.wicket.util.lang.Args;

import java.util.Base64;

/**
 * InlineImage
 */
public class InlineImage extends WebComponent {

    byte[] binaryImage;
    String contentType;

    /**
     * Creates an inline image
     *
     * @param id
     * @param binaryImage
     */
    public InlineImage(String id, byte[] binaryImage) {
        this(id, null, binaryImage);
    }

    /**
     * Creates an inline image
     *
     * @param id
     * @param contentType
     * @param binaryImage
     */
    public InlineImage(String id, String contentType, byte[] binaryImage) {
        super(id);
        if (Strings.isNullOrEmpty(contentType)) {
            this.contentType = "image/octet-stream";
        } else {
            this.contentType = contentType;
        }
        Args.notNull(binaryImage, "binaryImage");
        this.binaryImage = binaryImage;
    }

    /**
     * Renders the complete image tag with the base64 encoded content.
     */
    @Override
    protected void onComponentTag(ComponentTag tag) {
        super.onComponentTag(tag);
        checkComponentTag(tag, "img");
        tag.put("src", createBase64EncodedImage(contentType, binaryImage));
    }

    /**
     * createBase64EncodedImage
     *
     * @param contentType
     * @param binaryImage
     * @return
     */
    private CharSequence createBase64EncodedImage(String contentType, byte[] binaryImage) {
        String base64EncodedImage = Base64.getEncoder().encodeToString(binaryImage);
        return "data:" + contentType + ";base64," + base64EncodedImage;
    }
}
  • JAVA記述
    @Override
    protected void populateItem(Item item) {
        DataObject itemObject = item.getObject();
        //do something...
        //画像パスの取得
        Document doc = coll.findOneByMbid(itemObject.getMbid());
        Binary binary = doc.get(Keys.IMAGE_BINARY, Binary.class);
        InlineImage inlineImage = new InlineImage("artistImage", "image/png", binary.getData());
        item.add(inlineImage);;
    }

描画されるHTMLは、以下のようになります。

    <div class="col-md-3 image-box">
        <div class="overlay-container">
            <img alt="Artist image" class="fit" src="data:image/png;base64,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" style="">
        </div>
    </div>

その他 Image.java を使う

Image.java を使用すると、以下のように記述できます。
ByteArrayResource で設定した画像には、URLが割り当てられ、その画像に対するリンクが、
設定されます。
※なので、Inline画像にはなりません。

    @Override
    protected void populateItem(Item item) {
        DataObject itemObject = item.getObject();
        //do something...
        //画像パスの取得
        Document doc = coll.findOneByMbid(itemObject.getMbid());
        Binary binary = doc.get(Keys.IMAGE_BINARY, Binary.class);
        //Image Classを使用して、Binaryデータを設定する
        Image image = new Image("artistImage", new ByteArrayResource("image/png", binary.getData()));
        item.add(image);
    }

画像関連のクラスで画像を設定すると、何が嬉しいか?

WebComponent、AttributeModifier#replace() の利用でも、
img タグのscr属性の設定は可能ですが、
Image関連のクラスを利用することで、
設定対象のタグ妥当性チェックがかかるところが嬉しいことなのかと思います。
実装時の記述ミスに画面レンダリングの前に気がつけるのかと。
あとは、実装上1行少なくて済む?

以上です。


参考サイト

コメント