WebSurfer's Home

トップ > Blog 1   |   ログイン
APMLフィルター

ダウンロードしたファイルの更新日時

by WebSurfer 2010年8月30日 16:21

クライアントがサーバーからファイルをダウンロードし、自分の HDD などに保存した場合、そのファイルの更新日時は HDD に保存した日時になります。

ファイルのダウンロード

これをサーバー側で設定した日付にすることはできるでしょうか? ActiveX などのプラグインは使わないという条件でです。

結論から言えば、世間一般に使われている解凍ツールと互換性のある zip アーカイブを使えば可能です。

サーバーからは zip アーカイブに更新日時情報を含めて送信し、クライアント側で解凍して保存する時に zip アーカイブに含まれている更新日時に設定してもらうということです。

Web アプリケーションとしては、以下ような手順になります。

  1. データベースまたはファイルシステムからバイト列データを取り出す。
  2. 既存のライブラリを利用して zip アーカイブをバイト列として作成。更新日時やファイル名はその時設定。
  3. 作成したバイト列を Response.BinaryWrite メソッドで HTTP 出力ストリームに書き込み

参考に、フリーのライブラリ SharpZipLib を使った例をアップしておきます。実行するには Bin フォルダに ICSharpCode.SharpZipLib.dll を入れておく必要があります。下記のサイトから入手できます。

The Zip, GZip, BZip2 and Tar Implementation For .NET

<%@ 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)
  {
    // 例として、SQL Server DB からデータを取得する
    // ことで考えています。
    ConnectionStringSettings cs =
      ConfigurationManager.ConnectionStrings["MyDB"];
    string connString = cs.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";

    // 何故かバイト列のサイズを指定した方が圧縮率
    // が高くなる。圧縮前よりは小さくなるはずなの
    // で、とりあえず data.Length とする。
    byte[] zippedData = new byte[data.Length];
    int zippedDataLength = 0;
    using (MemoryStream memoryStream = 
      new MemoryStream(zippedData))
    {
      using (ZipOutputStream zipOutStream = 
        new ZipOutputStream(memoryStream))
      {
        // 圧縮度の設定。9 が最高
        zipOutStream.SetLevel(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() の前に置く
        // のは NG。zipOutStream.Length は NG。
        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>
  </div>
  </form>
</body>
</html>

そもそも、ファイル名、更新日時その他のメタデータはファイルシステムが保持しているもので、ファイル本体には含まれていません。

ファイル名のみは、ブラウザが自動的に attachment; filename= に指定された名前で保存してくれますが(日本語の文字化けの問題はありますが)、その他の情報はダメなようです。

Content-Disposition ヘッダーフィールドにファイル名、作成日時、更新日時等の情報を追加してクライアントに送ることは可能です。でもそれは送る手段が用意されているというだけで、ブラウザが自動的にそれに書き換えてくれることはありません。

Tags: , ,

Upload Download

About this blog

2010年5月にこのブログを立ち上げました。主に ASP.NET Web アプリ関係の記事です。

Calendar

<<  2024年4月  >>
31123456
78910111213
14151617181920
21222324252627
2829301234
567891011

View posts in large calendar