zoukankan      html  css  js  c++  java
  • wcf综合运用之:大文件异步断点续传


           在WCF下作大文件的上传,首先想到使用的就是Stream,这也是微软推荐的使用方式。处理流程是:首先把文件加载到内存中,加载完毕后传递数据。这种处理方式对小文件,值得推荐,比如几K,几十k的图片文件,文本文件对大文件就不适用,比如10G的电影,把10G的数据加载到缓存中再传递,这是不可想象的。这个时候我们想到的就是断点续传。由于数据量很大。会导致当前程序阻塞,所以采用异步发送的方式,以进度条显示出来,这也是本篇文章所要实现的功能. 另外,目前BasicHttpBinding, NetTcpBinding, 和NetNamedPipeBinding 支持流处理模型,其他的不支持,这也影响stream的使用。
           解释几个重要的概念以及实现的方式:
           1、断点续传:就是在上一次下载/上传断开的位置开始继续下载/上传。微软已经提供好了这样的方法: BinaryWriter 这个是二进制的写入器,看下面:

    namespace System.IO
    {
        public class BinaryWriter : IDisposable
        {
            public virtual long Seek(int offset, SeekOrigin origin); //设置当前流中的位置,第一个参数表示偏移量,第二个参数表示偏移量的参考依据
            public virtual void Write(byte[] buffer);                       //把数据写入Seek方法设置的位置
        }
    }

         2、异步线程:就是使用后台程序,不用阻塞当前线程,使用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

         最后是最后运行的效果:


        代码地址:/Files/wanqiming/Upload.rar

    原文链接:http://www.cnblogs.com/wanqiming/archive/2009/09/22/1571565.html

  • 相关阅读:
    Oracle 11g SQL Fundamentals Training Introduction02
    Chapter 05Reporting Aggregated data Using the Group Functions 01
    Chapter 01Restriicting Data Using The SQL SELECT Statemnt01
    Oracle 11g SQL Fundamentals Training Introduction01
    Chapter 04Using Conversion Functions and Conditional ExpressionsConditional Expressions
    Unix时代的开创者Ken Thompson (zz.is2120.bg57iv3)
    我心目中计算机软件科学最小必读书目 (zz.is2120)
    北京将评估分时分区单双号限行 推进错时上下班 (zz)
    佳能G系列领军相机G1X
    选购单反相机的新建议——心民谈宾得K5(转)
  • 原文地址:https://www.cnblogs.com/Percy_Lee/p/4869715.html
Copyright © 2011-2022 走看看