データベースの内容を圧縮ダウンロードする−ZipEntry/ZipOutputStreamクラス−
最後は、ちょっと変わり種のサンプルです。
データベースから取り出した大量のデータをCSV形式(カンマ区切りテキスト)に変換し、これをZipファイルとしてダウンロードさせてみましょう。往々にして、データベースから取り出した生のデータは容量的にも大きくなってしまうケースが少なくありません。特にナローバンドなユーザーにとっては、何百Kbytes、何Mbytesにも及ぶデータのダウンロードは苦痛でしょう。そんなときに、ZipOutputStreamクラスを介することで、クライアントへの出力時に圧縮処理をかけることができるのです。
<%@ page contentType="application/octet-stream; charset=Shift_JIS" import="java.io.*,java.sql.*,java.util.zip.*" %> <% response.setHeader("Content-Disposition","attachment; filename=sample.zip"); Class.forName("org.gjt.mm.mysql.Driver"); Connection db=DriverManager.getConnection("jdbc:mysql://localhost/sample?user= root&password =root&useUnicode=true&characterEncoding=Shift_JIS"); db.setReadOnly(true); Statement objSql=db.createStatement(); ResultSet rs=objSql.executeQuery("SELECT * FROM books"); ByteArrayOutputStream objBos=new ByteArrayOutputStream(); OutputStreamWriter objOsw=new OutputStreamWriter(objBos,"Shift_JIS"); objOsw.write("書名\t著者名\t出版社\t著者\t価格\t出版日"); objOsw.write(System.getProperty("line.separator")); while(rs.next()){ objOsw.write(rs.getString("title") + "\t"); objOsw.write(rs.getString("author") + "\t"); objOsw.write(rs.getString("published") + "\t"); objOsw.write(rs.getString("price") + "\t"); objOsw.write(rs.getString("publishDate") + "\t"); objOsw.write(System.getProperty("line.separator")); } objOsw.close(); ZipOutputStream objZos=new ZipOutputStream(response.getOutputStream()); ZipEntry objZe=new ZipEntry("database.txt"); objZe.setMethod(ZipOutputStream.DEFLATED); objZos.putNextEntry(objZe); byte[] aryByt=objBos.toByteArray(); objZos.write(aryByt,0,aryByt.length); objZos.closeEntry(); objZos.close(); objSql.close(); db.close(); %>
上記のコードを実行した際に表示される画面です。
ロジックの概要
ZipOutputStreamは、コンテンツの出力時にZip形式での圧縮(または解凍)処理を行うためのクラスです。新出のメソッドが一見複雑に絡み合っているようにも見えますが、手順自体はきわめて定型的な流れであるにすぎません。ここでは、データベースから取得した値をCSV形式に整形し、Zip形式に圧縮するまでの流れを簡単にまとめてみることにしましょう。
(1)出力ストリームの設定
メモリ内のバイト配列(ByteArrayOutputStream)への書き込みを行います。バイト配列ストリームを利用することで、サーバ上のメモリをあたかも一時的なファイルのように利用することができます。ByteArrayOutputStreamにはそのままでは文字列を書き込むことができませんので、OutputStreamWriterオブジェクトを介して書き込みを行うのが通例です。OutputStreamWriterクラスはバイナリストリームと文字ストリームとの橋渡しを行う手段を提供します。
(2)Zip出力ストリームの設定
圧縮出力用のストリームを設定します。ZipOutputStreamクラスのコンストラクタには、実際の出力先を表すバイト出力ストリームを設定します。ここでは、HttpServletResponse#getOutputStreamメソッドによって取得可能なServletOutputStreamを指定しておくことにしましょう。
通常、サーブレット/JSPは出力コンテンツを文字列として出力しようとしますが、Zipファイルのようなバイナリデータを文字コードなどを意識しない生データのままで出力したいという場合、このServletOutputStreamを用います。
生成されたZipOutputStreamオブジェクトを介して圧縮方法(setMethod)や圧縮レベル(setLevel)を指定することも可能です。
(3)エントリの設定
ZipEntryオブジェクトは、圧縮ファイルに格納する個々のファイルエントリを表します。ここではdatabase.txtという名前でアーカイブにエントリします。ZipEntryオブジェクトはZipOutputStream#putNextEntryメソッドを用いてZipアーカイブに追加します。
(4)エントリへのデータ書き込み
(1)で生成したByteArrayOutputStreamクラスの内容をバイト配列に変換したうえで、ZipOutputStreamオブジェクトに書き込みます。書き込み終了後は、必ずcloseEntryメソッドでエントリの終了を宣言してください。
もしも次のファイルエントリを追加する場合(putNextEntryメソッド)には、必ず前のエントリがクローズされている必要があります。
(5)後処理
ここでは1個のファイルを圧縮するだけとしますので、ZipOutputStreamをクローズします。これでアーカイブの作成は完了です。
クライアントにダウンロードデータであることを知らせる
ただし、上記のようにHttpServletResponse#getOutputStreamメソッドをただ出力ストリームに設定しただけでは、データが単純にブラウザ上に表示されてしまいます(おそらくブラウザにはバイナリの意味のない文字列が出力されることでしょう)。
そこで、クライアント側に出力データがダウンロード用に引き渡されたものであるということを教えてやる必要があります。それが1〜4行目の部分です。
<%@ page contentType="application/octet-stream; charset=Shift_JIS" import="java.io.*,java.sql.*,java.util.zip.*" %> <% response.setHeader("Content-Disposition","attachment; filename=sample.zip");
@pageディレクティブのcontentType属性には、いつもtext/htmlではなく、application/octet-streamを、また、Content-Dispositionヘッダとしてダウンロードファイル名を指定します。この2つの指定で、クライアント側は送信されてきたデータをダウンロードデータと見なし、デフォルトのファイル名をsample.zipとして保存します(ただし、ファイル名はユーザー側が自由に変更することが可能です)。
One Point
ZipOutputStreamクラスを利用することで、出力データを圧縮変換することが可能です。ZipEntryクラスを複数追加することで、圧縮ファイルに複数のファイルを含めることも可能です
以上4回にわたって、サーブレット/JSPで頻繁に使用すると思われる主要なクラス・インターフェイスのご紹介をしてきました。サーブレット/JSPというと、いかにも特別な世界に思われるかもしれませんが、要は「サーバサイドで利用できる」Javaアプリケーションにすぎないのです。つまり、サーブレット/JSPで本格的なアプリケーションを構築したいという場合には、やはりコア(基本)APIの理解は欠かせないということです。
コアAPIはサーブレットAPIとJSP以上に大きなクラスライブラリですが、頻繁に使用するクラスの種類はごく限られています。本稿でご紹介したクラス群を1つのきっかけとして、理解の幅を次第に広げていってみてください。
さて、JSPの話題が長く続きましたが、いよいよ次回からはサーブレットの登場です。雰囲気的にはずいぶん違ったものに見えるかもしれませんが、「JSPの知識を前提に」サーブレットでの注意点をご紹介していきたいと思います。まったく新しい内容としてではなく、あくまで「JSPの延長線上のサーブレット」という理解で読み進めてみると、きっと分かりやすいはずです。引き続き頑張っていきましょう!
Copyright © ITmedia, Inc. All Rights Reserved.