• 上传——断点续传之实践篇


    单线程普通上传

    1、用流打开文件

     var item = new FileInfo(filePath);
     FileStream stream = item.OpenRead();

     

    2、读取到字节

    var fs=stream;
    
    var transeBytesSize = fs.Length;
    
    var data = new byte[(int)transeBytesSize];
    
    BinaryReader bReader = new BinaryReader(fs);
    
    bReader.Read(data, 0, (int)transeBytesSize);

     

    3、调用服务端上传接口

    var uploadResult = await WebApi.UploadByTrunk(doc, data, md5, fileLength, transeBytesSize, chunkSize);

    此方法是一个异步的上传方法,后面会多次用到,接口参数说明:

    doc:文件相关的信息

    data:传输的字节

    md5:文件的md5

    fileLength:文件大小

    transeBytesSize:当前要传输的字节大小

    chunkSize:分片大小

    单线程断点续传

    1、获取上次传输的断点位置,通过服务端接口获取

     var startResult = await WebApi.GetLastUploadSize(doc.Id, fileLength, fileId);
    
     var startPoint = startResult.Results;

    接口参数说明:

    doc.Id 文档Id

    fileLength:文件长度

    fileId:文件Id,guid类型

     

    2、定位流的当前位置

    定位流的目的是跳过已经已经上传的字节,startPoint就是断点所在,所以跳过startPoint个字节,再上传。

    if (startPoint >= 0 && startPoint <= fileLength - 1)
    {
       fs.Seek(startPoint, SeekOrigin.Current);
    }

    3、分片传输

       int i = 0;
    
       var totalChunks = leftChunkSize % chunkSize == 0 ? leftChunkSize / chunkSize : leftChunkSize / chunkSize + 1;
    
       for (; startPoint <= fileLength - 1; startPoint += transeBytesSize)
       {
         var leftChunkSize = fileLength - startPoint;
         var transeBytesSize = leftChunkSize > chunkSize ? chunkSize : leftChunkSize;
    
         var data = new byte[(int)transeBytesSize];
         bReader.Read(data, 0, (int)transeBytesSize);
         i++;
         var uploadResult = await WebApi.UploadByTrunk(doc, data, md5, fileLength, transeBytesSize, chunkSize, mulThreadEnable, totalChunks, i);                    
       }

    上传接口参数补充,参考普通上传中的接口:

    mulThreadEnable:是否启用多线程

    totalChunks:总分片数

    i:当前分片序号,表示第几个分片,传输当前片序号的目的,在于让服务器知道上传是否结束。服务器知道后,可以按顺序合并分片文件。

         

    多线程断点续传   

    1、获取上次传输的分片数,通过服务端接口获取

     var lastChunks = await WebApi.GetLastChunks(doc.Id, fileId, chunkSize);
    
     if (lastChunks != null)
     {
         hasChunks = lastChunks.Results;
     }

     

    2、准备好分片数据

     List<Task<WebApiResponse>> tasks = new List<Task<WebApiResponse>>();
    
     int i = 0;
    
     Dictionary<int, byte[]> datas = new Dictionary<int, byte[]>();
     Dictionary<int, long> transeBytesSizeDic = new Dictionary<int, long>();
    
    
     for (; startPoint <= fileLength - 1; startPoint += transeBytesSize)
     {
       i++;
    
       leftChunkSize = fileLength - startPoint;
    
       transeBytesSize = leftChunkSize > chunkSize ? chunkSize : leftChunkSize;
    
       var data = new byte[(int)transeBytesSize];
    
       bReader.Read(data, 0, (int)transeBytesSize);
    
       if (hasChunks != null && hasChunks.Count > 0 && hasChunks.Contains(i)) continue;
    
       datas.Add(i, data);
    
       transeBytesSizeDic.Add(i, transeBytesSize);
    
     }   

    3、上传分片数据

     for (int j = 1; j <= totalChunks; j++)
     {
        int k = j;
        if (!datas.ContainsKey(k)) continue;   //跳过已经上传的分片
        var task = WebApi.UploadByTrunk(doc, datas[k], md5, fileLength, transeBytesSizeDic[k], chunkSize, mulThreadEnable, datas.Count, k);
        tasks.Add(task);
      }      

     

    4、等待任务完成后,发送结束标识

    Task.WaitAll(tasks.ToArray());     
    
     foreach (var item in tasks)
     {
    
        var allResult = item.Result;
    
        //处理上传后的结果,如判断成功与否等                                                        
    
        //此处为实际的业务逻辑                  
    
      }
    
      var sendResult = await WebApi.SendFinish(doc, md5, totalChunks, fileLength, chunkSize); 

    接口参数说明:

    doc:文件相关的信息

    md5:文件的md5

    fileLength:文件大小

    totalChunks:总分片大小

    chunkSize:分片大小

    说明:可以看到,此方法与上传方法比,少了很多参数。因为它只是通知服务器已经结束,不携带文件数据。

    由于多线程,最后一个分片,不一定最后传完,所以服务器无法判断上传是否结束。针对这个问题的解决方案,客户端多调用一个接口,通知服务器,我传输完毕,你可以合并分片文件。

    以上是我的断点续传的实践,项目已经结束,所以做个总结,供大家参考。    

  • 相关阅读:
    【Visual C++】游戏开发五十二 浅墨DirectX教程二十 骨骼动画来袭(一)
    转:骨骼动画教程及微软示例Skinned Mesh的解析(一)
    分布式系统的session共享问题
    相关官网文档地址
    10DSL查询文档语法查询分类和基本语法
    InitializingBean接口
    11DSL查询语法全文检索查询精确查询地理查询
    go基础系列~并发协程
    leetcode(c++)(区间DP)
    简单聊聊mysql的脏读、不可重复读、幻读
  • 原文地址:https://www.cnblogs.com/wangqiang3311/p/14893569.html
Copyright © 2020-2023  润新知