当前位置:首页 > 开发 > 编程语言 > Java > 正文

WCF大文件断点下载示例

发表于: 2012-04-08   作者:互联网   来源:转载   浏览次数:
摘要: 完整项目下载:http://files.cnblogs.com/qldsrx/FilesManager.rar之前发过片段的内容,都没给出完整的项目代码,不少人来要,故整理下,做个完整的演示项目出来,花了4天的时间调试,真要命。另外如果是IIS承载的WCF服务,建议直接写一个继承IHttpHandler的类来实现,那样效率更高,控制更精细,用WCF服务来做仅仅是在不使用IIS的情况下的妥协方法。注
完整项目下载: http://files.cnblogs.com/qldsrx/FilesManager.rar

  之前发过片段的内容,都没给出完整的项目代码,不少人来要,故整理下,做个完整的演示项目出来,花了4天的时间调试,真要命。另外如果是IIS承载的WCF服务,建议直接写一个继承IHttpHandler的类来实现,那样效率更高,控制更精细,用WCF服务来做仅仅是在不使用IIS的情况下的妥协方法。注:必须安装.NET4.0 以上版本

 

先简单介绍下该程序实现的功能:

  一个服务端,使用WCF自承载,默认采用了RESF模式,直接提供了HTTP下载,也可以开启NET TCP等其它模式。HTTP下载时,IE是不支持断点续传的,下载软件中,只有网络传送带支持断点续传,其它的软件都不支持,主要是我的WCF服务采用了流模式传输,客户端软件获取不到要接收的数据总长度,以为不支持断点续传,故而直接不考虑续传,但是网络传送带就不同,它能够继续尝试续传请求,看服务端如何响应,因此只有网络传送带支持断点续传。为了解决下载工具不支持断点续传的情况,我自己在客户端里增加了一个HTTP下载方式,输入下载地址,即可开始下载,中间暂停后还可以恢复,从断点处恢复。另外提供直接程序下载方式,那个功能比较简单,同时客户端无法中断下载,除非网络异常造成的中断,一旦中断可以重试,继续从断点处续传,但是手动中断不可以,这个是WCF消息机制密封装导致的,我们无法主动打断消息的传输(文件下载就是在接收消息)。

 

这是服务端界面 

 WCF大文件断点下载示例_第1张图片

  点击“开启服务”后,按钮变为“正在运行”,此时服务打开,服务监听端口默认为12251,如果不想用这个端口,可以打开源码重新编译,在Form1里面有个baseAddress变量,修改那个即可,也可以设置到配置文件中,演示项目我就不搞那么麻烦了。

  服务开启后,要先点击“文件目录” ,将当前提供文件下载服务的目录设置下,那个当前目录会显示当前设置的位置。然后点击“产生链接”,这是会出现文件列表,只有一层,未做递归处理。只有产生了链接后的文件,才能被下载到,否则都是非法访问,不给与下载,保证了系统安全。点击“复制选中链接”,你可以直接把下载链接复制到剪贴板,可以直接从IE下载试试效果了。

  那个HTTP帮助的链接,点击后可以看到服务的详细调用说明,如果要改变帮助,可以修改config文件。 

 

这是客户端界面:

 WCF大文件断点下载示例_第2张图片

服务地址输入后,点击“获取下载列表 ”,一旦获取成功,服务地址将不可改变。此时直接用“下载选中文件”功能,则通过WCF的接口函数直接下载文件,和底层传输协议无关。如果先“复制选中链接”,然后“打开HTTP下载窗口”,则通过HTTP地址下载文件,此时是通过WCF提供的RESF服务进行的下载。

 

整个演示项目涉及到的知识点很多,故而做了很长时间,下面简单说明下项目中涉及到的几个要点。

一、消息的流封装。

以前曾经写过一篇随笔,提到自定义文件流,现在考虑得更加成熟了,使用自定义读取流,可以对任何流进行封装,比如内存流,因为可能我们要传输的内容是要进行预处理的,一边处理,一边传输,这样就必须对内存流进行封装传输。  

///   <summary>
    
///  自定义读取流(只读)
    
