Java

ImageIOを使ってbyte配列の画像データをoutputstreamに書き込む

はじめに

ImageIOを使ってbyte配列をoutputStreamに入れようとしたら色々はまったので、メモとして記事にします。

とりあえずなんとかなったソース

    public void writeOutputStream(byte[] byteArray, OutputStream outputStream, String fileFormat) throws IOException {
        ByteArrayInputStream imageInput = new ByteArrayInputStream(byteArray);
        BufferedImage buffer = ImageIO.read(imageInput);

        ImageWriter writer = null;
        Iterator<ImageWriter> iter = ImageIO.getImageWritersByFormatName(fileFormat);
        if (iter.hasNext()) {
            writer = iter.next();
        }

        ImageWriteParam param = writer.getDefaultWriteParam();
        if (param.canWriteCompressed() && fileFormat.equals("jpg")) {
            param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
            param.setCompressionQuality(0.5f);
        }

        ImageOutputStream ios = ImageIO.createImageOutputStream(outputStream);
        writer.setOutput(ios);
        IIOImage iioImage = new IIOImage(buffer, null, null);
        writer.write(null, iioImage, param);
    }

ソースの説明

説明はそんなにいらない感じだと思いますが、ひっかかりそうな点を説明していきます。

引数の前提

  • byteArrayには画像データのbyte配列が入っている
  • fileFormatには"jpg"や"png"、"gif"なんかのファイル形式が入っている

ImageWriteParamの話

今回、画像の圧縮をするという任務があったため、ImageWriteParamを使う必要がありました。
今回最大にはまったのはこの部分で、圧縮する条件が厳しくJPEG or GIFのみが可能でPNGはExceptionが発生してしまいます。
無理やりJPEGのwriterを使ってPNGの圧縮をすることも可能ですが、残念ながら使い物にはなりませんでした。

なので、今回はparam.canWriteCompressed() && fileFormat.equals("jpg")の判定を入れることにしました。
canWriteCompressedはImageWriteParamの種類によって変わり、上に書いたようにgetImageWritersByFormatNameから取り出していくとJPEGとGIFはtrue、PNGはfalseと返ってきます。
さらにJPG判定を追加していますが(本当は"jpeg"も必要)、これはGIF画像の場合setCompressionTypeも指定せねばExceptionが発生するようですが、何を指定すればいいかよくわからなかったので入れました。

JavaDoc
https://docs.oracle.com/javase/jp/7/api/javax/imageio/ImageWriteParam.html

書き込みの話

書き込みにはImageWriteを使って書き込みますが、実際に書き込むObjectをsetOutputする際には特定の形式に変換しなければなりません。
ImageIOクラスにcreateメソッドがあるのでそれを使ってImageOutputStreamに変換すればよいのですが、具体的にどのクラスであれば書き込み可能であるかは調べてません。
とりあえずはこれで変換しておけば問題ないのですが、getImageWritersByFormatNameから取り出したwriterの中にImageOutputStreamが許容されてない場合があると面倒です。

PNG、JPEG、GIFで試した限りは全部大丈夫だったので、普段使いには問題ないと思います。
独自にImageWriteを作りたい場合は、大丈夫かどうかをきちんと考えないと痛い目にあう可能性があるので注意が必要です。

圧縮したりの最適化処理をせず直接OutputStreamに書き込みたい場合はImageIO.writeでさくっと出来るので許容されているOutputStreamに変換という手順は必要なく書き込むことが出来ます。