データの永続化...画像を含んだデータの保存方法(Java)
Javaのデスクトッププログラムで、データを保存する場合、 どうしていますか?
データ永続化方法
わたしの場合、Javaをはじめてまもないことは、
Javaに標準で備わっているシリアライズのしくみを
使っていました。
しかし、この方法は、標準のライブラリでない自作のモデルクラスをシリアライズしようとすると
いろいろ不便な点が多いので、やめました。
その次は、XMLを使った方法です。
この方法は現在も状況に応じて使用しています。
JDOMなどを使えば、簡単にXMLの読み書きはできる。
XMLなら、データを直接テキストエディタで開いて扱えるので
なにかと便利。
もちろん、java.awt.ColorなどをXMLデータへ変換するのはちょっと面倒で
追加のコードを書く必要はありますが。
その次は、JDBC経由でのデータベースへの保存。 MySQLなどを使ってしまうと、移植時に移植先でMySQLが使えなかったら 困る、という問題がある。 ただし、H2Database/HSQLDBなどPureJavaのデータベースを使えば、 問題が解決できる・・・かもしれません。 でもデータベースを使うのは大げさすぎて、この方法を使うのが適当な プロジェクトはそれほどありません。
YAML
http://ja.wikipedia.org/wiki/YAML
これくらい簡単な形式でデータを保存しておいた方が のちのち扱いやすい。 最近は、この手の軽量マークアップ言語関心がある。
画像データをどう保存するか?
- Base64などでエンコードしてXMLに埋め込む?
- JDBC経由でBLOBタイプなどを使いバイナリを直接書き込む?
最近気に入っているのは、画像データをそのまま画像ファイルとして扱ってしまう方法です。 そのままでは、 複数の画像データを扱う場合にひとつのファイルで済ますことができないので、 zipファイルを使います。
Zipを使う方法は、MS-Office2007のデータ形式でも使われているので、
一般的な方法なのでしょう。
さいわい、Javaには標準で java.util.zip というパッケージが含まれているので、
簡単に扱うことができます。
ZIP形式でのファイル作成
Zipファイルを作成するには、
java.util.zip.ZipOutputStream クラスを使います。
※java.util.zip.ZipFile というクラスがあるがこれはZipアーカイブ作成時には使用しない。
import java.io.*;
import java.util.zip.*;
public class WriteZipFile{
private static final int EOF=-1;
public WriteZipFile(){
}
private File[] imageFiles;
public void setImageFiles(File[] imageFiles){
this.imageFiles=imageFiles;
}
public File[] getImageFiles(){
if(imageFiles==null)
imageFiles=new File[0];
return imageFiles;
}
public void write(File zipFile) throws Exception{
ZipOutputStream out=new ZipOutputStream(new FileOutputStream(zipFile));
File[] files=getImageFiles();
for(int i=0; i<files.length; i++){
addImageFileEntry(out,files[i]);
}
out.close();
}
private void addImageFileEntry(ZipOutputStream out,File imageFile)
throws IOException{
if(imageFile==null || imageFile.exists()==false)
return ;
String entryName=imageFile.getName();
ZipEntry ent=new ZipEntry("image"+File.separator+entryName);
out.putNextEntry(ent);
{
BufferedInputStream bin=
new BufferedInputStream( new FileInputStream(imageFile) );
int c;
while( (c=bin.read()) != EOF){
out.write( (byte)c );
}
bin.close();
}
out.closeEntry();
}
private static File ZIP_FILE=new File("test.zip");
public static void main(String[] args) throws Exception{
if(args.length>0){
File[] files=new File[args.length];
for(int i=0; i<args.length; i++){
files[i]=new File(args[i]);
}
WriteZipFile wzf=new WriteZipFile();
wzf.setImageFiles( files );
wzf.write( ZIP_FILE );
}
}
}
以下のように作動確認します。
$ javac WriteZipFile.java
$ java WriteZipFile test0.gif test1.gif
$ jar tvf test.zip
19128 Tue Jun 03 20:27:42 JST 2008 image/test0.gif
19128 Tue Jun 03 20:27:44 JST 2008 image/test1.gif
WriteZipFileクラスでは、引数から指定されたファイルを読み取って、 imageディレクトリの中に同名のファイルを作成し、 test.zipというZipファイルを作成します。
Zipファイルの読み取り
WriteZipFileで作成したZipファイルを読み取り、java.awt.Image配列に変換します。
import java.io.*;
import java.awt.*;
import java.util.*;
import java.util.zip.*;
import javax.imageio.*;
public class ReadZipFile{
private static final int EOF=-1;
public ReadZipFile(){
}
public Image[] read(File zipFile) throws Exception{
ArrayList imageList=new ArrayList();
ZipFile zf=new ZipFile(zipFile);
java.util.Enumeration enu=zf.entries();
while(enu.hasMoreElements()){
ZipEntry ze=(ZipEntry)enu.nextElement();
System.out.println(ze);
String name=ze.getName();
if(name.startsWith("image")){
BufferedInputStream bin=
new BufferedInputStream(zf.getInputStream(ze));
imageList.add( ImageIO.read(bin) );
bin.close();
}
}
return (Image[])imageList.toArray(new Image[0]);
}
private static File ZIP_FILE=new File("test.zip");
public static void main(String[] args) throws Exception{
ReadZipFile rzf=new ReadZipFile();
Image[] images=rzf.read(ZIP_FILE);
for(int i=0; i<images.length; i++){
System.out.println(images[i]);
}
}
}
以下のように作動確認します。
$ javac ReadZipFile.java
$ java ReadZipFile
BufferedImage@16cd7d5: type = 13 IndexColorModel: #pixelBits = 8 numComponents = 4 color space = java.awt.color.ICC_ColorSpace@cdedfd transparency = 2 transIndex = 255 has alpha = true isAlphaPre = false ByteInterleavedRaster: width = 170 height = 340 #numDataElements 1 dataOff[0] = 0
BufferedImage@1c39a2d: type = 13 IndexColorModel: #pixelBits = 8 numComponents = 4 color space = java.awt.color.ICC_ColorSpace@cdedfd transparency = 2 transIndex = 255 has alpha = true isAlphaPre = false ByteInterleavedRaster: width = 170 height = 340 #numDataElements 1 dataOff[0] = 0