消息服务框架使用案例之--大文件上传(断点续传)功能

简介:

消息服务框架使用案例之--大文件上传(断点续传)功能

一、分块上传和断点续传原理

在我们的一个产品应用中,客户需要上传大量的文件到服务器,其中不乏很大的视频文件。虽然可以使用FTP这样成熟稳定的工具,但客户表示不会使用FTP工具,并且我们产品也觉得客户从我们软件在切换到FTP用户体验不好,如果做成后台脚本调用FTP上传那么进度信息很难呈现到我们软件上。最终,决定我们自己做文件上传功能。

    大文件上传受限于服务器每次处理数据的能力,不能一次传输完成,所以分块上传是必然的了,由于上传时间可能较长,中途可能因为网络或者人为原因终止上传,所以还需要断点上传功能。

    分块上传实际上是在客户端分块读取文件,然后在服务器分块写入文件,每次读写记录下读写的起始位置,也就是文件的偏移量,和要读写的数据长度。在上传过程中,每完成一个文件数据块的写入,就向客户端返回一次信息,客户端据此进行下一文件数据块的读取。
    断点续传功能也比较好实现,就是上传过程中将文件在服务器写为临时文件,等全部写完了(文件上传完),将此临时文件重命名为正式文件即可,如果中途上传中断过,下次上传的时候根据当前临时文件大小,作为在客户端读取文件的偏移量,从此位置继续读取文件数据块,上传到服务器从此偏移量继续写入文件即可。

二、消息服务框架实现文件上传

    假设我们将每一个文件数据块看做一份“消息”,那么文件上传本质上就是客户端和服务器两端频繁的消息交互而已。消息服务框架(MSF)是一个集成了服务容器和消息访问的框架,正好可以用来做文件上传应用。具体做法就是在服务端,编写一个“文件上传服务”,在客户端,编写一个调用上传服务的回调方法即可。

2.1,文件上传服务端

新建一个MSF服务类:

  public class FilesService : ServiceBase
  {

  }

然后添加一个处理上传文件的方法:

  文件上传服务方法

在这个方法中,有一个重要方法,
 //回调客户端,通知上传文件块
 var data = GetUploadFileData(uploadInfo);

它调用了MSF框架服务上下文的回调函数CallBackFunction,来读取客户端文件数据的,代码如下:

 private byte[] GetUploadFileData(UploadFileInfos fileinfo)
 {
    return base.CurrentContext.CallBackFunction<UploadFileInfos, byte[]>(fileinfo);
 }

另外,服务端写文件的方法CurWriteFile 实现如下:

复制代码
/// <summary>
        /// 将服务器端获取到的字节流写入文件
        /// </summary>
        /// <param name="pReadByte"></param>
        /// <param name="fileName">文件名</param>
        /// <param name="offset">要写入文件的位置</param>
        public void CurWriteFile(string fileName, byte[] pReadByte, long offset)
        {
            FileStream pFileStream = null;
            try
            {
                pFileStream = new FileStream(fileName, FileMode.OpenOrCreate);
                pFileStream.Seek(offset, SeekOrigin.Begin);
                pFileStream.Write(pReadByte, 0, pReadByte.Length);
            }
            catch(Exception ex)
            {
                throw new Exception("写文件块失败,写入位置:"+offset+",文件名:"+fileName+",错误原因:"+ex.Message);
            }
            finally
            {
                if (pFileStream != null)
                    pFileStream.Close();

                resetEvent.Set();
            }
        }
复制代码

2.2,文件上传客户端

现在看文件上传客户端代码,如何提供服务端需要的文件读取回调函数

复制代码
  ServiceRequest request = new ServiceRequest();
            request.ServiceName = "FilesService";
            request.MethodName = "UploadFiles";
            request.Parameters = new object[] { infos };
            Proxy srvProxy = new Proxy();
            srvProxy.ServiceBaseUri = string.Format("net.tcp://{0}", serverHost);
            srvProxy.ErrorMessage += srvProxy_ErrorMessage;
          


            Task<UploadResult> result= srvProxy.RequestServiceAsync<UploadResult, UploadFileInfos, byte[]>(request,
                 uploadingInfo =>
                 {
                    //action委托方法显示进度给客户端
                    action(new UploadStateArg()
                     {
                         State = uploadingInfo.Offset + uploadingInfo.Length >= uploadingInfo.Size
                         ? UploadState.Success: UploadState.Uploading,
                         ProgressFile = uploadingInfo.FilePath,
                         ProcessValue = Convert.ToInt32(uploadingInfo.Offset * 100 / uploadingInfo.Size),
                         TotalProcessValue = Convert.ToInt32((uploadingInfo.UploadIndex +1) * 100 / index)
                     });
                     Console.WriteLine(">>>Debug:Path:{0},FilePath:{1}",folder, uploadingInfo.FilePath);
                     var fullName = Path.IsPathRooted(folder)? folder + uploadingInfo.FilePath : uploadingInfo.FilePath;
                     Console.WriteLine(">>>服务器读取客户端文件:{0},偏移量:{1} 长度:{2}",
                         fullName, uploadingInfo.Offset, uploadingInfo.Length);
                     return ReadFileData(fullName, uploadingInfo.Offset, uploadingInfo.Length);
                 }
             );
复制代码

在上面的方法中, srvProxy.RequestServiceAsync泛型方法需要3个参数,第一个参数是服务的结果类型,第二个参数是提供给服务端回调方法(前面的base.CurrentContext.CallBackFunction方法)的参数,第三个参数是服务回调方法的结果。srvProxy.RequestServiceAsync 的回调方法的参数 uploadingInfo 是服务器推送过来的消息,里面包含了需要读取的文件信息,包括文件名,偏移量,读取长度等信息。

