WCF異步上傳相關(guān)技巧分享
WCF總對于文件的傳輸方式可以通過各種方法來實現(xiàn),而且對于大文件的傳輸也能輕松的實現(xiàn)。在這里我們可以通過WCF異步上傳的方法來詳細介紹一下這方面的應(yīng)用知識,以此來加深大家對這方面的認識程度。#t#
在WCF下作大文件的上傳,首先想到使用的就是Stream,這也是微軟推薦的使用方式。處理流程是:首先把文件加載到內(nèi)存中,加載完畢后傳遞數(shù)據(jù)。這種處理方式對小文件,值得推薦,比如幾K,幾十k的圖片文件,文本文件對大文件就不適用,比如10G的電影,把10G的數(shù)據(jù)加載到緩存中再傳遞,這是不可想象的。這個時候我們想到的就是斷點續(xù)傳。由于數(shù)據(jù)量很大。會導致當前程序阻塞,所以采用異步發(fā)送的方式,以進度條顯示出來,這也是本篇文章所要實現(xiàn)的功能. 另外,目前BasicHttpBinding, NetTcpBinding, 和NetNamedPipeBinding 支持流處理模型,其他的不支持,這也影響stream的使用。
解釋幾個重要的概念以及實現(xiàn)的方式:
1、WCF異步上傳之斷點續(xù)傳:就是在上一次下載/上傳斷開的位置開始繼續(xù)下載/上傳。微軟已經(jīng)提供好了這樣的方法: BinaryWriter 這個是二進制的寫入器,看下面:
- namespace System.IO
 - {
 - public class BinaryWriter : IDisposable
 - {
 - public virtual long Seek(int offset, SeekOrigin origin);
 - //設(shè)置當前流中的位置,***個參數(shù)表示偏移量,第二個參數(shù)表示偏移量的參考依據(jù)
 - public virtual void Write(byte[] buffer); //把數(shù)據(jù)寫入Seek方法設(shè)置的位置
 - }
 - }
 
2、WCF異步上傳之異步線程:就是使用后臺程序,不用阻塞當前線程,使用backgroundWorker組建,可以大大減少代碼的編寫量
下面的操作都是與WCF相關(guān)的部分。首先我們要定義一個數(shù)據(jù)契約用來傳遞數(shù)據(jù):
- [DataContract]
 - public class FileInfo
 - {
 - //文件名
 - [DataMember]
 - public string Name { get; set; }
 - //文件字節(jié)大小
 - [DataMember]
 - public long Length { get; set; }
 - //文件的偏移量
 - [DataMember]
 - public long Offset { get; set; }
 - //傳遞的字節(jié)數(shù)[DataMember]
 - public byte[] Data { get; set; }
 - //創(chuàng)建時間
 - [DataMember]
 - public DateTime CreateTime { get; set; }
 - }
 - 接著定義操作的契約:
 - [ServiceContract]
 - public interface IFilesLoad
 - {
 - [OperationContract]
 - List< FileInfo> GetFilesList(); //獲得以已經(jīng)上傳的文件列表
 - [OperationContract]
 - FileInfo GetFiles(string fileName);
 
//根據(jù)文件名尋找文件是否存在,返回文件的字節(jié)長度- [OperationContract]
 - FileInfo UplodaFile(FileInfo file); //上傳文件
 - }
 
定義了契約,下面就要來實現(xiàn)契約,這里僅僅粘貼重要部分,在后面可以下載源代碼
- public Fish.DataContracts.FileInfo UplodaFile
 
(Fish.DataContracts.FileInfo file)- {
 - string filePath = System.Configuration.ConfigurationManager.
 
AppSettings["filePath"] + "/" + file.Name;
//獲取文件的路徑,已經(jīng)保存的文件名- FileStream fs = new FileStream(filePath, FileMode.OpenOrCreate);
 
//打開文件- long offset = file.Offset; //file.Offset
 
文件偏移位置,表示從這個位置開始進行后面的數(shù)據(jù)添加- BinaryWriter writer = new BinaryWriter(fs);//初始化文件寫入器
 - writer.Seek((int)offset, SeekOrigin.Begin);//設(shè)置文件的寫入位置
 - writer.Write(file.Data);//寫入數(shù)據(jù)
 - file.Offset = fs.Length;//返回追加數(shù)據(jù)后的文件位置
 - file.Data = null;
 - writer.Close();
 - fs.Close();
 - return file;
 - }
 
下面來進行服務(wù)端得WCF配置
- < system.serviceModel>
 - < services>
 - < !-- 文件斷點續(xù)傳 -->
 - < 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>
 
這里最要的是設(shè)置maxReceivedMessageSize, messageEncoding。比較重要的是設(shè)置為Mtom,可以提高30%的效率,這是wcf特意為大文件提供的。下面看客戶端代碼:
- var fileManger = Common.ServiceBroker.FindService
 
< IFilesLoad>(); //創(chuàng)建WCF代理- string localPath = e.Argument as string;
 - string fileName = localPath.Substring(localPath.LastIndexOf
 
('\\') + 1);//獲得文件本地文件地址- int maxSiz = 1024 * 100; //設(shè)置每次傳100k
 - FileStream stream = System.IO.File.OpenRead(localPath);
 
//讀取本地文件- Fish.DataContracts.FileInfo file = fileManger.GetFiles
 
(fileName); //更加文件名,查詢服務(wù)中是否存在該文件- if (file == null) //表示文件不存在
 - {
 - file = new Fish.DataContracts.FileInfo();
 - file.Offset = 0; //設(shè)置文件從開始位置進行數(shù)據(jù)傳遞
 - }
 - file.Name = fileName;
 - file.Length = stream.Length;
 - if (file.Length == file.Offset)
 
//如果文件的長度等于文件的偏移量,說明文件已經(jīng)上傳完成- {
 - MessageBox.Show("該文件已經(jīng)在服務(wù)器中,不用上傳!", "提示",
 
MessageBoxButtons.OK, MessageBoxIcon.Warning);- return;
 - }
 - else
 - {
 - while (file.Length != file.Offset)
 
//循環(huán)的讀取文件,上傳,直到文件的長度等于文件的偏移量- {
 - file.Data = new byte[file.Length - file.Offset
 
< = maxSiz ? file.Length - file.Offset : maxSiz]; //設(shè)置傳遞的數(shù)據(jù)的大小- stream.Position = file.Offset; //設(shè)置本地文件數(shù)據(jù)的讀取位置
 - stream.Read(file.Data, 0, file.Data.Length);//把數(shù)據(jù)寫入到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); //關(guān)閉wcf
 
以上就是對WCF異步上傳的相關(guān)介紹。















 
 
 
 
 
 
 