欢迎光临
我们一直在努力

Silverlight以及Mvc最佳文件下载解决方案(附源码)

(一)前言

 

目前,在Silverlight中下载文件通常采用两种方式进行文件下载:

1、客户端通过SaveFileDialog类进行文件下载,服务端使用字节数组(byte[])进行数据传递。

2、客户端通过访问服务端的一般处理文件(.ashx)来进行文件下载。

 

对于第1种方式下载,缺陷主要为:点击下载之后,弹出的SaveFileDialog对话框居然没有文件名!!!(必须自己手写文件名,这里Silverlight还有待提高)。Silverlight中的SaveFileDialog相关属性和方法如下:

 1     public sealed class SaveFileDialog
 2     {
 3          public SaveFileDialog();
 4          public string DefaultExt { getset; }
 5          public string Filter { getset; }
 6          public int FilterIndex { getset; }
 7          public string SafeFileName { get; }
 8          public Stream OpenFile();
 9          public bool? ShowDialog();
10     }

 

对于第2种方式下载的话,容易暴露相关的信息(处理文件页面有时直接在地址栏显示相关的信息)。

Silverlight主要通过HtmlPage.Window.Navigate(new Uri(url));来访问一般处理文件,一般处理文件执行文件下载(response来执行);

 

到目前为止,开发华为悍马项目已经半年多了,主要以MVC和Silverlight进行开发。因此,针对于当前的项目,本人试图以Silverlight调用Mvc action来进行下载,如下的内容都将围绕该主题进行讲解(目前这个还木有更新到项目中,仅仅是本人笔记本上设计的)。

 

(二)相关类图以及FileDownloadResult

 

Silverlight以及Mvc最佳文件下载解决方案(附源码)

 

在MVC中,Action主要以ActionResult来作为返回结果,然后调用ActionResult的ExecuteResult()方法来执行相关操作。然而,到目前为止关于文件操作的ActionResult主要为FileStreamResult、FileContentResult以及FilePathResult,这些都不太方便使用(对于文件下载来说)。因此本人打算以FileDownloadResult类来进行文件下载的相关操作。

 

(三)具体实现

 

1、FileDownloadResult类的具体实现

FileDownloadResult类主要是实现抽象类ActionResult的ExecuteResult(ControllerContext context)方法,具体代码如下:

 1     public class FileDownloadResult : ActionResult
 2     {
 3         public FileDownloadResult(string filefullpath)
 4         {
 5             this.FileFullPath = fileFullPath;
 6         }
 7 
 8         public FileDownloadResult(string fileName, string fileFullPath)
 9         {
10             this.FileName = fileName;
11             this.FileFullPath = fileFullPath;
12         }
13 
14         public string FileName 
15         {
16             get
17             private set;
18         }
19 
20         public string FileFullPath 
21         {
22             get;
23             private set
24         }
25 
26         public override void ExecuteResult(ControllerContext context)
27         {
28             if (context == null || (!File.Exists(this.FileFullPath)))
29             {
30                 return;
31             }
32 
33             FileInfo fileInfo = new FileInfo(this.FileFullPath);
34             SetFileName(fileInfo);
35             SetResponse(context.HttpContext.Response);
36             OutputFile(context.HttpContext.Response, fileInfo);
37         }
38 
39         private void SetFileName(FileInfo fileInfo)
40         {
41             if (string.IsNullOrWhiteSpace(this.FileName))
42             {
43                 this.FileName = fileInfo.Name;
44             }
45         }
46 
47         private static void OutputFile(HttpResponseBase response, FileInfo fileInfo)
48         {
49             response.WriteFile(fileInfo.FullName, 0, fileInfo.Length);
50             response.Flush();
51             response.End();
52         }
53 
54         private void SetResponse(HttpResponseBase response)
55         {
56             SetResponseState(response);
57             SetResponseHead(response);
58             SetResponseContent(response);
59         }
60 
61         private static void SetResponseState(HttpResponseBase response)
62         {
63             response.ClearHeaders();
64             response.Clear();
65             response.Expires = 0;
66             response.Buffer = true;
67         }
68 
69         private static void SetResponseContent(HttpResponseBase response)
70         {
71             response.ContentEncoding = Encoding.UTF8;
72             response.ContentType = "Application/octet-stream";
73         }
74 
75         private void SetResponseHead(HttpResponseBase response)
76         {
77             response.HeaderEncoding = Encoding.UTF8;
78             response.AddHeader("Content-Disposition""attachment;filename=" +
79                 HttpUtility.UrlEncode(this.FileName, Encoding.UTF8).Replace("+"" "));
80         }
81     }

 

