InputStream に格納されているストリームデータのサイズを取得する

公開日: : 最終更新日:2014/05/05 Java ,

Jersey を使った Web アプリを作成中なんですが、 enctype=”multipart/form-data” で POST されるアップロードファイルは InputStream でサーバに送信されてきます。

この InputStream についてちょっと躓いた部分があったので、その内容をシェアしたいと思います。

icatch-upload_1229138273_mini

photo credit: johntrainor via photopin cc

目次

1. Jersey を使ったアプリについて

躓いた内容を説明する前に軽く作っているアプリについて触れておくと、フレームワークは上述した Jersey だけを使っています。
アプリの規模が小さいのでコントローラ部分を管理してくれるものだけで充分なのです。

Jersey は 2.x 系ではなく 1.x 系です。
理由は、過去に 1.x 系でアプリを作成した方が会社にいるってことが大きいです。
超短納期な案件なので 2.x にチャレンジしていろいろハマりたくないって逃げの精神で選択しました。

Jersey で multipart を扱うライブラリは、次の POM のものを使っています。

<dependency>
    <groupId>com.sun.jersey.contribs</groupId>
    <artifactId>jersey-multipart</artifactId>
    <version>1.8</version>
</dependency>

あと、Java のバージョンはお客さん都合で 1.6 です。

2. 躓いた部分とは

さて、躓いた話ですが、冒頭でも書いたようにアップロードファイルは InputStream で送信されてきます。

これは仕様なんだと思いますが、ファイルを選択していなくても submit すると、必ず InputStream が送信されるようになっています。
なので、「ファイルを選択しなかった」の判定が inputStream == null で出来ないのです。

となると、「InputStream に格納されているストリームデータのサイズで判定するか」と思って InputStream (Java Platform SE 6) を確認しました。

ですが、getSize みたいなメソッドは定義されておらず、InputStream#available が使えそうかなと思いましたが、、、

InputStream クラスの available メソッドは常に 0 を返します。

InputStream (Java Platform SE 6)

って書いてありました。

ダメじゃんって思いながらも検索してみると、InputStream#available はストリームデータのサイズを返すものではないから使っちゃダメとという書き込みを Stack Overflow に見つけたので諦めました。

3. InputStream に格納されているストリームデータのサイズを取得する方法

では、どうやってストリームデータのサイズを知るのかというと、一つの案としてストリームデータを ByteArrayOutputStream に書き込んでしまう、という方法があるそうです。
上記の Stack Overflow にあった書き込みを引用します。

Of course, you then won’t be able to read the data itself… if you want to do that, you’ll need to keep the data as you read it, e.g. by copying it to a ByteArrayOutputStream.

Java InputStream size – Stack Overflow

なるほど。
ByteArrayOutputStream#size が定義されているので、これならストリームデータのサイズが分かります。

ということで、この方法で InputStream に格納されているストリームデータのサイズを取得しました。

以下に、その処理で書いたコードを載せておきます。
良ければ、組み合わせてお使いください。

3-1. InputStream に格納されているストリームデータのサイズを取得するコードスニペット

ByteArrayOutputStream xxx = new ByteArrayOutputStream();
byte[] buf = new byte[32768]; // この値は適当に変更してください
int size = 0;
    
while((size = inputStream.read(buf, 0, buf.length)) != -1) {
    xxx.write(buf, 0, size);
}           

この処理で InputStream に格納されているストリームデータが ByteArrayOutputStream に書き込まれます。
ByteArrayOutputStream#size を使ってストリームデータのサイズが分かるようになります。

3-2. ByteArrayOutputStream に格納されているデータをファイルに書き出すコードスニペット

せっかくなので、 ByteArrayOutputStream が保持しているストリームデータをファイルに書き出す処理のコードスニペットも載せておきます。

/**
 * ByteArrayOutputStream が保持しているデータをファイルに書き出す。
 * 
 * @param byteArrayOutputStream 書き出すデータを保持している ByteArrayOutputStream
 * @param file 書き出し先の File
 * @throws FileNotFoundException file が見つからない場合
 * @throws IOException 書き出し処理に失敗した場合
 */
