|
.NET TIPS
[ASP.NET]動的に圧縮ファイルを生成するには?
山田 祥寛
2005/10/07 |
|
|
サーバから複数のファイル、しかも大容量のデータをクライアントにダウンロードさせる場合、個々のファイルを個別にダウンロードさせるよりも、必要なデータを(ZIP形式などで)圧縮したうえで1ファイルにまとめた方が通信時間を短縮することができる。
もちろんあらかじめ必要なデータが分かっている場合には、静的に圧縮ファイルを用意しておいた方がよいだろう。しかし取得するファイルを動的に選択させたい、あるいはデータベースから取得したデータに基づいて、ファイルそのものを動的に生成したいという場合には、アプリケーションで動的に圧縮ファイルを生成する必要がある。
本稿では、(.NET Framework 1.1で新たに.NET言語として加わった)「J#」が提供するZipOutputStreamクラス(java.util.zip名前空間)を利用して、データベースから取得したテーブルの内容(タブ区切りテキスト)を動的に圧縮し、ダウンロード提供する方法について紹介する。
なお本稿のサンプルを実行するに当たっては、あらかじめbooks、schedule、sitemapという名前でテーブルを作成し、中に適当なデータをセットしておくこと。フィールド・レイアウトは特に問わない。
それではさっそく、具体的な手順を見てみることにしよう。
1. アセンブリ“vjslib.dll”への参照を追加する
本稿のサンプルでは、J#が提供するjava.io、java.util.zipのような名前空間のクラスを使用している。そのため、サンプルを動作させるには、NET Framework 1.1のほかに、「Visual J# .NET Version 1.1再頒布可能パッケージ」をインストールしておく必要がある。
また、J#のアセンブリである“vjslib.dll”への参照をweb.configに追加しておくこと(アセンブリの追加に関する詳細については、「TIPS:[ASP.NET]ASP.NETでVB.NET固有の関数をC#から利用するには?」を参照されたい)。
“vjslib.dll”への参照を追加するための具体的なweb.configの記述は以下のとおりだ。
<?xml version="1.0" encoding="UTF-8" ?>
<configuration>
<system.web>
<compilation debug="true">
<assemblies>
<add assembly="vjslib, Version=1.0.5000.0, Culture=Neutral, PublicKeyToken=b03f5f7f11d50a3a" />
</assemblies>
</compilation>
</system.web>
</configuration>
|
|
“vjslib.dll”への参照を追加する設定(web.config) |
2. Webフォームから圧縮ファイル(ZIPファイル)を生成する
以上の設定ができたら、データベース内のテーブルから複数のタブ区切りテキスト・ファイルを生成し、これを圧縮ファイル化してみよう。以下が具体的なコードだ。
<%@ Page ContentType="text/html" Language="C#" %>
<%@ Import Namespace="java.io" %>
<%@ Import Namespace="java.util.zip" %>
<%@ Import Namespace="System.Data" %>
<%@ Import Namespace="System.Data.SqlClient" %>
<%@ Import Namespace="System.IO" %>
<script runat="Server">
void Page_Load(Object sender, EventArgs e) {
// dataフォルダは圧縮ファイルを一時的に保存するフォルダ。
// 生成して5分以上経過しているファイルはゴミと見なし、削除
DirectoryInfo dinf = new DirectoryInfo(Server.MapPath("data"));
foreach (FileInfo file in dinf.GetFiles()) {
if (file.CreationTime.AddMinutes(5) < DateTime.Now) {
file.Delete();
}
}
// セッションIDから圧縮ファイル名を生成
String fileName = "data/" + FormsAuthentication.HashPasswordForStoringInConfigFile(Session.SessionID, "MD5") + ".zip";
// データを取得するテーブル名を配列として定義
String[] tbls = { "books", "schedule", "sitemap" };
// 指定されたファイルへの圧縮出力ストリームを生成
ZipOutputStream zipStream = new ZipOutputStream(
new FileOutputStream(Server.MapPath(fileName)));
SqlConnection db = new SqlConnection("Data Source=(local);User ID=sa;Password=sa;Persist Security Info=True;Initial Catalog=dotnet");
db.Open();
// books、schedule、sitemapを順に処理し、タブ区切りテキストを生成
for (int i = 0; i <= tbls.GetUpperBound(0); i++)
// 取得したデータをバイト配列に書き込むための
// OutputStreamWriterを用意
ByteArrayOutputStream bArray = new ByteArrayOutputStream();
OutputStreamWriter oStream =
new OutputStreamWriter(bArray, "SJIS");
SqlCommand comm = new SqlCommand("SELECT * FROM " + tbls[i],db);
SqlDataReader reader = comm.ExecuteReader();
// テーブルから取得したDataReaderに基づき、
//タイトル行をタブ区切りで出力
for (int j = 0; j < reader.FieldCount; j++) {
oStream.write(reader.GetName(j));
if (j < reader.FieldCount - 1) { oStream.write("\t"); }
}
oStream.write("\r\n");
// DataReaderに含まれるデータをタブ区切りテキストとして出力
while (reader.Read()) {
for (int j = 0; j < reader.FieldCount; j++) {
oStream.write(reader.GetValue(j).ToString());
if (j < reader.FieldCount - 1) { oStream.write("\t"); }
}
oStream.write("\r\n");
}
oStream.close();
// Zipファイル内のファイル・エントリを生成
//(ファイル名は「<テーブル名>.txt」)
ZipEntry zEntry = new ZipEntry(tbls[i] + ".txt");
zEntry.setMethod(ZipOutputStream.DEFLATED);
// 圧縮出力ストリームにファイル・エントリを追加し、
// 上で作成したバイト配列を書き込む
zipStream.putNextEntry(zEntry);
zipStream.write(bArray.toByteArray(),
0, bArray.toByteArray().Length);
zipStream.closeEntry();
reader.Close();
}
zipStream.flush();
zipStream.close();
// HyperLinkコントロールに生成された圧縮ファイルへのパスをセット
lnk.NavigateUrl = fileName;
}
</script>
<html>
<head>
<title>圧縮ファイルの作成</title>
</head>
<body>
<asp:HyperLink id="lnk" runat="Server" Text="ファイル・ダウンロード" />
</body>
</html>
|
|
データベースから取得したデータから圧縮ファイルを生成するWebフォーム(C#版:zip_cs.aspx) |
<%@ Page ContentType="text/html" Language="VB" %>
<%@ Import Namespace="java.io" %>
<%@ Import Namespace="java.util.zip" %>
<%@ Import Namespace="System.Data" %>
<%@ Import Namespace="System.Data.SqlClient" %>
<%@ Import Namespace="System.IO" %>
<script runat="Server">
Sub Page_Load(sender As Object, e As EventArgs)
' dataフォルダは圧縮ファイルを一時的に保存するフォルダ。
' 生成して5分以上経過しているファイルはゴミと見なし、削除
Dim dinf As New DirectoryInfo(Server.MapPath("data"))
For Each file As FileInfo In dinf.GetFiles()
If file.CreationTime.AddMinutes(5) < DateTime.Now Then file.Delete()
Next
' セッションIDから圧縮ファイル名を生成
Dim fileName As String = "data/" & Session.SessionID & ".zip"
' データを取得するテーブル名を配列として定義
Dim tbls() As String = {"books", "schedule", "sitemap"}
' 指定されたファイルへの圧縮出力ストリームを生成
Dim zipStream As New ZipOutputStream( _
New FileOutputStream(Server.MapPath(fileName)))
Dim db As New SqlConnection("Data Source=(local);User ID=sa;Password=sa;Persist Security Info = True;Initial Catalog=dotnet")
db.Open()
' books、schedule、sitemapを順に処理し、タブ区切りテキストを生成
For i As Integer = 0 To tbls.GetUpperBound(0)
' 取得したデータをバイト配列に書き込むための
' OutputStreamWriterを用意
Dim bArray As New ByteArrayOutputStream()
Dim oStream As New OutputStreamWriter(bArray, "SJIS")
Dim comm As New SqlCommand("SELECT * FROM " & tbls(i), db)
Dim reader As SqlDataReader = comm.ExecuteReader()
' テーブルから取得したDataReaderに基づき、
'タイトル行をタブ区切りで出力
For j As Integer = 0 To reader.FieldCount-1
oStream.write(reader.GetName(j))
If j< reader.FieldCount - 1 Then oStream.write(Chr(9))
Next
oStream.write(Chr(13) & Chr(10))
' DataReaderに含まれるデータをタブ区切りテキストとして出力
Do While reader.Read()
For j As Integer = 0 To reader.FieldCount - 1
oStream.write(reader.GetValue(j).ToString())
If j < reader.FieldCount - 1 Then oStream.write(Chr(9))
Next
oStream.write(Chr(13) & Chr(10))
Loop
oStream.close()
' Zipファイル内のファイル・エントリを生成
'(ファイル名は「<テーブル名>.txt」)
Dim zEntry As New ZipEntry(tbls(i) & ".txt")
zEntry.setMethod(ZipOutputStream.DEFLATED)
' 圧縮出力ストリームにファイル・エントリを追加し、
' 上で作成したバイト配列を書き込む
zipStream.putNextEntry(zEntry)
zipStream.write(bArray.toByteArray(), _
0, bArray.toByteArray().length)
zipStream.closeEntry()
reader.Close()
Next
zipStream.flush()
zipStream.close()
' HyperLinkコントロールに生成された圧縮ファイルへのパスをセット
lnk.NavigateUrl = fileName
End Sub
</script>
<html>
<head>
<title>圧縮ファイルの作成</title>
</head>
<body>
<asp:HyperLink id="lnk" runat="Server" Text="ファイル・ダウンロード" />
</body>
</html>
|
|
データベースから取得したデータから圧縮ファイルを生成するWebフォーム(VB.NET版:zip_vb.aspx) |
複数の出力ストリームを介しているため、やや複雑に見えるかもしれないが、順を追って全体の流れを見ていくことにしよう。なおタブ区切りテキスト生成の手順は、本稿の主題ではないので、ここでは割愛する。詳細はコード内のコメントを参照していただきたい(なお本稿の例では、「books.txt」「schedule.txt」「sitemap.txt」という3つのテキスト・ファイルを生成し、1つのZIPファイルに圧縮している)。
まずZIP形式の圧縮データを出力するのは、ZipOutputStreamクラス(java.util.zip名前空間)の役割だ。このクラスは、出力ストリームに対してZIP圧縮フィルタを提供する。ここでは、出力ストリームとして、FileOutputStreamクラス(java.io名前空間)を介しているので、ファイル・ストリームへの出力時に圧縮処理が実行されることになる。
ZipOutputStreamオブジェクトへの書き込みは、以下の手順で行うことができる。
(1)putNextEntryメソッドによるファイル・エントリの生成
(2)writeメソッドによる実データの書き込み
(3)closeEntryメソッドによるファイル・エントリのクローズ
個々のファイル・エントリ(本稿の例では、「books.txt」「schedule.txt」「sitemap.txt」のそれぞれのファイル)を表すのは、ZipEntryオブジェクト(java.util.zip名前空間)の役割だ。圧縮処理を行う場合には、圧縮メソッドとしてsetMethodメソッドにZipOutputStream.DEFLATED(圧縮処理)をセットすること。
実データ(本稿では、タブ区切りテキスト)を書き込むwriteメソッドの構文は、以下のとおり。
public void write(sbyte[] b, int off, int len) |
Public Sub write(ByVal b() As SByte, ByVal off As Integer, ByVal len As Integer) |
|
ZipOutputStream.writeメソッドの構文(上:C#、下:VB.NET) |
第1パラメータは書き込むデータ(バイト配列)を、第2パラメータはデータの開始オフセットを、第3パラメータは書き込むデータのバイト数を指定する。 |
バイト配列への書き込みは、ByteArrayOutputStreamオブジェクト(java.io名前空間)によって行う。ByteArrayOutputStreamオブジェクトをラップしているOutputStreamWriterオブジェクト(java.io名前空間)は、文字ストリームからバイトストリームへの橋渡し的な役割を果たすものだと思っていただければよいだろう。
OutputStreamWriterオブジェクトを介することで、渡された文字列は指定された文字コードを使用してバイト・コードに符号化されるというわけだ。ByteArrayOutputStreamオブジェクトに蓄積されたバイト・データは、toByteArrayメソッドでバイト配列として取得することができる。
これら(1)〜(3)の手順を繰り返すことで、ZIP圧縮データに複数のファイル・エントリを追加することができる。すべてのエントリを追加したら、closeメソッドでZipOutputStreamオブジェクトをクローズして完了だ。
以上が理解できたら、さっそくサンプル・プログラムを実行してみよう。
|
|
|
サンプル・プログラムの実行結果 |
ダウンロードした圧縮ファイルを解凍ソフトで閲覧したところ(上画面)。また、個々のファイル・エントリ(タブ区切りテキスト)はMicrosoft Excelなどの表計算アプリケーションから開くことができる(下画面)。 |
ダウンロードした圧縮ファイルに3つのファイル(「books.txt」「schedule.txt」「sitemap.txt」)が含まれており、各ファイルがMicrosoft Excelなどの表計算アプリケーションから正しく表示できれば成功だ。
本稿のサンプル・プログラムでは、取得するテーブルをハード・コーディングしているが、もちろんフォーム上からエンド・ユーザーが指定し、動的に変更することも可能だ。余力のある方は改良してより良いツールを作成していただきたい。
カテゴリ:Webフォーム 処理対象:ファイル・ダウンロード
使用ライブラリ:ZipOutputStreamクラス(java.util.zip名前空間)
使用ライブラリ:FileOutputStreamクラス(java.io名前空間)
使用ライブラリ:ZipEntryクラス(java.util.zip名前空間)
使用ライブラリ:ByteArrayOutputStreamクラス(java.io名前空間)
使用ライブラリ:OutputStreamWriterクラス(java.io名前空間)
関連TIPS:[ASP.NET]ASP.NETでVB.NET固有の関数をC#から利用するには? |
|
generated by
|
|