Webからファイルをダウンロードする際、Webサイトによってはダウンロードするファイルの名前がサーバ側で設定されている場合がある。これは通常、サーバ側でHTTPヘッダに次のような文字列(Content-Dispositionヘッダ)を追加することにより行われる。
Content-Disposition: attachment; filename="<ファイル名>"
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
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
downnoname.vbのダウンロード
Content-Dispositionヘッダの「filename=」に続くファイル名部分は、ダブル・クオーテーションで囲まれている場合とそうでない場合があるため、ここでは正規表現を使用して必要な個所を抜き出している(正規表現のパターン内での改行やRegexOptions.IgnorePatternWhitespaceについては「TIPS:正規表現のパターン内にコメント文を記述するには?」を参照)。なお、「filename="<ファイル名>"」は通常、Content-Dispositionヘッダの末尾に記述されるようである。
カテゴリ:クラス・ライブラリ 処理対象:ネットワーク
使用ライブラリ:HttpWebRequestクラス(System.Net名前空間)
使用ライブラリ:HttpWebResponseクラス(System.Net名前空間)
関連TIPS:WebClientクラスでWebページを取得するには?
関連TIPS:WebRequest/WebResponseクラスでWebページを取得するには?
関連TIPS:正規表現のパターン内にコメント文を記述するには?
Copyright© Digital Advantage Corp. All Rights Reserved.