private void byteArrayOutputStreamToFile(
        ByteArrayOutputStream byteArrayOutputStream, File file)
        throws FileNotFoundException, IOException {
    OutputStream output = new FileOutputStream(file);
    try {
        IOUtils.write((byteArrayOutputStream.toByteArray()), output);
    } finally {
        if(output != null) {
            output.close();
        }
    }
}

4. まとめ

以上、InputStream に格納されているストリームデータのサイズを取得する方法でした。

今までこの手の処理は、 Apache Commons の FileUpload とかを使って処理していたので気が付かなかったのですが、 InputStream に格納されているストリームデータのサイズって取り出せなかったんですね。
普通に getSize 的なメソッドから取れるものだと思っていました。

こうなった背景があるんでしょうけど、ちょっと不便ですね。

5. その他の Java に関する記事

その他の Java に関する記事は次の通りです。
気になる記事があったらぜひチェックしてみてください!

Googleアドセンス用(PC)

  • このエントリーをはてなブックマークに追加
  • follow us in feedly

関連記事

GradleLogoReg

Gradle で Java アプリのプロジェクトを作成して Eclipse にインポートするまでの手順

Mac に SDKMAN! を使って Gradle をインストールしたので、実際にプロジェクトを作成

記事を読む

no image

ファイルの拡張子が JPG であるかを問い合わせるスニペット

特に目新しいこともないが、同じようなことをする機会があるかもしれないのでメモ。Gist にも登録済み

記事を読む

no image

Java から VBScript を使って Excel を印刷する

背景 Java のデスクトップアプリを作成していたときの要件に「Excel で作成された帳票を印刷

記事を読む

no image

ファイルに関するユーティリティ

ファイルに関するスニペットをいくつか書いた。いつかコピペする日が来ると思うのでメモしておく。 作成

記事を読む

icatch-zip_524574337_mini-thumbnail

Java でファイルが1つだけ圧縮された zip を解凍するサンプル

ファイルが1つだけ圧縮された zip を解凍するサンプルです。 単一ファイルが圧縮された zip

記事を読む

no image

ディレクトリに関するユーティリティ

ディレクトリに関するスニペットをいくつか書いた。いつかコピペする日が来ると思うのでメモしておく。

記事を読む

icatch-10139609144_5bc33706f9_z-resized

保存ダイアログを表示させてファイルをダウンロードさせる方法

例えば、ブラウザに表示している画像は右クリックを使ってダウンロードできますが、左クリックから保存ダイ

記事を読む

icatch-3361b5e1-resized

Optional型の変数にget()とifPresent()は使わない

Java8 から導入された Optional を勉強するため、過去にプライベートで書いたコードに O

記事を読む

no image

Stream#map()とStream#collect()を使ってListを生成する

Java8 から導入された Stream API も少しずつ慣れてきて、「そういえばこういうパターン

記事を読む

no image

Google Guava の Function を使った文字列加工の FluentIterable サンプル

お客様の都合上 Java7 で開発している案件があるのだが、Java8 の Function とか

記事を読む

Googleアドセンス用(PC)

Message

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です


七 × = 42

次のHTML タグと属性が使えます: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

Googleアドセンス用(PC)

icatch-jersey_multi_pathparams
Jerseyの@PathParamはスラッシュの間に複数指定できる

http://hoge-api/user/{id}.{format}

icatch-vagrant_box_customize
VagrantのBoxファイルをカスタマイズして独自のBoxファイルを作成する

配布されている Vagrant の Box ファイルを使って検証環境を

icatch-2015-006-1
バリデーションチェックにJava8のOptionalを使ってスマートに書く(自分比)

Web アプリのバリデーションチェックにアノテーションを使うことが増え

icatch-2015-005-1
ユニットテストの偏りを防ぐ命名規則の付け方

ユニットテスト名に以下の命名規則を付けるようにして二ヶ月ぐらい経った。

icatch-2015-004-1
Vagrantで起動したCentOS上のOctopressをホストOSから確認する設定

タイトルの通りだが、Vagrant を使って起動した CentOS に

→もっと見る

PAGE TOP ↑