主要的要点如下:

 (1) 第28行       if (context == null || (!File.Exists(this.FileFullPath)))    ---->主要为了避免异常发生而进行的防御性编码。

 (2) 第34行       SetFileName(fileInfo);    ---->如果文件名FileName不存在,则获取文件完整路径的具体文件名称。此处主要是设置下载对话框的文件名称,可以解决Silverlight中SaveFileDialog不能设置文件名称的缺陷。具体的设置文件名称到下载对话框为如下的77-79的代码:

        77           response.HeaderEncoding = Encoding.UTF8;
        78           response.AddHeader("Content-Disposition""attachment;filename=" +
        79                 HttpUtility.UrlEncode(this.FileName, Encoding.UTF8).Replace("+"" "));
(3)第72行         response.ContentType = "Application/octet-stream";    ----->主要解决文件下载类型的问题。

 

以上的这些方法重构后代码度量的可维护性指数为81,Silverlight以及Mvc最佳文件下载解决方案(附源码),基本上达到代码质量的要求了。

 

2、Mvc Download Action的实现

 1     public class FileController : Controller
 2     {
 3         public ActionResult Download(string filePath)
 4         {
 5             if (!System.IO.File.Exists(filePath))
 6             { 
 7                return RedirectToAction("FileNotFound""Error");
 8             }
 9 
10             return new FileDownloadResult(filePath);
11         }
12     }

 对于文件下载,调用的方式很简单,实例化FileDownloadResult即可。

 5             if (!System.IO.File.Exists(filePath))
 6             { 
 7                return RedirectToAction("FileNotFound""Error");
 8             }
主要是对传入的文件地址的防御性的编码,对传入的空值、NULL值以及不存在的文件进行验证(后续的单元测试可以查看相关测试)。

 

3、Silverlight中访问Mvc的Download Action

1         void btnDownload_Click(object sender, RoutedEventArgs e)
2         {
3             string url = @"http://localhost:2429/File/Download?FilePath=" + "E:\\图片操作源码.txt";
4             HtmlPage.Window.Navigate(new Uri(url));
5         }

 

 4、关于Silverlight中SaveFileDialog下载,服务端获取文件字节数组的代码如下(以下的代码为本人笔记本上的代码,比项目中自己以前写的那个更简洁):

 1     public class FileHelper
 2     {
 3         public static byte[] LoadFileBytes(string fileFullName)
 4         {
 5             if (!File.Exists(fileFullName))
 6             {
 7                 return new byte[0];
 8             }
 9 
10             try
11             {
12                 return ConvertToBytes(fileFullName);
13             }
14             catch
15             {
16                 return new byte[0];
17             }
18         }
19 
20         private static byte[] ConvertToBytes(string fileFullName)
21         {
22             using (FileStream fileStream = File.OpenRead(fileFullName))
23             {
24                 return CopyToArray(fileStream);
25             }
26         }
27 
28         private static byte[] CopyToArray(FileStream fileStream)
29         {
30             using (MemoryStream memoryStream = new MemoryStream())
31             {
32                 fileStream.CopyTo(memoryStream, (int)fileStream.Length);
33                 return memoryStream.ToArray();
34             }
35         }
36     }

 

 (三)单元测试

 

