Silverlight中用WCF实现文件快速上传(一、基本方法)

来源:互联网 发布:js 隐藏元素 编辑:程序博客网 时间:2024/05/02 00:37

    在Silverlight中可能通过服务上传文件。通过服务上传文件可以比较方便地实现异步上传、分块上传、进度显示等等功能。因此,“Silverlight + 服务”组合可以实现比较强大类似于C/S模式中的那种上传功能。下面详细说明实现步骤。

    1. 创建Silverlight应用程序。

    这个是基本能力,不会的先找资料学习一下吧。

    假定创建的解决方案为:UploadFiles。默认情况下存在两个项目:UploadFiles和UploadFiles.Web。

    2. 添加WCF服务。

    在UploadFiles.Web项目中添加WCF服务:右键→添加→新建项→选“启用 Silverlight 功能的 WCF 服务”,如果VS是英文的则为“Silverlight-enabled WCF Service”。

    3. WCF服务的代码:

[c-sharp] view plaincopy
  1.  namespace UploadFiles.Web  
  2. {  
  3.     [ServiceContract(Namespace = "")]  
  4.     [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]  
  5.     public class Service1  
  6.     {  
  7.         [OperationContract]  
  8.         public void DoWork()  
  9.         {  
  10.             // 在此处添加操作实现  
  11.             return;  
  12.         }  
  13.         // 在此处添加更多操作并使用 [OperationContract] 标记它们  
  14.         [OperationContract]  
  15.         public void ActionUpload(string fileName, byte[] fileData, bool isAppend)  
  16.         {  
  17.             //文件上传所在目录  
  18.             //可以按日期、文件类型或混合建立相应的文件目录,以便使用  
  19.             string uploadFolder = System.Web.Hosting.HostingEnvironment.MapPath("~/upload");  
  20.             //目录不存在则新建  
  21.             if (!System.IO.Directory.Exists(uploadFolder))  
  22.             {  
  23.                 System.IO.Directory.CreateDirectory(uploadFolder);  
  24.             }  
  25.             //文件读写模式  
  26.             //如果参数为真则表示续传,以追回模式写文件  
  27.             System.IO.FileMode fileMode = isAppend ? System.IO.FileMode.Append : System.IO.FileMode.Create;  
  28.             //写入文件  
  29.             using (System.IO.FileStream fs = new System.IO.FileStream(uploadFolder + @"/" + fileName, fileMode, System.IO.FileAccess.Write))  
  30.             {  
  31.                 fs.Write(fileData, 0, fileData.Length);  
  32.             }  
  33.         }  
  34.     }  
  35. }  
 

    4. Silverlight中的上传执行代码

    在MainPage.xaml中添加一个执行上传的按钮,后台执行代码如下:

[c-sharp] view plaincopy
  1. private void Button_Click(object sender, RoutedEventArgs e)  
  2. {  
  3.     //选择本地文件对话框  
  4.     OpenFileDialog openFileDialog = new OpenFileDialog();  
  5.     openFileDialog.Filter = "(*.*)|*.*";  
  6.     //单选  
  7.     openFileDialog.Multiselect = false;  
  8.     if (openFileDialog.ShowDialog() == true)  
  9.     {  
  10.         //所选上传文件信息  
  11.         FileInfo fileInfo = openFileDialog.File;  
  12.         //上传文件信息  
  13.         UploadFileInfo uploadFileInfo = new UploadFileInfo();  
  14.         uploadFileInfo.Name = fileInfo.Name;  
  15.         //文件内容  
  16.         Stream stream = fileInfo.OpenRead();  
  17.         uploadFileInfo.Size = (int)(stream.Length / 1024);  
  18.         uploadFileInfo.Data = new List<byte[]>();  
  19.         //读取指定大小的文件流内容到uploadFile.Data以便上传  
  20.         int len;  
  21.         long rest;  
  22.         while (stream.Position > -1 && stream.Position < stream.Length)  
  23.         {  
  24.             rest = stream.Length - stream.Position;  
  25.             //len = (rest >= 16384) ? 16384 : (int)rest;  //WCF默认配置参数最大只允许16KB  
  26.             len = (rest >= 2097152) ? 2097152 : (int)rest;  //2MB  
  27.             byte[] fileData = new byte[len];  
  28.             stream.Read(fileData, 0, len);  
  29.             uploadFileInfo.Data.Add(fileData);  
  30.         }  
  31.         stream.Close();  
  32.         localService.Service1Client wcfService = new TestUploadFile_WCF.localService.Service1Client();  
  33.         wcfService.ActionUploadCompleted += new EventHandler<System.ComponentModel.AsyncCompletedEventArgs>(wcfService_ActionUploadCompleted);  
  34.         wcfService.ActionUploadAsync(uploadFileInfo.Name, uploadFileInfo.Data[0], false, uploadFileInfo);  
  35.     }  
  36. }  
  37. void wcfService_ActionUploadCompleted(object sender, System.ComponentModel.AsyncCompletedEventArgs e)  
  38. {  
  39.     if (e.Error == null)  
  40.     {  
  41.         UploadFileInfo uploadFileInfo = e.UserState as UploadFileInfo;  
  42.         uploadFileInfo.Status += uploadFileInfo.Data[0].Length / 1024;  
  43.         uploadFileInfo.Data.RemoveAt(0);  
  44.         if (uploadFileInfo.Data.Count == 0)  
  45.         {  
  46.             btnUploadFile.Content = "Uploaded";  
  47.             MessageBox.Show("文件上传成功。");  
  48.         }  
  49.         else  
  50.         {  
  51.             (sender as localService.Service1Client).ActionUploadAsync(uploadFileInfo.Name, uploadFileInfo.Data[0], true, uploadFileInfo);  
  52.             btnUploadFile.Content = uploadFileInfo.Status.ToString() + "/" + uploadFileInfo.Size.ToString() + "KB Uploaded.";  
  53.         }  
  54.     }  
  55. }  
 

    辅助类:

[c-sharp] view plaincopy
  1. public class UploadFileInfo  
  2. {  
  3.     //文件名  
  4.     public string Name { getset; }  
  5.     //文件大小  
  6.     public int Size { getset; }  
  7.     //上传状态  
  8.     public int Status { getset; }  
  9.     //文件流  
  10.     public List<byte[]> Data { getset; }  
  11. }  
 

    5. 上传数据尺寸配置

    默认情况下,WCF只允许最大16KB的数据块上传。按此尺寸,实测上传速度很慢。如果要修改尺寸,则要修改Web.config文件。

    注意:是修改UploadFiles.Web中的Web.config,而不是UploadFiles中的ServiceReferences.ClientConfig。最开始我曾以为是修改后者,结果被困了好久,汗颜!

    网上有好多文章讲修改参数的,但无一指明是修改.Web项目中的Web.config,让我困惑了好久,最后在一个英文网页中找到的答案……。

    修改后的内容如下:

[c-sharp] view plaincopy
  1. <system.serviceModel>  
  2.  <behaviors>  
  3.   <serviceBehaviors>  
  4.    <behavior name="UploadFiles.Web.Service1Behavior">  
  5.     <serviceMetadata httpGetEnabled="true"/>  
  6.     <serviceDebug includeExceptionDetailInFaults="false"/>  
  7.    </behavior>  
  8.   </serviceBehaviors>  
  9.  </behaviors>  
  10.  <bindings>  
  11.   <customBinding>  
  12.    <binding name="customBinding0">  
  13.          <binaryMessageEncoding>  
  14.            <readerQuotas maxArrayLength="2097152"/><!--2MB-->  
  15.          </binaryMessageEncoding>  
  16.     <httpTransport maxReceivedMessageSize="2147483647"/>  
  17.    </binding>  
  18.   </customBinding>  
  19.  </bindings>  
  20.  <serviceHostingEnvironment aspNetCompatibilityEnabled="true"/>  
  21.  <services>  
  22.   <service behaviorConfiguration="UploadFiles.Web.Service1Behavior" name="UploadFiles.Web.Service1">  
  23.    <endpoint address="" binding="customBinding" bindingConfiguration="customBinding0" contract="UploadFiles.Web.Service1"/>  
  24.    <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/>  
  25.   </service>  
  26.  </services>  
  27. </system.serviceModel>  
 

    修改的结果是允许最大2MB的数据块上传。Sunpire网友(参见参考文献2)提到的要增加24KB冗余的问题,我在实测中没有遇到。即:Sunpire网友的文章中提到<customBinding>要设置2MB+24KB,才能上传2MB数据的情况我没有遇到。我实际设置2MB就能上传2MB数据。当然,我没有象Sunpire网友那样去做那么多测试,只做了1MB和2MB的测试。

    6. 总结

    按照上述方法,可以实现各种文件快速上传。小文件上传几乎是瞬间的事,大文件也很快。

    缺点:上传时会一次性的把整个文件读入内存。进一步的解决方案是边读边上传。

 

参考文献:

[1]agile 的《Silverlight 3 中使用WCF上传文件 (简单进度条展示)》:http://my.oschina.net/agile/blog/2130

[2]Sunpire 的《Silverlight+WCF文件上传,含分段大小调节(WCF 16KB配置)、压缩Buffer调节、速度指示、批量等》:http://www.soaspx.com/dotnet/silverlight/silverlight_20100321_3357.html

0 0
原创粉丝点击