トップ回答者
データベースに保存したファイルをダウンロードする際、更新日を指定したい

質問
-
はじめまして。さとと申します。
開発環境: Visual Web Developer 2008、SQLServer2000
ファイルをデータベースに保存し、保存したファイルをダウンロードする仕組みを構築しました。
当然ながら、データベースからファイル出力する際、そのファイルの最終更新日がファイル出力した日に
変更されます。
ファイルをデータベースに保存する際、GetLastWriteTimeでそのファイルの最終更新日を記録し、
ファイルをデータベースから取り出す際にその更新日を付加してファイル出力したいのですが方法がよく分かりません。
ファイル出力の際、下記構文で出力しております。
If objRs.Read() Then
Response.ContentType = CType(objRs.Item(0), String)
Response.BinaryWrite(CType(objRs.Item(1), Byte()))
Response.AddHeader("Content-Disposition", "attachment;filename=" + HttpUtility.UrlEncode(objRs.Item(2)))
End IfobjDb.Close()
Response.End()
この処理の後に、LastWriteTimeでファイルに更新日を設定するのだと思うのですが、どのように指定すれば
いいのでしょうか?
ヒントをお願いします。
以上、よろしくお願いします。
回答
-
さと581402 さんからの引用 この処理の後に、LastWriteTimeでファイルに更新日を設定するのだと思うのですが、どのように指定すれば
いいのでしょうか?
ヒントをお願いします。
WindowsアプリケーションであればLastWriteTimeで日付の更新は可能ですが、Webアプリケーションの場合、すでにクライアントにファイルがダウンロードされてしまっているので更新は出来ません。
ただし、Response.AddHeader("Content-Disposition", "attachment;filename=" + HttpUtility.UrlEncode
にファイルの更新日付の情報を入れる事ができれば可能だと思いますが、出来るかどうか不明です。
(うろ覚えですが、RFCで定義されていた気はするのですが…)
一度、そちらの観点から調べてみてはどうでしょうか?
-
> そうしたところで、結局、ブラウザ次第なんじゃないのかな
自分もそう思います。
例として正しいかどうかわかりませんが、FTPでファイルをGETやPUTしてもファイルの更新日付は変わってしまいますし。
もし、どうしてもサーバーの日付と合わせる必要があるのであれば、ファイルをダウンロードさせるActiveXコントロールを作成して、それで日付を更新する方法はありますけど。
(セキュリティやActiveXコントロールをインストールする必要があるため、あまり勧められません)今思いつきましたけど、サーバー側でファイルをzipやlhaで圧縮し、クライアント側でダウンロード後、それを展開すれば更新日時はサーバーと同じになると思いますけど、どうでしょう?
-
さと581402 さんからの引用 ファイルをデータベースに保存する際、GetLastWriteTimeでそのファイルの最終更新日を記録し、
ファイルをデータベースから取り出す際にその更新日を付加してファイル出力したいのですが方法がよく分かりません。
ファイル出力の際、下記構文で出力しております。
If objRs.Read() Then
Response.ContentType = CType(objRs.Item(0), String)
Response.BinaryWrite(CType(objRs.Item(1), Byte()))
Response.AddHeader("Content-Disposition", "attachment;filename=" + HttpUtility.UrlEncode(objRs.Item(2)))
End IfobjDb.Close()
Response.End()
この処理の後に、LastWriteTimeでファイルに更新日を設定するのだと思うのですが、どのように指定すれば
いいのでしょうか?
みなさんのレスから、結論としては、
データベースから取り出されたバイト列データ objRs.Item(1) からファイルを
作って、> 一度サーバ上のどこかにファイルを書き込んで、更新日時を変更したものを
> zipやlhaで圧縮し、
その圧縮ファイルをバイト列データに変換し、Response.BinaryWrite メソッド
で HTTP 出力ストリームに書き込み、> クライアント側でダウンロード後、それを展開
ということになるようです。
上記でよろしいでしょうか? > 回答者のみなさん
-
さと581402 さんからの引用 でも、「3)GZipStreamでzipに圧縮。」した際に、圧縮前のファイル更新日が勝手に変更されてしまいます。
今頃になって気がつきました。(汗)
たぶん、FileStream で読んでくる実データのみを圧縮して送ることを考
えていると思いますが、そもそも実データにはファイル名や更新日などの
情報(メタデータ)は入ってないです。WinZip 等が作る zip アーカイブには、実データに加えてメタデータが含
まれており、解凍時にメタデータを使ってファイル名や更新日時を設定し
ているようです。という訳で、「圧縮前のファイル更新日が勝手に変更」と言うことではな
さそうです。今回の目的を果たすには、実データに加えてメタデータも一緒に、世間一
般に使われている解凍ツールと互換性のある zip アーカイブにして送っ
てやる必要があるということになります。zip アーカイブを作るには、以下のサイトで紹介されている方法が役に
立ちそうです。荒井省三のBlog: Dynamic Language SDK を使った ZIPアーカイブの使い方
http://blogs.msdn.com/shozoa/archive/2008/09/24/how-to-zip-archive-by-using-dynamic-language-sdk.aspx例えば、ここで紹介されている「フリーのライブラリを使用する(たとえ
ば、ここなど)。」のリンク↓The Zip, GZip, BZip2 and Tar Implementation For .NET
http://www.icsharpcode.net/OpenSource/SharpZipLib/Default.aspxこのライブラリを使えば、更新日時は自由に設定できますし、作った zip
ファイルは WinZip で開けることは自分も確認済みです。圧縮するだけな
らかなり簡単に出来ます(他の手段でも簡単かもしれませんが)。マイクロソフトのサンプルもありますが(下記サイトから入手できる)、
これで作った圧縮ファイルは、WinZip と互換性がないです。理由は調べ
てません(調べる前に諦めました)。Microsoft .NET Framework SDK version 2.0 Samples Download
http://www.microsoft.com/downloads/details.aspx?FamilyID=adef80f9-1e44-4b94-be24-dff48f826ce6&DisplayLang=en -
SurferOnWww さんからの引用 みなさんのレスから、結論としては、
データベースから取り出されたバイト列データ objRs.Item(1) からファイルを
作って、> 一度サーバ上のどこかにファイルを書き込んで、更新日時を変更したものを
> zipやlhaで圧縮し、
その圧縮ファイルをバイト列データに変換し、Response.BinaryWrite メソッド
で HTTP 出力ストリームに書き込み、> クライアント側でダウンロード後、それを展開
ということになるようです。
よく考えてみると、「一度サーバ上のどこかにファイルを書き込んで、更
新日時を変更」というステップは不要でした。以下のステップで可能です。
(1) データベースからバイト列データを取り出す。
(2) 既存のライブラリを利用して zip アーカイブをバイト列として作成。
更新日時やファイル名はその時設定。(3) 作成したバイト列を Response.BinaryWrite メソッドで HTTP 出力ス
トリームに書き込みご参考までに、先のレスで紹介した下記のサイトから入手できるライブラ
リを使った例をアップしておきます。The Zip, GZip, BZip2 and Tar Implementation For .NET
http://www.icsharpcode.net/OpenSource/SharpZipLib/Default.aspx実行するには Bin フォルダに ICSharpCode.SharpZipLib.dll を入れてお
く必要があります(上記のサイトから入手できます)。Code Snippet<%@ Page Language="C#" %>
<%@ Import Namespace="System.Data" %>
<%@ Import Namespace="System.Data.SqlClient" %>
<%@ Import Namespace="System.Configuration" %>
<%@ Import Namespace="System.IO" %>
<%@ Import Namespace="ICSharpCode.SharpZipLib.Zip" %><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<script runat="server">
protected void Page_Load(object sender, EventArgs e)
{
string connString =
ConfigurationManager.ConnectionStrings["databaseConnectionString"].ConnectionString;
SqlConnection connection = new SqlConnection(connString);
string query = "SELECT filename, data FROM files WHERE id=@id";
SqlCommand command = new SqlCommand(query, connection);
command.Parameters.AddWithValue("@id", Convert.ToInt32(Request.QueryString["id"]));
string filename = null;
byte[] data = null;
try
{
connection.Open();
SqlDataReader reader = command.ExecuteReader();if (reader.Read())
{
filename = (string)reader[0];
data = (byte[])reader[1];
}
}
finally
{
connection.Close();
}if (filename == null || data == null)
{
// DB から取得できなかった場合の処置(ここでは単に return)
return;
}Response.AppendHeader("Content-Disposition",
"attachment; filename=" + HttpUtility.UrlEncode(filename) + ".zip");
Response.ContentType = "application/x-zip-compressed";byte[] zippedData = new byte[data.Length]; // 圧縮前 (data.Length) よりは小さくなるはず。
int zippedDataLength = 0;
using (MemoryStream memoryStream = new MemoryStream(zippedData))
{
using (ZipOutputStream zipOutStream = new ZipOutputStream(memoryStream))
{
zipOutStream.SetLevel(9); // 圧縮度の設定。9 が最高
ZipEntry entry = new ZipEntry(filename);
entry.DateTime = new DateTime(2000, 1, 1); // ここで更新日時を設定
zipOutStream.PutNextEntry(entry);
zipOutStream.Write(data, 0, data.Length);
zipOutStream.Finish();// 圧縮後のサイズを取得。Finish() の前に置くのはダメ。zipOutStream.Length はダメ。
zippedDataLength = (int)zipOutStream.Position;
zipOutStream.Close();
}
}
Array.Resize(ref zippedData, zippedDataLength);
Response.BinaryWrite(zippedData);
Response.End();
}
</script><html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title></title>
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:Label ID="Label1" runat="server"></asp:Label>
</div>
</form>
</body>
</html>
すべての返信
-
さと581402 さんからの引用 この処理の後に、LastWriteTimeでファイルに更新日を設定するのだと思うのですが、どのように指定すれば
いいのでしょうか?
ヒントをお願いします。
WindowsアプリケーションであればLastWriteTimeで日付の更新は可能ですが、Webアプリケーションの場合、すでにクライアントにファイルがダウンロードされてしまっているので更新は出来ません。
ただし、Response.AddHeader("Content-Disposition", "attachment;filename=" + HttpUtility.UrlEncode
にファイルの更新日付の情報を入れる事ができれば可能だと思いますが、出来るかどうか不明です。
(うろ覚えですが、RFCで定義されていた気はするのですが…)
一度、そちらの観点から調べてみてはどうでしょうか?
-
試していませんが、filenameと同様に、creation-date、modification-date、read-dateでうまくいくかもしれません。
CatTailさんが言われているのは、RFC2183のことだと思います。RFC 2183 インターネットメッセージの情報交流表現:Content-Dispositionヘッダーフィールド
http://jbpe.tripod.com/rfcj/rfc2183.j.sjis.txt -
trapemiya さんからの引用 試していませんが、filenameと同様に、creation-date、modification-date、read-dateでうまくいくかもしれません。
modification-date のみですが、実際に試してみました。結果は何も変化な
し(更新日時はダウンロードした日時のまま)でした。
RFC 2183 の 3. Examples の個人的解釈ですが、必要がある場合、受信
者がダウンロードしたファイルの更新日時を正しく修正できるように、実際の
更新日時を modification-date 書いておくということのようです。(ファイル名
の方は filename で指定された名前にすべきだそうですが)
というわけで、どっとねっとふぁんさんが書かれた方法の他なさそうな気がし
ます。
-
> そうしたところで、結局、ブラウザ次第なんじゃないのかな
自分もそう思います。
例として正しいかどうかわかりませんが、FTPでファイルをGETやPUTしてもファイルの更新日付は変わってしまいますし。
もし、どうしてもサーバーの日付と合わせる必要があるのであれば、ファイルをダウンロードさせるActiveXコントロールを作成して、それで日付を更新する方法はありますけど。
(セキュリティやActiveXコントロールをインストールする必要があるため、あまり勧められません)今思いつきましたけど、サーバー側でファイルをzipやlhaで圧縮し、クライアント側でダウンロード後、それを展開すれば更新日時はサーバーと同じになると思いますけど、どうでしょう?
-
さと581402 さんからの引用 ファイルをデータベースに保存する際、GetLastWriteTimeでそのファイルの最終更新日を記録し、
ファイルをデータベースから取り出す際にその更新日を付加してファイル出力したいのですが方法がよく分かりません。
ファイル出力の際、下記構文で出力しております。
If objRs.Read() Then
Response.ContentType = CType(objRs.Item(0), String)
Response.BinaryWrite(CType(objRs.Item(1), Byte()))
Response.AddHeader("Content-Disposition", "attachment;filename=" + HttpUtility.UrlEncode(objRs.Item(2)))
End IfobjDb.Close()
Response.End()
この処理の後に、LastWriteTimeでファイルに更新日を設定するのだと思うのですが、どのように指定すれば
いいのでしょうか?
みなさんのレスから、結論としては、
データベースから取り出されたバイト列データ objRs.Item(1) からファイルを
作って、> 一度サーバ上のどこかにファイルを書き込んで、更新日時を変更したものを
> zipやlhaで圧縮し、
その圧縮ファイルをバイト列データに変換し、Response.BinaryWrite メソッド
で HTTP 出力ストリームに書き込み、> クライアント側でダウンロード後、それを展開
ということになるようです。
上記でよろしいでしょうか? > 回答者のみなさん
-
trapemiya さんからの引用 RFC2183の通りに記述してもそうならないのが気になります・・・。なんでろう? ちなみにこれはIEのみの現象ではありませんでした。もうちょっとトライしてみようかな・・・
RFC 2183 の 3. Examples を見ますと、ファイルの更新日を modification-date
に設定するかどうかを選択するのは受信者であり、ブラウザが勝手に変えてしま
ってはいけないということのように思えます。The recipient's user might also choose to set the last-modified date of
the stored file to date in the modification-date parameter: -
皆さま、ご回答ありがとうございます。
さとと申します。
ヘッダで更新日を指定することが無理なようなので、
1)データベースから取り出されたバイト列データ objRs.Item(1) からサーバ上にファイルを作成。
2)更新日時を変更。
3)GZipStreamでzipに圧縮。
4)Response.WriteFileでファイルをダウンロード。
してみました。
でも、「3)GZipStreamでzipに圧縮。」した際に、圧縮前のファイル更新日が勝手に変更されてしまいます。
GZipStreamとはそういう物なのでしょうか?(解凍ソフトは「eo」を使っていますが、手動で圧縮したファイルは更新日が保持された形で解凍されるので
解凍ソフトの問題ではないようです。)
-
さと581402 さんからの引用 でも、「3)GZipStreamでzipに圧縮。」した際に、圧縮前のファイル更新日が勝手に変更されてしまいます。
GZipStreamとはそういう物なのでしょうか?どうも、少なくともデフォルトでは、そういうもののようです。
Msdn ライブラリの GZipStream クラスにあるサンプルプログラムで、
FileStream outfile = File.Create("decommpressed.doc");
outfile.Write(decompressedBuffer, 0, totalCount);を追加して、decommpressed.doc の更新日を見ると、確かに書き換えられて
いました。
何かの設定で変更しないようにすることができるかどうかまでは調べ切れていま
せんが。
-
さと581402 さんからの引用 でも、「3)GZipStreamでzipに圧縮。」した際に、圧縮前のファイル更新日が勝手に変更されてしまいます。
今頃になって気がつきました。(汗)
たぶん、FileStream で読んでくる実データのみを圧縮して送ることを考
えていると思いますが、そもそも実データにはファイル名や更新日などの
情報(メタデータ)は入ってないです。WinZip 等が作る zip アーカイブには、実データに加えてメタデータが含
まれており、解凍時にメタデータを使ってファイル名や更新日時を設定し
ているようです。という訳で、「圧縮前のファイル更新日が勝手に変更」と言うことではな
さそうです。今回の目的を果たすには、実データに加えてメタデータも一緒に、世間一
般に使われている解凍ツールと互換性のある zip アーカイブにして送っ
てやる必要があるということになります。zip アーカイブを作るには、以下のサイトで紹介されている方法が役に
立ちそうです。荒井省三のBlog: Dynamic Language SDK を使った ZIPアーカイブの使い方
http://blogs.msdn.com/shozoa/archive/2008/09/24/how-to-zip-archive-by-using-dynamic-language-sdk.aspx例えば、ここで紹介されている「フリーのライブラリを使用する(たとえ
ば、ここなど)。」のリンク↓The Zip, GZip, BZip2 and Tar Implementation For .NET
http://www.icsharpcode.net/OpenSource/SharpZipLib/Default.aspxこのライブラリを使えば、更新日時は自由に設定できますし、作った zip
ファイルは WinZip で開けることは自分も確認済みです。圧縮するだけな
らかなり簡単に出来ます(他の手段でも簡単かもしれませんが)。マイクロソフトのサンプルもありますが(下記サイトから入手できる)、
これで作った圧縮ファイルは、WinZip と互換性がないです。理由は調べ
てません(調べる前に諦めました)。Microsoft .NET Framework SDK version 2.0 Samples Download
http://www.microsoft.com/downloads/details.aspx?FamilyID=adef80f9-1e44-4b94-be24-dff48f826ce6&DisplayLang=en -
SurferOnWww さんからの引用 みなさんのレスから、結論としては、
データベースから取り出されたバイト列データ objRs.Item(1) からファイルを
作って、> 一度サーバ上のどこかにファイルを書き込んで、更新日時を変更したものを
> zipやlhaで圧縮し、
その圧縮ファイルをバイト列データに変換し、Response.BinaryWrite メソッド
で HTTP 出力ストリームに書き込み、> クライアント側でダウンロード後、それを展開
ということになるようです。
よく考えてみると、「一度サーバ上のどこかにファイルを書き込んで、更
新日時を変更」というステップは不要でした。以下のステップで可能です。
(1) データベースからバイト列データを取り出す。
(2) 既存のライブラリを利用して zip アーカイブをバイト列として作成。
更新日時やファイル名はその時設定。(3) 作成したバイト列を Response.BinaryWrite メソッドで HTTP 出力ス
トリームに書き込みご参考までに、先のレスで紹介した下記のサイトから入手できるライブラ
リを使った例をアップしておきます。The Zip, GZip, BZip2 and Tar Implementation For .NET
http://www.icsharpcode.net/OpenSource/SharpZipLib/Default.aspx実行するには Bin フォルダに ICSharpCode.SharpZipLib.dll を入れてお
く必要があります(上記のサイトから入手できます)。Code Snippet<%@ Page Language="C#" %>
<%@ Import Namespace="System.Data" %>
<%@ Import Namespace="System.Data.SqlClient" %>
<%@ Import Namespace="System.Configuration" %>
<%@ Import Namespace="System.IO" %>
<%@ Import Namespace="ICSharpCode.SharpZipLib.Zip" %><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<script runat="server">
protected void Page_Load(object sender, EventArgs e)
{
string connString =
ConfigurationManager.ConnectionStrings["databaseConnectionString"].ConnectionString;
SqlConnection connection = new SqlConnection(connString);
string query = "SELECT filename, data FROM files WHERE id=@id";
SqlCommand command = new SqlCommand(query, connection);
command.Parameters.AddWithValue("@id", Convert.ToInt32(Request.QueryString["id"]));
string filename = null;
byte[] data = null;
try
{
connection.Open();
SqlDataReader reader = command.ExecuteReader();if (reader.Read())
{
filename = (string)reader[0];
data = (byte[])reader[1];
}
}
finally
{
connection.Close();
}if (filename == null || data == null)
{
// DB から取得できなかった場合の処置(ここでは単に return)
return;
}Response.AppendHeader("Content-Disposition",
"attachment; filename=" + HttpUtility.UrlEncode(filename) + ".zip");
Response.ContentType = "application/x-zip-compressed";byte[] zippedData = new byte[data.Length]; // 圧縮前 (data.Length) よりは小さくなるはず。
int zippedDataLength = 0;
using (MemoryStream memoryStream = new MemoryStream(zippedData))
{
using (ZipOutputStream zipOutStream = new ZipOutputStream(memoryStream))
{
zipOutStream.SetLevel(9); // 圧縮度の設定。9 が最高
ZipEntry entry = new ZipEntry(filename);
entry.DateTime = new DateTime(2000, 1, 1); // ここで更新日時を設定
zipOutStream.PutNextEntry(entry);
zipOutStream.Write(data, 0, data.Length);
zipOutStream.Finish();// 圧縮後のサイズを取得。Finish() の前に置くのはダメ。zipOutStream.Length はダメ。
zippedDataLength = (int)zipOutStream.Position;
zipOutStream.Close();
}
}
Array.Resize(ref zippedData, zippedDataLength);
Response.BinaryWrite(zippedData);
Response.End();
}
</script><html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title></title>
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:Label ID="Label1" runat="server"></asp:Label>
</div>
</form>
</body>
</html> -
SurferOnWww 様
お世話になっております。
さとと申します。
皆様からいろいろな情報をお教え頂き、テストを行っていたので、ご連絡が遅くなってしまいました。
SurferOnWww 様からお教え頂いた「The Zip, GZip, BZip2 and Tar Implementation For .NET」の方法で実現しました。
何でか、解凍ファイルが異常に大きくなってしまう現象が発生したので、ネット上でいろいろ調査した結果、
「ICSharpCode.SharpZipLib.dll」のバージョンを0.85.5.452から0.84.0.0に下げたら正常に動作することができました。
ありがとうございました。