ひでっぷの技術メモ

はてなダイアリーから移行しました

DBにファイルを保存 −BLOB型−

今やっている仕事で客先から「ログファイルをそのままDBに保存したい。」という話が出ました。
最初はログファイルを特定フォルダにコピーし、ファイル名だけをDBに保存しようと思っていたがそれじゃだめとのこと。
どうするかなーと考えていたらお客さんからBLOB型というものがあるらしいと教えてもらいました。

BLOBとはBinary Large OBjectの略だそうで、
画像や音声などのバイナリデータを保存できる型。
oracleの場合最大の保存できるデータ量は4GBでまったく問題ないと思います。(逆にこれだけ大きいとファイルに展開するときにJavaのメモリが足らなくなる・・・)

最初はBFILE型を使用しようと思っていましたが、BFILE型の場合
・ファイル自体をDBに保存するのではなくファイルのポインタが作成される。
・ファイル自体はDBの特定のフォルダに別途保存する必要がある。
・DB以外のファイル操作でデータが消えてもしらねー。

ということで当初考えていたファイル名だけデータベースに保存するのと変わらないということでやめました。

BLOB型、便利そうだとは思っても実際に使ってみないことには使えるかどうかがわかりません。ので、実際にサンプルプログラムを作成しました。

まず保存するデータベースのテーブル構造ですが


FILENAME VARCHAR2(100), //ファイル名
DATA BLOB, //バイナリデータ
FILESIZE NUMBER //ファイルサイズ

としました。
ファイル名をキーにし、DBに保存したのち別フォルダに書き出す。
という単純なサンプルプログラムです。

DBへの接続部分などは省きます、いきなりデータを保存するところから(笑)


public void insert(File file)
  throws SQLException,IOException{
StringBuffer sb = new StringBuffer("INSERT INTO ");
sb.append(tableName);
sb.append(" VALUES(?,?,?)");
PreparedStatement ps =
    getPreparedStatement(sb.toString());
ps.setString(1,file.getName());
ps.setBinaryStream(2,new FileInputStream(file),
        file.length());

ps.setInt(3,file.length());

ps.executeUpdate();
ps.close();
}

挿入メソッドですが、引数をFileクラスにしました。
ポイントは赤字のところです。
BLOB型はバイナリデータを保存できる型なのでBinaryStreamにセットします。
setBinaryStreamメソッドの引数はInputStreamです。
ファイルの場合はInputStreamを継承したFileInputStreamを使用すればOK。
これだけでファイルのDB保存完了です。ちょー簡単♪


次はDBに保存したファイルを取り出す場合


public void select(File file)
  throws SQLException,IOException{
StringBuffer sb = new StringBuffer("SELECT * FROM ");
sb.append(tableName);
sb.append(" WHERE FILENAME = ?");
PreparedStatement ps =
    getPreparedStatement(sb.toString());
ps.setString(1,file.getName());
ResultSet rs = ps.executeQuery();

whiel(rs.next()){
FileOutputStream fs = new FileOutputStream(file);
fs.write(rs.getBlob(2).getBytes(1,rs.getInt(1)));
}
rs.close();
ps.close();
}


今度は取り出しにBlobクラスを使用しました。
FileOutputSrteamのwrite(書き出し)メソッドの引数はbyte配列(バイナリデータ)です。
Blobクラスからbyte配列を取り出して書き出せば終わり!
画面で表記したい場合などはここから展開をする必要がありますね。
今回はサンプルプログラムなのでとりあえずここまで。

最大で700KBくらいのファイルの読み書きをしてみましたが、全然問題ない早さです。
今度はもうちょっと大きなファイルの読み書きをしてみようかな。
サンプルメソッドですが、赤字の部分以外はあまりあてにしないでください(笑)
うちの会社の独自クラスのメソッドなんかも入ってますんで・・・。