///   </summary>
     internal  class CusStreamReader : Stream
    {
         long _endPosition; // 结束位置
        Stream innerStream;
         ///   <summary>
        
///  参数为当前流的断点
        
///   </summary>
         public  event Action< long> Reading;

         ///   <summary>
        
///  直接使用原始流。
        
///   </summary>
        
///   <param name="stream"> 原始流 </param>
         public CusStreamReader(Stream stream)
        {
             this.innerStream = stream;
            _endPosition = stream.Length;
        }
         ///   <summary>
        
///  使用流当前位置,指定长度初始化自定义流
        
///   </summary>
        
///   <param name="stream"> 原始流 </param>
        
///   <param name="count"> 使用长度 </param>
         public CusStreamReader(Stream stream,  long count)
        {
             this.innerStream = stream;
            _endPosition = stream.Position + count;
             if (_endPosition > stream.Length)
                _endPosition = stream.Length;
        }
         ///   <summary>
        
///  指定初始位置、长度初始化自定义流
        
///   </summary>
        
///   <param name="stream"> 原始流 </param>
        
///   <param name="offset"> 初始位置 </param>
        
///   <param name="count"> 使用长度 </param>
         public CusStreamReader(Stream stream,  long offset,  long count)
        {
            stream.Position = offset > stream.Length ? stream.Length : offset;
             this.innerStream = stream;
            _endPosition = offset + count;
             if (_endPosition > stream.Length)
                _endPosition = stream.Length;
        }
         ///   <summary>
        
///  从自定义流读取指定长度到array,但是不超过初始化时设定的长度。
        
///   </summary>
        
///   <returns> 读取的字节数 </returns>
         public  override  int Read( byte[] array,  int offset,  int count)
        {
             int readcount =  0;
             if (Position + count >  this._endPosition)
                readcount = innerStream.Read(array, offset, ( int)( this._endPosition - Position));
             else
                readcount = innerStream.Read(array, offset, count);
             if (Reading !=  null)
                Reading(Position);
             return readcount;
        }
         ///   <summary>
        
///  从自定义流读取一个字节,但是不超过初始化时设定的长度。
        
///   </summary>
        
///   <returns> 读取的字节,未找到则返回-1 </returns>
         public  override  int ReadByte()
        {
             if (Position >=  this._endPosition)
                 return - 1;
             else
                 return  base.ReadByte();
        }

         public  override  bool CanRead
        {
             get {  return innerStream.CanRead; }
        }

         public  override  bool CanSeek
        {
             get {  return  false; }
        }

         public  override  bool CanWrite
        {
             get {  return  false; }
        }

         public  override  void Flush()
        {
             throw  new NotImplementedException();
        }

         ///   <summary>
        
///  自定义流剩余长度。
        
///   </summary>
         public  override  long Length
        {
             get {  return _endPosition - innerStream.Position; }
        }

         ///   <summary>
        
///  自定义流位置,返回原始流的位置
        
///   </summary>
         public  override  long Position
        {
             get
            {
                 return innerStream.Position;
            }
             set
            {
                 throw  new NotImplementedException();
            }
        }

         public  override  long Seek( long offset, SeekOrigin origin)
        {
             throw  new NotImplementedException();
        }

         public  override  void SetLength( long value)
        {
             throw  new NotImplementedException();
        }

         public  override  void Write( byte[] buffer,  int offset,  int count)
        {
             throw  new NotImplementedException();
        }
    }

 

CusStreamReader fs =  new CusStreamReader( new FileStream(file.filepath, FileMode.Open, FileAccess.Read, FileShare.Read), offset, count);
fs.Reading += (t) =>
{
     // 限速代码,实际使用时可以去掉,或者精确控制
    Thread.Sleep( 300);
    Console.WriteLine(t);
};

这里要特别说明的是这个Reading事件,非常重要。我们既可以利用它来限速,也可以用它来显示传输的状态,显示当前传了多少字节了。

 

二、异常处理 。

对于非法请求,必须返回错误异常,而WCF默认的错误异常处理和HTTP的不一样,如果用HttpWebRequest来请求下载,错误异常是不能直接捕获到的,查阅了相关资料后发现,要捕获类型为WebException的异常,然后特殊处理。.NET4.0里面,可以直接抛出WebFaultException<T>类型的异常,这样HttpWebRequest请求时可以很方便处理异常信息,但是这又带来一个问题,如果同时支持ServiceModel里访问,这个WebFaultException异常将获取不到内容,只能得到错误代码如(404),显然这样是不行的,反复调试后发现,服务端应该抛出FaultException异常,这样ServiceModel里访问可以直接捕获到异常,HttpWebRequest也可以分析出错误信息,核心代码如下:

try
{

}
catch (WebException ex)
{
     var errResp = ex.Response  as HttpWebResponse;
     using ( var stream = errResp.GetResponseStream())
    {
         string message =  null;
         using ( var sr =  new StreamReader(stream))
        {
            message = sr.ReadToEnd();
             var match = Regex.Match(message,  " (?is)(?<=<Text[^>]*>).*(?=</Text>) ");
             if (match.Success)
            {
                message = match.Value;
            }
        }
        MessageBox.Show(message);
    }
}

 

其它的就不多说了,自己看代码,虽然只是演示项目,但是涉及到的知识点很多。 

当前标签: WCF

 
青龙白虎 2012-04-08 14:55 阅读:451 评论:2
 
青龙白虎 2009-12-16 15:40 阅读:707 评论:3

WCF大文件断点下载示例

  • 0

    开心

    开心

  • 0

    板砖

    板砖

  • 0

    感动

    感动

  • 0

    有用

    有用

  • 0

    疑问

    疑问

  • 0

    难过

    难过

  • 0

    无聊

    无聊

  • 0

    震惊

    震惊

编辑推荐
完整项目下载: http://files.cnblogs.com/qldsrx/FilesManager.rar   之前发过片段的内容,都没
使用NSURLConnection实现大文件断点下载 由于是实现大文件的断点下载,不是下载一般图片什么的.在设
在WCF下作大文件的上传,首先想到使用的就是Stream,这也是微软推荐的使用方式。处理流程是:首先把
问题 作为资源共享平台, 百度云做的还是很出色的, "xxx site:pan.baidu.com"就可以找到很丰富的资源
http://www.cnblogs.com/wendingding/p/3947550.html iOS开发网络篇—多线程断点下载 说明:本文介
iOS开发网络篇—多线程断点下载 说明:本文介绍多线程断点下载。项目中使用了苹果自带的类,实现了
原文:http://blog.csdn.net/shimiso/article/details/8529633 什么是Socket? 所谓Socket通常也称作
什么是Socket? 所谓Socket通常也称作“套接字”,用于描述IP地址和端口,是一个通信连的句柄,应用
(一). 概述 最近做了个C/S文件下载工具,支持多任务, 多线程和断点续传功能. 其中部分代码是从网上找
版权所有 IT知识库 CopyRight © 2009-2015 IT知识库 IT610.com , All Rights Reserved. 京ICP备09083238号