WCF总对于文件的传输方式可以通过各种方法来实现,而且对于大文件的传输也能轻松的实现。在这里我们可以通过WCF异步上传的方法来详细介绍一下这方面的应用知识,以此来加深大家对这方面的认识程度。#t#
在WCF下作大文件的上传,首先想到使用的就是Stream,这也是微软推荐的使用方式。处理流程是:首先把文件加载到内存中,加载完毕后传递数据。这种处理方式对小文件,值得推荐,比如几K,几十k的图片文件,文本文件对大文件就不适用,比如10G的电影,把10G的数据加载到缓存中再传递,这是不可想象的。这个时候我们想到的就是断点续传。由于数据量很大。会导致当前程序阻塞,所以采用异步发送的方式,以进度条显示出来,这也是本篇文章所要实现的功能. 另外,目前BasicHttpBinding, NetTcpBinding, 和NetNamedPipeBinding 支持流处理模型,其他的不支持,这也影响stream的使用。
解释几个重要的概念以及实现的方式:
1、WCF异步上传之断点续传:就是在上一次下载/上传断开的位置开始继续下载/上传。微软已经提供好了这样的方法: BinaryWriter 这个是二进制的写入器,看下面:
- namespace System.IO
 - {
 - public class BinaryWriter : IDisposable
 - {
 - public virtual long Seek(int offset, SeekOrigin origin);
 - //设置当前流中的位置,***个参数表示偏移量,第二个参数表示偏移量的参考依据
 - public virtual void Write(byte[] buffer); //把数据写入Seek方法设置的位置
 - }
 - }
 
2、WCF异步上传之异步线程:就是使用后台程序,不用阻塞当前线程,使用backgroundWorker组建,可以大大减少代码的编写量
下面的操作都是与WCF相关的部分。首先我们要定义一个数据契约用来传递数据:
- [DataContract]
 - public class FileInfo
 - {
 - //文件名
 - [DataMember]
 - public string Name { get; set; }
 - //文件字节大小
 - [DataMember]
 - public long Length { get; set; }
 - //文件的偏移量
 - [DataMember]
 - public long Offset { get; set; }
 - //传递的字节数[DataMember]
 - public byte[] Data { get; set; }
 - //创建时间
 - [DataMember]
 - public DateTime CreateTime { get; set; }
 - }
 - 接着定义操作的契约:
 - [ServiceContract]
 - public interface IFilesLoad
 - {
 - [OperationContract]
 - List< FileInfo> GetFilesList(); //获得以已经上传的文件列表
 - [OperationContract]
 - FileInfo GetFiles(string fileName);
 
//根据文件名寻找文件是否存在,返回文件的字节长度- [OperationContract]
 - FileInfo UplodaFile(FileInfo file); //上传文件
 - }
 
定义了契约,下面就要来实现契约,这里仅仅粘贴重要部分,在后面可以下载源代码
- public Fish.DataContracts.FileInfo UplodaFile
 
(Fish.DataContracts.FileInfo file)- {
 - string filePath = System.Configuration.ConfigurationManager.
 
AppSettings["filePath"] + "/" + file.Name;
//获取文件的路径,已经保存的文件名- FileStream fs = new FileStream(filePath, FileMode.OpenOrCreate);
 
//打开文件- long offset = file.Offset; //file.Offset
 
文件偏移位置,表示从这个位置开始进行后面的数据添加- BinaryWriter writer = new BinaryWriter(fs);//初始化文件写入器
 - writer.Seek((int)offset, SeekOrigin.Begin);//设置文件的写入位置
 - writer.Write(file.Data);//写入数据
 - file.Offset = fs.Length;//返回追加数据后的文件位置
 - file.Data = null;
 - writer.Close();
 - fs.Close();
 - return file;
 - }
 
下面来进行服务端得WCF配置
- < system.serviceModel>
 - < services>
 - < !-- 文件断点续传 -->
 - < service behaviorConfiguration="DefaultBehavior"
 
name="Fish.ServiceImpl.FilesService">- < endpoint address="" binding="basicHttpBinding"
 
bindingConfiguration ="StreamedHTTP"
contract="Fish.ServiceInterfaces.IFilesLoad">< /endpoint>- < host>
 - < baseAddresses>
 - < add baseAddress="http://localhost:8080/Fish/FilesService"/>
 - < /baseAddresses>
 - < /host>
 - < /service>
 - < /services>
 - < behaviors>
 - < serviceBehaviors>
 - < behavior name="DefaultBehavior">
 - < serviceMetadata httpGetEnabled="true" />
 - < serviceDebug includeExceptionDetailInFaults="true" />
 - < /behavior>
 - < /serviceBehaviors>
 - < /behaviors>
 - < bindings>
 - < basicHttpBinding>
 - < binding name="StreamedHTTP" maxReceivedMessageSize="2000000000000"
 
messageEncoding="Mtom" transferMode="Streamed">- < readerQuotas maxArrayLength="20000000"/>
 - < /binding>
 - < /basicHttpBinding>
 - < /bindings>
 - < /system.serviceModel>
 
这里最要的是设置maxReceivedMessageSize, messageEncoding。比较重要的是设置为Mtom,可以提高30%的效率,这是wcf特意为大文件提供的。下面看客户端代码:
- var fileManger = Common.ServiceBroker.FindService
 
< IFilesLoad>(); //创建WCF代理- string localPath = e.Argument as string;
 - string fileName = localPath.Substring(localPath.LastIndexOf
 
('\\') + 1);//获得文件本地文件地址- int maxSiz = 1024 * 100; //设置每次传100k
 - FileStream stream = System.IO.File.OpenRead(localPath);
 
//读取本地文件- Fish.DataContracts.FileInfo file = fileManger.GetFiles
 
(fileName); //更加文件名,查询服务中是否存在该文件- if (file == null) //表示文件不存在
 - {
 - file = new Fish.DataContracts.FileInfo();
 - file.Offset = 0; //设置文件从开始位置进行数据传递
 - }
 - file.Name = fileName;
 - file.Length = stream.Length;
 - if (file.Length == file.Offset)
 
//如果文件的长度等于文件的偏移量,说明文件已经上传完成- {
 - MessageBox.Show("该文件已经在服务器中,不用上传!", "提示",
 
MessageBoxButtons.OK, MessageBoxIcon.Warning);- return;
 - }
 - else
 - {
 - while (file.Length != file.Offset)
 
//循环的读取文件,上传,直到文件的长度等于文件的偏移量- {
 - file.Data = new byte[file.Length - file.Offset
 
< = maxSiz ? file.Length - file.Offset : maxSiz]; //设置传递的数据的大小- stream.Position = file.Offset; //设置本地文件数据的读取位置
 - stream.Read(file.Data, 0, file.Data.Length);//把数据写入到file.Data中
 - file = fileManger.UplodaFile(file); //上传
 - e.Result = file.Offset;
 - (sender as BackgroundWorker).ReportProgress((int)
 
(((double)file.Offset / (double)((long)file.Length)) * 100), file.Offset);- if (this.backgroundWorker1.CancellationPending)
 - return;
 - }
 - }
 - stream.Close();
 - Common.ServiceBroker.DisposeService< IFilesLoad>(fileManger); //关闭wcf
 
以上就是对WCF异步上传的相关介绍。