.NET TIPS

サーバにより指定されたファイル名でファイルをダウンロードして保存するには?[C#、VB]

デジタルアドバンテージ 遠藤 孝信
2007/07/12

 Webからファイルをダウンロードする際、Webサイトによってはダウンロードするファイルの名前がサーバ側で設定されている場合がある。これは通常、サーバ側でHTTPヘッダに次のような文字列(Content-Dispositionヘッダ)を追加することにより行われる。

Content-Disposition: attachment; filename="<ファイル名>"
Content-Dispositionヘッダの記述例
Content-Dispositionヘッダの仕様についてはRFC 2183(英文)を参照。

 このような設定が行われているファイルを例えばIEでダウンロードする場合、ローカルに保存されるファイルには<ファイル名>部分で指定された名前が自動的に使用されるようになっている。

 しかし、プログラムで例えばWebClientクラス(System.Net名前空間)のDownloadFileメソッドを使用してダウンロードする場合(「TIPS:WebClientクラスでWebページを取得するには?」を参照)には、メソッドのパラメータとして、ダウンロードするファイルのURLとともに、そのファイルの保存時の名前を指定しなければならず、サーバ側で指定されたファイル名は利用できない。

サーバにより指定されたファイル名の取得

 ファイルをダウンロードするときにサーバ側で指定されたファイル名で保存するには、HttpWebRequestクラス/HttpWebResponseクラス(System.Net名前空間)を使用し(「TIPS:WebRequest/WebResponseクラスでWebページを取得するには?」を参照)、ダウンロードする前に上記のようなContent-Dispositionヘッダの内容を読み取り、ファイル名部分を独自に取得すればよい。

 具体的には、ダウンロード時のレスポンス(HttpWebResponseオブジェクト)からHeadersプロパティによりContent-Dispositionヘッダの内容を取得し、そこから「filename=」に続く文字列を切り出してファイル名とする。このような処理を行うサンプル・プログラムを次に示す。

指定された名前でファイルをダウンロードするサンプル・プログラム

 このサンプル・プログラムは、コマンドラインのオプションで指定されたURLからファイルをダウンロードして保存する(Content-Dispositionヘッダによりファイル名が指定されていない場合には、ファイル名を「download.tmp」とする)。

// downnoname.cs

using System;
using System.IO;
using System.Net;
using System.Text.RegularExpressions;

class DownloadWithNoName {
  static void Main(string[] args) {

    if (args.Length == 0)
      return;
    string url = args[0];

    HttpWebRequest req = (HttpWebRequest)WebRequest.Create(url);
    HttpWebResponse res = (HttpWebResponse)req.GetResponse();

    string fileName = "download.tmp";

    // ヘッダ情報からファイル名の取得
    string dispos = res.Headers["Content-Disposition"];

    if (!String.IsNullOrEmpty(dispos)) {

      // filename=<ファイル名>の抜き出し
      Regex re = new Regex(@"
        filename\s*=\s*
        (?:
          ""(?<filename>[^""]*)""
          |
          (?<filename>[^;]*)
        )
        ", RegexOptions.IgnoreCase
         | RegexOptions.IgnorePatternWhitespace);

      Match m = re.Match(dispos);
      if (m.Success) {
        fileName = m.Groups["filename"].Value;
      }
    }

    // ファイルのダウンロード
    using (Stream st = res.GetResponseStream())
    using (FileStream fs = new FileStream(fileName, FileMode.Create)) {
      Byte[] buf = new Byte[1024];
      int count = 0;
      do {
        count = st.Read(buf, 0, buf.Length);
        fs.Write(buf, 0, count);
      } while (count != 0);
    }
    res.Close();
  }
}

// コンパイル方法:csc downnoname.cs
ファイルをダウンロードするC#のサンプル・プログラム(downnoname.cs)

' downnoname.vb

Imports System
Imports System.IO
Imports System.Net
Imports System.Text.RegularExpressions

Class DownloadWithNoName
  Shared Sub main(ByVal args As String())

    If args.Length = 0 Then
      Return
    End If
    Dim url As String = args(0)

    Dim req As HttpWebRequest _
      = CType(WebRequest.Create(url), HttpWebRequest)
    Dim res As HttpWebResponse _
      = CType(req.GetResponse(), HttpWebResponse)

    Dim fileName As String = "download.tmp"

    ' ヘッダ情報からファイル名の取得
    Dim dispos As String = res.Headers("Content-Disposition")

    If Not String.IsNullOrEmpty(dispos) Then

      ' filename=<ファイル名>の抜き出し
      Dim re As New Regex( _
        "filename\s*=\s*           " & _
        "(?:                       " & _
        "  ""(?<filename>[^""]*)"" " & _
        "  |                       " & _
        "  (?<filename>[^;]*)      " & _
        ")                         " _
        , RegexOptions.IgnoreCase _
        Or RegexOptions.IgnorePatternWhitespace)

      Dim m As Match = re.Match(dispos)
      If m.Success Then
        fileName = m.Groups("filename").Value
      End If
    End If

    ' ファイルのダウンロード
    Using st As Stream = res.GetResponseStream()
      Using fs As New FileStream(fileName, FileMode.Create)
        Dim buf(1024) As Byte
        Dim count As Integer = 0
        Do
          count = st.Read(buf, 0, buf.Length)
          fs.Write(buf, 0, count)
        Loop While count <> 0
      End Using
    End Using
    res.Close()
  End Sub
End Class

' コンパイル方法:vbc downnoname.vb
ファイルをダウンロードするVBのサンプル・プログラム(downnoname.vb)

 Content-Dispositionヘッダの「filename=」に続くファイル名部分は、ダブル・クオーテーションで囲まれている場合とそうでない場合があるため、ここでは正規表現を使用して必要な個所を抜き出している(正規表現のパターン内での改行やRegexOptions.IgnorePatternWhitespaceについては「TIPS:正規表現のパターン内にコメント文を記述するには?」を参照)。なお、「filename="<ファイル名>"」は通常、Content-Dispositionヘッダの末尾に記述されるようである。End of Article

カテゴリ:クラス・ライブラリ 処理対象:ネットワーク
使用ライブラリ:HttpWebRequestクラス(System.Net名前空間)
使用ライブラリ:HttpWebResponseクラス(System.Net名前空間)
関連TIPS:WebClientクラスでWebページを取得するには?
関連TIPS:WebRequest/WebResponseクラスでWebページを取得するには?
関連TIPS:正規表現のパターン内にコメント文を記述するには?

この記事と関連性の高い別の.NET TIPS
ファイルのダウンロードをレジューム(再開)するには?
[ASP.NET]データベースの内容をクライアントにダウンロード提供するには?
Webサーバのプロトコル違反による例外を回避するには?
WebClientクラスでWebページを取得するには?
ファイルをダウンロードさせるには?[JavaScript]
このリストは、(株)デジタルアドバンテージが開発した
自動関連記事探索システム Jigsaw(ジグソー) により自動抽出したものです。
generated by

「.NET TIPS」

@IT Special

- PR -

TechTargetジャパン

Insider.NET フォーラム 新着記事
  • 第2回 簡潔なコーディングのために (2017/7/26)
     ラムダ式で記述できるメンバの増加、throw式、out変数、タプルなど、C# 7には以前よりもコードを簡潔に記述できるような機能が導入されている
  • 第1回 Visual Studio Codeデバッグの基礎知識 (2017/7/21)
     Node.jsプログラムをデバッグしながら、Visual Studio Codeに統合されているデバッグ機能の基本の「キ」をマスターしよう
  • 第1回 明瞭なコーディングのために (2017/7/19)
     C# 7で追加された新機能の中から、「数値リテラル構文の改善」と「ローカル関数」を紹介する。これらは分かりやすいコードを記述するのに使える
  • Presentation Translator (2017/7/18)
     Presentation TranslatorはPowerPoint用のアドイン。プレゼンテーション時の字幕の付加や、多言語での質疑応答、スライドの翻訳を行える
@ITメールマガジン 新着情報やスタッフのコラムがメールで届きます(無料)
- PR -

イベントカレンダー

PickUpイベント

- PR -

アクセスランキング

もっと見る

ホワイトペーパーTechTargetジャパン

注目のテーマ

Insider.NET 記事ランキング

本日 月間
ソリューションFLASH