1、FileDownloadResult的单元测试代码:

 1     [TestClass()]
 2     public class FileDownloadResultTest
 3     {
 4         /// <summary>
 5         /// ExecuteResult 的测试。
 6         ///</summary>
 7         [TestMethod()]
 8         public void ExecuteResultTest()
 9         {
10             string fileFullPath = @"E:\TempTestFile.txt";
11             CreateFile(fileFullPath);
12 
13             FileController controller = new FileController();
14             ExecuteResult(fileFullPath, controller);
15 
16             HttpResponseBase response=controller.ControllerContext.HttpContext.Response;
17 
18             Assert.IsNotNull(controller.ControllerContext);
19             Assert.IsNotNull(response);
20 
21             Assert.IsTrue(response.Buffer);
22             Assert.AreEqual(0, response.Expires);
23             Assert.IsTrue(string.Equals(response.ContentType, "Application/octet-stream"));
24             Assert.AreEqual(response.ContentEncoding, Encoding.UTF8);
25             Assert.AreEqual(response.HeaderEncoding, Encoding.UTF8);
26 
27             DeleteFile(fileFullPath);
28         }
29 
30         /// <summary>
31         /// 当参数异常时,ExecuteResult 的测试。
32         ///</summary>
33         [TestMethod()]
34         public void ExecuteResultWithAbnormalArgTest() 
35         {
36             string fileFullPath = @"E:\TempTestNonExsitingFile.txt";
37             FileController controller = new FileController();
38             ExecuteResult(fileFullPath, controller);
39 
40             HttpResponseBase response = controller.ControllerContext.HttpContext.Response;
41 
42             Assert.IsNotNull(controller.ControllerContext);
43             Assert.IsNotNull(response);
44         }
45 
46         /// <summary>
47         /// 当参数为null或者empty时,ExecuteResult 的测试。
48         ///</summary>
49         [TestMethod()]
50         public void ExecuteResultWithNullOrEmptyArgTest() 
51         {
52             FileController controller = new FileController();
53             ExecuteResult(null, controller);
54 
55             HttpResponseBase response = controller.ControllerContext.HttpContext.Response;
56 
57             Assert.IsNotNull(controller.ControllerContext);
58             Assert.IsNotNull(response);
59         }
60 
61         private void DeleteFile(string fileFullPath)
62         {
63             if (File.Exists(fileFullPath))
64             {
65                 File.Delete(fileFullPath);
66             }
67         }
68 
69         private void CreateFile(string fileFullPath)
70         {
71             if (!File.Exists(fileFullPath))
72             {
73                 using (FileStream fileStream = File.Create(fileFullPath))
74                 {
75                 }
76             }
77         }
78 
79         private static void ExecuteResult(string fileFullPath, FileController controller)
80         {
81             FileDownloadResult target = new FileDownloadResult(fileFullPath);
82             MvcContextHelper.SetControllerContext(controller);
83             target.ExecuteResult(controller.ControllerContext);
84         }      
85     }

 

 对于69-77行的代码,其中73-75没有做任何操作,仅仅是释放掉资源而已,避免异常的发生:

69         private void CreateFile(string fileFullPath)
70         {
71             if (!File.Exists(fileFullPath))
72             {
73                 using (FileStream fileStream = File.Create(fileFullPath))
74                 {
75                 }
76             }
77         }

 

以上的测试涉及到ControllerContext 的模拟,因此这里采用Moq来进行测试,相关代码如下:

 1     public class MvcContextHelper
 2     {
 3         public static HttpContextBase SetHttpContext()
 4         {
 5             var context = new Mock<HttpContextBase>();
 6             var request = new Mock<HttpRequestBase>();
 7             var response = new Mock<HttpResponseBase>();
 8             var session = new Mock<HttpSessionStateBase>();
 9             var server = new Mock<HttpServerUtilityBase>();
10 
11             request.Setup(r => r.Form).Returns(new NameValueCollection());
12             request.Setup(r => r.QueryString).Returns(new NameValueCollection());
13             context.Setup(ctx => ctx.Request).Returns(request.Object);
14             context.Setup(ctx => ctx.Response).Returns(response.Object);
15             context.Setup(ctx => ctx.Response.Headers).Returns(new NameValueCollection());
16             context.Setup(ctx => ctx.Session).Returns(session.Object);
17             context.Setup(ctx => ctx.Server).Returns(server.Object);
18             context.Setup(ctx => ctx.Response.Output).Returns(new StringWriter());
19 
20             return context.Object;
21         }
22 
23         public static void SetControllerContext(Controller controller)
24         {
25             var httpContext = SetHttpContext();
26             ControllerContext context = new ControllerContext(
27                 new RequestContext(httpContext, new RouteData()),
28                 controller);
29             controller.ControllerContext = context;
30         }
31     }

 

