エンタープライズ:特集 2003/09/24 10:58:00 更新

[JAVA Developer特別企画]2003年11月号
MySQLにファイルを格納する

MySQLは多くのフィールド(データ)型をサポートしており、文字や数値、日付や時間のほか、バイナリデータまで格納できます。バイナリデータを格納するのは、「BLOB(ブロブ)型」。今回は、このBLOB型を使って、デジカメ写真を格納する方法を紹介します。
JAVA Developer 2003年11月号より転載

MySQLのBLOB型は4つ
 MySQLは、TINYBLOB、BLOB、MEDIUMBLOB、LONGBLOBという4種類の可変長バイナリデータを格納する型をサポートしています(表1)。それぞれの違いは、格納できるデータの最大長です。今回の目的はデジカメ写真ですので、1M〜2MB前後のデータを格納できるMEDIUMBLOB型を使います。

最大サイズ(バイト)
TINYBLOB 255(28−1)
BLOB 65,535(216−1)
MEDIUMBLOB 16,777,215(224−1)
LONGBLOB 4,294,967,295(232−1)

アップロードはJakartaCommons FileUploadを利用
 まずはクライアントPCからサーバーへのファイルアップロード機能を実装していきます。
 はじめは、ファイルアップロード用のフォームです。ここでのポイントは、methodに必ず「post」を指定し、「enctype="multipart/form-data"」を記述することです。また、アップロードするファイル名は、「<input type="file" name="file1">」のように、typeで「file」を指定します。

リスト1 アップロードフォーム(抜粋)

<form name="upload" method="post"
     enctype="multipart/form-data">
ファイル1:<input type="file" name="file1"><br>
ファイル2:<input type="file" name="file2"><br>
ファイル3:<input type="file" name="file3"><br>
<input type="submit" value="アップロード">
<input type="reset" value="リセット"><br>
</form>

 このフォームからデータを受け取って処理する部分には、JakartaCommons FileUpload 1.0(commons-fileupload-1.0.jar)を利用します。FileUploadのDiskFileUploadクラスでは、アップロードできる最大サイズ、メモリー内に保持するサイズなどを指定したあと、requestオブジェクトをパースし、ListにアップロードするFileItemオブジェクトを格納します(リスト2-(1))。Iteratorを使ってFileItemオブジェクトを取り出し、ImageFileオブジェクトを生成して、アップロードデータを格納していきます(リスト3)。

リスト2 ファイルのアップロードを実装(抜粋)

DiskFileUpload uploader =
   new DiskFileUpload();
uploader.setSizeMax(UPLOAD_LIMIT_SIZE);
uploader.setSizeThreshold(IN_MEMORY_SIZE);

List items =
   uploader.parseRequest(request); //-(1)
Iterator iter = items.iterator();
while (iter.hasNext()) {
   ImageFile image = new ImageFile();
   if (image.set((FileItem)iter.next())) {
       iData.add(image);
   }
}

リスト3 ImageFileクラス

public class ImageFile {
   public String fileName;
   public String contentType;
   public int fileSize;
   public InputStream inputStream;

   public boolean set(FileItem item)
           throws IOException {
       fileSize = (int)item.getSize();
       if (fileSize > 0) {
           fileName = item.getName();
           contentType =      
               item.getContentType();
           inputStream =
               item.getInputStream();
           return (fileName != null &&
                   contentType != null);
       }
       return false;
   }
}

 次に、ImageFileオブジェクトのデータをデータベースへ登録します。ここでは、BLOB型へのデータ登録にPreparedStatementを使います(リスト4-(2))。PreparedStatementには、データ型別にsetXXXメソッドが用意されており、その中にBLOB型に対応するsetBinaryStreamメソッドがあります(リスト4-(3))。このほかBLOB型に対応するメソッドとしては、setBlobやsetBytesメソッドがあります。

リスト4 PreparedStatementを使って登録(抜粋)

Connection con = ds.getConnection();
PreparedStatement stmt =  
   con.prepareStatement(
   "INSERT INTO "+
   "image(name,type,size,rawdata,entry) "+
   "VALUES(?, ?, ?, ?, ?)"); //-(2)

stmt.setString(1, image.fileName);
stmt.setString(2, image.contentType);
stmt.setInt(3, image.size);
stmt.setBinaryStream(4,
   image.inputStream, image.size); //-(3)
java.util.Date now = new java.util.Date();
stmt.setTimestamp(5,
   new Timestamp(now.getTime()));

stmt.executeUpdate();

stmt.close();
con.close();


※imageはImageFileクラスのオブジェクト

 ここまでで、アップロードからデータベースへ登録するまでのコーディングは終了です。テスト用にいくつかファイルをアップロードし、データベースに登録されていることを確認します。

「PacketTooBigException」で
ファイルが格納できない

図1
図1 例外発生!

 さらにファイルをアップロードしていくと、比較的大きめのファイルの場合に例外が発生して、うまく格納できないという症状に遭遇しました(図1)。例外はJDBCが投げているものです。例外名を手がかりにFAQを探してみると、それらしき記述を発見。解決策は、my.iniの[mysqld]セクションに、次のオプションを記述するというものです。


max_allowed_packet=4M

 このオプションは、パケットの最大サイズを指定するものです。上記の場合はパケットサイズを4MBに指定しています。4MBは、普段使っているデジカメのファイルサイズより多少余裕を持たせた値です。あまり大きすぎても無駄にメモリーを使うだけですから、登録したいファイルサイズを考慮した値を指定しましょう。
 MySQLを再起動し、先ほどうまく格納できなかったファイルを再度アップロード。今度は問題なく格納できました。これで、ファイルのアップロードからデータベースへの格納までは大丈夫。アップロードしたイメージの表示や検索などの機能は、これからじっくり盛り込んでいこうと思います。
 読者の皆さんも、BLOB型を活用してみませんか。

参考リンク
FileUpload 1.0
Jakarta CommonsなどのAPI翻訳

関連リンク
▼JAVA Developer
▼定期購読のご案内
▼バックナンバー販売協力店

JAVA Developer11月号表紙 JAVA Developer 11月号

大特集
DBチューニング大会

特集2 AS徹底解説番外編
[特別企画]
・動的画像生成
・DOMの基本テクニック
・目指せ最強! 最速! CodeRally
・WS-Iの最新事情レポート

[松浦 武範,JAVA Developer]