引言
用WebClient下載遠端資源時,經常會遇到類似這樣的網址:
http://www.uushare.com/filedownload?user=icesee&id=2205188
http://www.guaishow.com/u/luanfujie/g9675/
我們不知道這個Url具體代表的是一個網頁,還是某種類型的檔案。
而有些Url雖然帶有副檔名,但可能是錯誤的副檔名,常見的比如把gif檔案標上了jpg副檔名。
如果我們沒法正確判斷下載源的檔案類型的話,就無法儲存為正確的檔案格式,會給後續操作及人工閱覽造成困擾。
所幸的是,WebRequest可以給出下載源的MIME資訊,這讓我們可以確定檔案的真實格式,並以此來決定最終的儲存副檔名。(MIME是什嗎?)
建立MIME映射字典
我們首先需要做的工作就是建立一個MIME類型到其對應副檔名的映射字典。
我從網上找來了一個MIME類型列表,並通過Regex將其轉換為程式碼,粘入了程式中:
這個通過Regex轉換而來的代碼量非常大。
需注意的是,其中有很多MIME類型相同但副檔名不同的資料,我們在添加到字典時就將多餘的不必要記錄忽略了,比如高亮處的那三條都是audio/x-aiff類型,那麼後兩個副檔名都不會添加到字典中,也不會在後續的操作中被使用。
如果你覺得有些類型添加的對應副檔名不是最常見的對應類型的話,就得手動調整代碼了。(下文中就出現了這種情況,如text/html對應的是dhtml副檔名,image/jpeg對應的是jpe副檔名)
字典構建完畢之後,就可以通過這樣一個方法來擷取MIME類型所對應的副檔名了:
string 擷取對應副檔名(string ContentType)
{
foreach (var f in MimeDic.Keys)
{
if (ContentType.ToLower().IndexOf(f) >= 0) return MimeDic[f];
}
return null;
}
這裡之所以使用IndexOf方法判斷,是因為傳入的ContentType中可能還包含其他資訊,比如編碼格式。
題外話:看到網上曾有人抱怨說WebClient下載網頁時容易產生亂碼,而且又不好讀取網頁的編碼格式,其實WebRequest的ContentType中就包含MIME和編碼格式資訊:
產生下載檔案路徑
現在有了上面的方法,我們就可以通過MIME類型確定檔案的副檔名了。
現在我們將書寫一個用於產生下載檔案路徑的方法,其功能為:
分析檔案的源Url,將其檔案名稱部分作為下載檔案的檔案名稱。
如果其Url中不含檔案名稱部分(網域名稱或目錄形式),則以其目錄名為下載檔案的檔案名稱。
根據傳入的MIME類型自動確定並替換Url中的原始副檔名(如果有的話),以用作下載檔案的檔案名稱。
判斷傳入的儲存目錄中是否已存在與下載檔案名稱相同的檔案,存在的話就進行重新命名,直到沒有同名檔案為止。
功能有點多了,不適合做範例,不過還是很實用的,所以這裡就順道分享出來。
其代碼為:
複製代碼 代碼如下:string 產生下載檔案存放路徑(string 存放目錄, Uri Uri, string ContentType)
{
var ex = 擷取對應副檔名(ContentType);
string up = null;
string upne = null;
if (Uri.LocalPath == "/")
{
//處理Url是網域名稱的情況
up = upne = Uri.Host;
}
else
{
if (Uri.LocalPath.EndsWith("/"))
{
//處理Url是目錄的情況
up = Uri.LocalPath.Substring(0, Uri.LocalPath.Length - 1);
upne = Path.GetFileName(up);
}
else
{
//處理常規Url
up = Uri.LocalPath;
upne = Path.GetFileNameWithoutExtension(up);
}
}
var name = string.IsNullOrEmpty(ex) ? Path.GetFileName(up) : upne + "." + ex;
var fn = Path.Combine(存放目錄, name);
var x = 1;
while (File.Exists(fn))
{
fn = Path.Combine(存放目錄, Path.GetFileNameWithoutExtension(name) + "(" + x++ + ")" + Path.GetExtension(name));
}
return fn;
}
為了驗證其效果,我們通過一個單元測試進行評測: 複製代碼 代碼如下:[TestMethod]
public void 檔案名稱產生測試()
{
var d = @"C:\Users\Public\Downloads";
//gif格式檔案,正常下載
Assert.AreEqual(@"C:\Users\Public\Downloads\35ad5275ed17904d4a2d40f3dacea80b.gif", 產生下載檔案存放路徑(d, new Uri("/upload/2009-11/20091112231022422.gif"), "image/gif"));
//url中副檔名是gif,但MIME類型實際是image/jpeg的資源。下載後的副檔名是jpe,因為字典MimeDic裡儲存的對應副檔名就是jpe。
Assert.AreEqual(@"C:\Users\Public\Downloads\35ad5275ed17904d4a2d40f3dacea80b.jpe", 產生下載檔案存放路徑(d, new Uri("/upload/2009-11/20091112231022422.gif"), "image/jpeg"));
//一個帶參數的網頁url。下載後的副檔名是dhtml,因為字典MimeDic裡儲存的對應副檔名就是dhtml。
Assert.AreEqual(@"C:\Users\Public\Downloads\filedownload.dhtml", 產生下載檔案存放路徑(d, new Uri("http://www.uushare.com/filedownload?user=icesee&id=2205188"), "text/html"));
//一個網頁url,其格式為目錄形式的,沒有確切檔案名稱。
Assert.AreEqual(@"C:\Users\Public\Downloads\g9675.dhtml", 產生下載檔案存放路徑(d, new Uri("http://www.guaishow.com/u/luanfujie/g9675/"), "text/html"));
//網域名稱形式
Assert.AreEqual(@"C:\Users\Public\Downloads\www.g.cn.dhtml", 產生下載檔案存放路徑(d, new Uri("http://www.g.cn/"), "text/html"));
Assert.AreEqual(@"C:\Users\Public\Downloads\g.cn.dhtml", 產生下載檔案存放路徑(d, new Uri("http://g.cn"), "text/html"));
}
檔案下載
萬事俱備,只欠東風了,讓我們來完成下載方法: 複製代碼 代碼如下:/// <summary>
/// 下載檔案到指定目錄,並返回下載後存放的檔案路徑
/// </summary>
/// <param name="Uri">網址</param>
/// <param name="存放目錄">存放目錄,如果該目錄中已存在與待下載檔案同名的檔案,那麼將自動重新命名</param>
/// <returns>下載檔案存放的檔案路徑</returns>
public string 下載檔案(Uri Uri, string 存放目錄)
{
var q = WebRequest.Create(Uri).GetResponse();
var s = q.GetResponseStream();
var b = new BinaryReader(s);
var file = 產生下載檔案存放路徑(存放目錄, Uri, q.ContentType);
FileStream fs = new FileStream(file, FileMode.Create, FileAccess.Write);
fs.Write(b.ReadBytes((int)q.ContentLength), 0, (int)q.ContentLength);
fs.Close();
b.Close();
s.Close();
return file;
}
代碼很簡單,就不多說了,我們來完成最後的測試: 複製代碼 代碼如下:[TestMethod]
public void 檔案下載測試()
{
var d = @"C:\Users\Public\Downloads";
//首次下載
Assert.AreEqual(@"C:\Users\Public\Downloads\filedownload.dhtml", 下載檔案(new Uri("http://www.uushare.com/filedownload?user=icesee&id=2205188"), d));
//第二次下載,遇到同名檔案,自動重新命名
Assert.AreEqual(@"C:\Users\Public\Downloads\filedownload(1).dhtml", 下載檔案(new Uri("http://www.uushare.com/filedownload?user=icesee&id=2205188"), d));
//下載一個原本是gif類型的檔案
Assert.AreEqual(@"C:\Users\Public\Downloads\2naqyw8.gif", 下載檔案(new Uri("http://i38.tinypic.com/2naqyw8.jpg"), d));
}
結語
相較WebClient而言,WebRequest擁有更好的可控性,在WebClient無解的時候,就嘗試讓WebRequest上場吧。
範例原始碼和本文的XPS版本打包下載
http://xiazai.jb51.net/200911/yuanma/asp.net_mime_down.rar
轉載http://skyd.cnblogs.com/