データベースに保存されているファイルを提供しています。ファイルを取得する唯一の方法は、次のようにid
を使用することです。
www.AwesomeURL.com/AwesomeSite.aspx?requestedFileId=23
WebClient Class を使用しているため、すべてがファイルとして機能しています。
私が直面している問題は1つだけです。
realファイル名を取得するにはどうすればよいですか?
私のコードは次のようになります。
WebClient client = new WebClient ();
string url = "www.AwesomeURL.com/AwesomeSite.aspx?requestedFileId=23";
client.DownloadFile(url, "IDontKnowHowToGetTheRealFileNameHere.txt");
私が知っているのはidだけです。
これは、適切な名前のブラウザからurl
にアクセスしようとしても発生しません=>DownloadedFile.xls。
正しい応答を得る適切な方法は何ですか?
同じ問題が発生し、次のクラスが見つかりました:System.Net.Mime.ContentDisposition。
using (WebClient client = new WebClient()){
client.OpenRead(url);
string header_contentDisposition = client.ResponseHeaders["content-disposition"];
string filename = new ContentDisposition(header_contentDisposition).FileName;
...do stuff...
}
クラスのドキュメントは、電子メールの添付ファイルを対象としていることを示唆していますが、テストに使用したサーバーでは正常に機能し、解析を回避するのは本当に素晴らしいことです。
サーバーがcontent-dispositionヘッダーを適用していると仮定した場合、必要な完全なコードは次のとおりです。
using (WebClient client = new WebClient())
{
using (Stream rawStream = client.OpenRead(url))
{
string fileName = string.Empty;
string contentDisposition = client.ResponseHeaders["content-disposition"];
if (!string.IsNullOrEmpty(contentDisposition))
{
string lookFor = "filename=";
int index = contentDisposition.IndexOf(lookFor, StringComparison.CurrentCultureIgnoreCase);
if (index >= 0)
fileName = contentDisposition.Substring(index + lookFor.Length);
}
if (fileName.Length > 0)
{
using (StreamReader reader = new StreamReader(rawStream))
{
File.WriteAllText(Server.MapPath(fileName), reader.ReadToEnd());
reader.Close();
}
}
rawStream.Close();
}
}
サーバーがこのヘッダーを設定しなかった場合は、デバッグを試して、使用しているResponseHeadersを確認してください。そのうちの1つに、必要な名前が含まれている可能性があります。ブラウザに名前が表示されている場合は、どこか .. :)から取得する必要があります。
次の方法でcontent-disposition
ヘッダーを確認する必要があります。
string disposition = client.ResponseHeaders["content-disposition"];
典型的な例は次のとおりです。
"attachment; filename=IDontKnowHowToGetTheRealFileNameHere.txt"
HTTP content-disposition
ヘッダーを使用して、提供するコンテンツのファイル名を提案できます。
Content-Disposition: attachment; filename=downloadedfile.xls;
したがって、AwesomeSite.aspx
スクリプトでは、content-disposition
ヘッダーを設定します。 WebClient
クラスでは、そのヘッダーを取得して、AwesomeSite
サイトの提案に従ってファイルを保存します。
私はwstのコードでこれを達成します。
これは、c:\ tempフォルダーにあるURLファイルをダウンロードするための完全なコードです。
public static void DownloadFile(string url)
{
using (WebClient client = new WebClient())
{
client.OpenRead(url);
string header_contentDisposition = client.ResponseHeaders["content-disposition"];
string filename = new ContentDisposition(header_contentDisposition).FileName;
//Start the download and copy the file to the destinationFolder
client.DownloadFile(new Uri(url), @"c:\temp\" + filename);
}
}
Shadow Wizardによって提案されたソリューションはテキストファイルに対してはうまく機能しますが、アプリケーションで画像や実行可能ファイルなどのバイナリファイルのダウンロードをサポートする必要がありました。
これがWebClient
の小さな拡張機能です。ダウンロードは非同期です。また、サーバーがすべての正しいヘッダーを送信するかどうかが実際にはわからないため、ファイル名のデフォルト値が必要です。
static class WebClientExtensions
{
public static async Task<string> DownloadFileToDirectory(this WebClient client, string address, string directory, string defaultFileName)
{
if (!Directory.Exists(directory))
throw new DirectoryNotFoundException("Downloads directory must exist");
string filePath = null;
using (var stream = await client.OpenReadTaskAsync(address))
{
var fileName = TryGetFileNameFromHeaders(client);
if (string.IsNullOrWhiteSpace(fileName))
fileName = defaultFileName;
filePath = Path.Combine(directory, fileName);
await WriteStreamToFile(stream, filePath);
}
return filePath;
}
private static string TryGetFileNameFromHeaders(WebClient client)
{
// content-disposition might contain the suggested file name, typically same as origiinal name on the server
// Originally content-disposition is for email attachments, but web servers also use it.
string contentDisposition = client.ResponseHeaders["content-disposition"];
return string.IsNullOrWhiteSpace(contentDisposition) ?
null :
new ContentDisposition(contentDisposition).FileName;
}
private static async Task WriteStreamToFile(Stream stream, string filePath)
{
// Code below will throw generously, e. g. when we don't have write access, or run out of disk space
using (var outStream = new FileStream(filePath, FileMode.CreateNew))
{
var buffer = new byte[8192];
while (true)
{
int bytesRead = await stream.ReadAsync(buffer, 0, buffer.Length);
if (bytesRead == 0)
break;
// Could use async variant here as well. Probably helpful when downloading to a slow network share or tape. Not my use case.
outStream.Write(buffer, 0, bytesRead);
}
}
}
}