asp.net 下载文件时根据MIME类型自动判断保存文件的扩展名

引言

用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类型列表,并通过正则表达式将其转换为程序代码,粘入了程序中:
 
这个通过正则表达式转换而来的代码量非常大。
需注意的是,其中有很多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/

(0)

相关推荐

  • ASP.NET(C#) Web Api通过文件流下载文件的实例

    下载文件到本地是很多项目开发中需要实现的一个很简单的功能.说简单,是从具体的代码实现上来说的,.NET的文件下载方式有很多种,本示例给大家介绍的是ASP.NET Web Api方式返回HttpResponseMessage下载文件到本地.实现的方法很简单,其中就是读取服务器的指定路径文件流,将其做为返回的HttpResponseMessage的Content.直接贴出DownloadController控件器的代码: using System; using System.Collections.

  • ASP.NET实现从服务器下载文件问题处理

    假设在服务器的根目录下有个名为Download的文件夹,这个文件夹存放一些提供给引用程序下载的文件 public void DownloadFile(string path, string name){ try{ System.IO.FileInfo file = new System.IO.FileInfo(path); Response.Clear(); Response.Charset = "GB2312"; Response.ContentEncoding = System.T

  • ASP.NET中下载文件的几种实例代码

    复制代码 代码如下: //TransmitFile实现下载     protected void Button1_Click(object sender, EventArgs e)    {        /*         微软为Response对象提供了一个新的方法TransmitFile来解决使用Response.BinaryWrite         下载超过400mb的文件时导致Aspnet_wp.exe进程回收而无法成功下载的问题.         代码如下:         */

  • asp.net C#实现下载文件的六种方法实例

    复制代码 代码如下: protected void Button1_Click(object sender, EventArgs e)  {  /*  微软为Response对象提供了一个新的方法TransmitFile来解决使用Response.BinaryWrite  下载超过400mb的文件时导致Aspnet_wp.exe进程回收而无法成功下载的问题.  代码如下:  */ Response.ContentType = "application/x-zip-compressed"

  • ASP.NET 在下载文件时对其重命名的思路及实现方法

    有些时候为了保证文件再上传时不会覆盖掉之前上传的文件,同时由于上传的目标目录里的文件可能很多,这个时候一个一个查是不太好的事情,所以这里可以自动生成GUID使文件名重命名成GUID_原来的名称.扩展名.但是在下载的时候最好可能保证恢复到原来的名称.这个时候听伤神的.搜了一下相关资料后得知可使用response来解决.具体代码如下. [csharp]  复制代码 代码如下: <pre name="code" class="csharp">string pa

  • ASP.NET批量下载文件的方法

    本文实例讲述了ASP.NET批量下载文件的方法.分享给大家供大家参考.具体方法如下: 一.实现步骤 在用户操作界面,由用户选择需要下载的文件,系统根据所选文件,在服务器上创建用于存储所选文件的临时文件夹,将所选文件拷贝至临时文件夹.然后调用 RAR程序,对临时文件夹进行压缩,然后输出到客户端.最后删除临时文件夹.   二.代码实现   1.ASP.NET批量下载 核心代码 复制代码 代码如下: //遍历服务器指定文件夹下的所有文件 string path = "uploads/Image/&qu

  • asp.net 下载文件时输出文件内容

    复制代码 代码如下: <script language="C#" runat="server"> protected void Page_Load(object sender, System.EventArgs e) { Response.ContentType = "application/x-shockwave-flash"; string fileFlash = Server.MapPath("/") + &

  • ASP.NET Web Api 2实现多文件打包并下载文件的实例

    最近由于工作和个人事务,站点也好久没更新了,但这并不影响我对.NET的热情.站点的更新工作还是得想办法抽时间来完成的. 今天利用中午的时间来写一篇关于Asp.Net Web Api下载文件的文章,之前我也写过类似的文章,请见:<ASP.NET(C#) Web Api通过文件流下载文件的实例> 本文以这篇文章的基础,提供了ByteArrayContent的下载以及在下载多个文件时实现在服务器对多文件进行压缩打包后下载的功能. 关于本文中实现的在服务器端用.NET压缩打包文件功能的过程中,使用到了

  • asp.net BackgroundWorker之在后台下载文件

    示例: 下面的代码示例演示如何使用 BackgroundWorker 组件从 URL 加载 XML 文件.用户单击"下载"按钮时,Click 事件处理程序将调用 BackgroundWorker 组件的 RunWorkerAsync 方法来启动下载操作.在下载过程中,将禁用该按钮,然后在下载完成后再启用该按钮.MessageBox 将显示文件的内容. 复制代码 代码如下: using System; using System.Collections.Generic; using Sys

  • 在ASP.NET中下载文件的实现代码

    这是笔者常被问到的一个问题,如何通过ASP.NET来下载文件,这个问题可大可小,我们先从小的开始.当我们要让用户下载一个文件,最简单的方式是通过Response.Redirect指令: Response.Redirect("test.doc") 您可以把上面这行指令放在Button的Click事件当中,当用户点击按钮之后,网页就会被转址到该word档,造成下载的效果. 但是这样的下载有几个问题: 1. 无法下载不存在的文件:例如,我们若是想把程序动态(临时)产生的文字,当作一个文件下载

  • asp.net Web Services上传和下载文件(完整代码)第1/2页

    下面,我们就分别介绍如何通过Web Services从服务器下载文件到客户端和从客户端通过Web Services上载文件到服务器.一:通过Web Services显示和下载文件 我们这里建立的Web Services的名称为GetBinaryFile,提供两个公共方法:分别是GetImage()和GetImageType(),前者返回二进制文件字节数组,后者返回文件类型,其中,GetImage()方法有一个参数,用来在客户端选择要显示或下载的文件名字.这里我们所显示和下载的文件可以不在虚拟目录

随机推荐