其中,客户端读取文件的方法 ReadFileData 实现如下:

复制代码
  /// <summary>
        /// 读取文件返回字节流
        /// </summary>
        /// <param name="fileName">文件路径</param>
        /// <param name="offset">要读取的文件流的位置</param>
        /// <param name="length">要读取的文件块大小</param>
        /// <returns></returns>
        private byte[] ReadFileData(string fileName, long offset, int length)
        {
            FileStream pFileStream = null;
            byte[] pReadByte = new byte[0];
            try
            {
                pFileStream = new FileStream(fileName, FileMode.Open, FileAccess.Read);
                BinaryReader r = new BinaryReader(pFileStream);
                r.BaseStream.Seek(offset, SeekOrigin.Begin);    
                pReadByte = r.ReadBytes(length);
                return pReadByte;
            }
            catch
            {
                return pReadByte;
            }
            finally
            {
                if (pFileStream != null)
                    pFileStream.Close();
            }
        }
复制代码

这样,在一次文件上传的“请求-响应”过程中,MSF的服务端进行了多次回调客户端的操作,客户端根据服务端推送过来的参数信息来精确的读取服务端需要的文件数据。一个支持断点续传的大文件上传服务,使用MSF框架就做好了。

三、其它

本文使用到的其它相关服务端对象的代码定义如下:

  View Code

如果你不清楚如何使用MSF来实现本文的功能,请先阅读下面的文章:

“一切都是消息”--MSF(消息服务框架)入门简介

建议你读完相关的其它两篇文章:

“一切都是消息”--MSF(消息服务框架)之【请求-响应】模式

“一切都是消息”--MSF(消息服务框架)之【发布-订阅】模式

读完后,建议你再读读MSF的理论总结:

分布式系统的消息&服务模式简单总结

 

有关消息服务框架(MSF)更多的讨论,请加我们QQ群讨论,群号:18215717 ,加群口令:消息服务框架

 



    本文转自深蓝医生博客园博客,原文链接:http://www.cnblogs.com/bluedoctor/p/8137052.html,如需转载请自行联系原作者


相关实践学习
RocketMQ一站式入门使用
从源码编译、部署broker、部署namesrv,使用java客户端首发消息等一站式入门RocketMQ。
消息队列 MNS 入门课程
1、消息队列MNS简介 本节课介绍消息队列的MNS的基础概念 2、消息队列MNS特性 本节课介绍消息队列的MNS的主要特性 3、MNS的最佳实践及场景应用 本节课介绍消息队列的MNS的最佳实践及场景应用案例 4、手把手系列:消息队列MNS实操讲 本节课介绍消息队列的MNS的实际操作演示 5、动手实验:基于MNS,0基础轻松构建 Web Client 本节课带您一起基于MNS,0基础轻松构建 Web Client
相关文章
|
7月前
|
存储 Java UED
Java网络编程:实现文件断点续传功能
文件下载是网络应用程序中的常见任务,而断点续传是提供更好用户体验的重要功能之一。本文将详细介绍如何使用Java实现文件断点续传功能,使用户能够在下载中断后从上次中断的地方继续下载。
160 0
|
3月前
|
前端开发 NoSQL Redis
如何实现大文件上传:秒传、断点续传、分片上传
如何实现大文件上传:秒传、断点续传、分片上传
147 0
|
22小时前
|
存储 前端开发 安全
如何处理大文件上传
【4月更文挑战第20天】
22 9
|
11天前
|
消息中间件 Docker 微服务
RabbitMQ入门指南(十一):延迟消息-延迟消息插件
RabbitMQ是一个高效、可靠的开源消息队列系统,广泛用于软件开发、数据传输、微服务等领域。本文主要介绍了DelayExchange插件、延迟消息插件实现延迟消息等内容。
22 0
|
29天前
|
消息中间件 物联网 Android开发
MQTT常见问题之mqtt支持顺序消息失败如何解决
MQTT(Message Queuing Telemetry Transport)是一个轻量级的、基于发布/订阅模式的消息协议,广泛用于物联网(IoT)中设备间的通信。以下是MQTT使用过程中可能遇到的一些常见问题及其答案的汇总:
|
6月前
|
存储 前端开发 算法
大文件上传如何做断点续传?全端+后端结合开发
大文件上传如何做断点续传?全端+后端结合开发
252 0
|
7月前
|
存储 前端开发 C#
如何实现文件断点续传功能
相信大家都使用过迅雷、电驴、百度云网盘等一类的工具,所有这些支持上传或下载的工具都有一个功能,那就是断点续传,也就是在你网络不佳传输断开时,传输会暂停,在网络恢复后,可以继续传输,从而避免数据的重复上传,以减少网络流量,提高效率。那么,你有仔细想过这其中的实现原理嘛?
|
8月前
|
存储 前端开发 NoSQL
注册java实现文件分片上传并且断点续传
一、简单的分片上传 针对第一个问题,如果文件过大,上传到一半断开了,若重新开始上传的话,会很消耗时间,并且你也并不知道距离上次断开时,已经上传到哪一部分了。因此我们应该先对大文件进行分片处理,防止上面提到的问题。
|
8月前
|
监控 UED
大文件上传如何做断点续传
大文件上传如何做断点续传
221 0
|
XML 消息中间件 JavaScript
服务端自定义生成PDF的几种方案
服务端自定义生成PDF的几种方案