2、FileController的单元测试

 1     [TestClass()]
 2     public class FileControllerTest
 3     {
 4         /// <summary>
 5         /// Download 的测试。
 6         ///</summary>
 7         [TestMethod()]
 8         public void DownloadTest() 
 9         {
10             FileController controller = new FileController();
11             string fileFullPath = @"E:\TempTestFile.txt";
12             CreateFile(fileFullPath);
13 
14             ActionResult actionResult  = controller.Download(fileFullPath);
15 
16             FileDownloadResult result = actionResult as FileDownloadResult;
17             Assert.IsNotNull(result);
18             Assert.IsTrue(string.Equals(fileFullPath,result.FileFullPath));
19 
20             DeleteFile(fileFullPath);
21         }
22 
23         /// <summary>
24         /// 当参数异常,Download 的测试。
25         ///</summary>
26         [TestMethod()]
27         public void DownloadWithAbnormalArgTest() 
28         {
29             FileController controller = new FileController();
30             string fileFullPath = @"E:\TempTestNonExsitingFile.txt";
31             ActionResult actionResult = controller.Download(fileFullPath);
32 
33             AssertAbnormalResult(actionResult);
34         }
35 
36         /// <summary>
37         /// 当参数为null或者empty时,Download 的测试。
38         ///</summary>
39         [TestMethod()]
40         public void DownloadWithNullOrEmptyArgTest() 
41         {
42             FileController controller = new FileController();
43             ActionResult actionResult = controller.Download(string.Empty);
44             AssertAbnormalResult(actionResult);
45 
46             actionResult = controller.Download(null);
47             AssertAbnormalResult(actionResult);
48         }
49 
50         private static void AssertAbnormalResult(ActionResult actionResult)
51         {
52             RedirectToRouteResult result = actionResult as RedirectToRouteResult;
53             Assert.IsNotNull(result);
54             Assert.IsTrue(string.Equals("FileNotFound", result.RouteValues["action"]));
55             Assert.IsTrue(string.Equals("Error", result.RouteValues["controller"]));
56         }
57 
59         private void CreateFile(string fileFullPath)
60         {
61             if (!File.Exists(fileFullPath))
62             {
63                 using (FileStream fileStream = File.Create(fileFullPath))
64                 {
65                 }
66             }
67         }
68 
69         private void DeleteFile(string fileFullPath)
70         {
71             if (File.Exists(fileFullPath))
72             {
73                 File.Delete(fileFullPath);
74             }
75         }
76     }

 

(四)效果图

 

在Silverlight中点击下载显示的效果图如下:

Silverlight以及Mvc最佳文件下载解决方案(附源码)

 

(五)总结

 

上述的代码以及随笔,本人从中午吃完饭一直整到现在,XX,45行代码花了哥这么久(还有一个FileHelperTest的内容没写在随笔了,再写的话,估计全部是代码了!在源代码中有相关测试代码)。时间过得真快,还木有吃饭,自己得马山煮饭吃了,Silverlight以及Mvc最佳文件下载解决方案(附源码),明天又得上班了.....

 

源代码下载:  /Files/jasenkin/MVC/Jasen.MvcDownload.Web.rar

 

  • 海报
海报图正在生成中...
赞(0) 打赏
声明:
1、本博客不从事任何主机及服务器租赁业务,不参与任何交易,也绝非中介。博客内容仅记录博主个人感兴趣的服务器测评结果及一些服务器相关的优惠活动,信息均摘自网络或来自服务商主动提供;所以对本博客提及的内容不作直接、间接、法定、约定的保证,博客内容也不具备任何参考价值及引导作用,访问者需自行甄别。
2、访问本博客请务必遵守有关互联网的相关法律、规定与规则;不能利用本博客所提及的内容从事任何违法、违规操作;否则造成的一切后果由访问者自行承担。
3、未成年人及不能独立承担法律责任的个人及群体请勿访问本博客。
4、一旦您访问本博客,即表示您已经知晓并接受了以上声明通告。
文章名称:《Silverlight以及Mvc最佳文件下载解决方案(附源码)》
文章链接:https://www.456zj.com/20140.html
本站资源仅供个人学习交流,请于下载后24小时内删除,不允许用于商业用途,否则法律问题自行承担。

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址