zoukankan      html  css  js  c++  java
  • 上传——断点续传之实践篇(1)

    在上一篇中,主要介绍了客户端的断点续传的处理,这一篇,主要补充下服务端的断点续传。

    服务端单线程断点续传

    1、获取上次传输的断点

     var filePath = Path.Combine(rootFolderPath, document.WellId.ToString(), query.FileId + ".temp");
     if (System.IO.File.Exists(filePath))
     {
         var length = new FileInfo(filePath).Length;
    
         if (query.TotalSize > length)
         {
           response.Results = length;
         } 
      }

    2、单线程写入

     1        private WebApiResponse WriteToFile(string rootFilePath, Guid wellId, string fileId, string ext, IHttpFile file, bool isNotChunk, bool isLastChunk)
     2         {
     3             WebApiResponse response = new WebApiResponse();
     4 
     5             try
     6             {
     7                 var folderPath = Path.Combine(rootFilePath, wellId.ToString());
     8 
     9                 if (Directory.Exists(folderPath) == false)
    10                 {
    11                     Directory.CreateDirectory(folderPath);
    12                 }
    13                 if (isNotChunk)
    14                 {
    15                     var filePath = Path.Combine(folderPath, fileId + ext);
    16 
    17                     if (System.IO.File.Exists(filePath))
    18                     {
    19                         System.IO.File.Delete(filePath);
    20                     }
    21 
    22                     using var stream = new FileStream(filePath, FileMode.CreateNew);
    23                     file.InputStream.WriteTo(stream);
    24 
    25                 }
    26                 else
    27                 {
    28                     //附加到临时文件
    29                     var filePath = Path.Combine(folderPath, fileId + ".temp");
    30                     using var stream = new FileStream(filePath, FileMode.Append);
    31                     file.InputStream.WriteTo(stream);
    32                     stream?.Close();
    33 
    34                     if (isLastChunk)
    35                     {
    36                         //最后一个分片,更新文件名
    37 
    38                         var tempFilePath = Path.Combine(folderPath, fileId + ".temp");
    39 
    40                         var targetFilePath = Path.Combine(folderPath, fileId + ext);
    41 
    42                         System.IO.File.Move(tempFilePath, targetFilePath, true);
    43                     }
    44                 }
    45             }
    46             catch (Exception ex)
    47             {
    48                 return WebApiResponse.Fail("文件上传中出错:" + ex.Message);
    49             }
    50 
    51             return response;
    52         }

    服务端多线程断点续传

    1、获取上次已经上传的分片

      

     1                   //获取临时文件夹中文件数,减去1,防止分片不完整
     2                     var mergeDirPath = Path.Combine(rootFolderPath, document.WellId.ToString(), "Temp");
     3 
     4                     if (Directory.Exists(mergeDirPath))
     5                     {
     6                         DirectoryInfo di = new DirectoryInfo(mergeDirPath);
     7 
     8                         //删除掉一些fileId对应不上的文件
     9 
    10                         var tempfiles = di.GetFiles();
    11 
    12                         for (int i = 0; i < tempfiles.Length; i++)
    13                         {
    14                             if (!tempfiles[i].Name.Contains(query.FileId))
    15                             {
    16                                 tempfiles[i].Delete();
    17                             }
    18                         }
    19 
    20                         var files = di.GetFiles().OrderBy(f => f.CreationTime).ToList();
    21 
    22                         List<int> seqs = new List<int>();
    23 
    24                         foreach (var item in files)
    25                         {
    26                             //去掉小于片区的文件
    27                             if (item.Length != query.ChunkSize) continue;
    28                             var pName = Path.GetFileNameWithoutExtension(item.Name);
    29 
    30                             var s = pName.Last().ToString();
    31 
    32                             seqs.Add(int.Parse(s));
    33                         }
    34 
    35                         response.Results = seqs;
    36                     }

    2、多线程写入

     1        private WebApiResponse MulThreadWriteToFile(string rootFilePath, Guid wellId, string fileId, IHttpFile file, long chunkNumber)
     2         {
     3             WebApiResponse response = new WebApiResponse();
     4 
     5             try
     6             {
     7                 var folderPath = Path.Combine(rootFilePath, wellId.ToString(), "Temp");
     8 
     9                 if (Directory.Exists(folderPath) == false)
    10                 {
    11                     Directory.CreateDirectory(folderPath);
    12                 }
    13 
    14                 var filePath = Path.Combine(folderPath, fileId + chunkNumber + ".temp");
    15 
    16                 if (System.IO.File.Exists(filePath))
    17                 {
    18                     System.IO.File.Delete(filePath);
    19                 }
    20 
    21                 using var stream = new FileStream(filePath, FileMode.CreateNew);
    22                 file.InputStream.WriteTo(stream);
    23                 stream?.Close();
    24             }
    25             catch (Exception ex)
    26             {
    27                 WebApiResponse.Fail("文件上传中出错:" + ex.Message);
    28             }
    29 
    30             return response;
    31         }

    3、多线程完成后通知

     DirectoryInfo di = new DirectoryInfo(mergeDirPath);
     var files = di.GetFiles().OrderBy(f => f.Name).ToList();
    
      foreach (var item in files)
      {
            using var stream = new FileStream(filePath, FileMode.Append);
    
            using var fs = item.OpenRead();
    
            fs.WriteTo(stream);
    
            fs?.Close();
    
            stream?.Close();
      }

    客户端多线程上传

    NeedAddFiles为要上传的文件集合,按分页的思想,每一页的列表上传,分给一个task,页内是一个一个文件上传的,多页是并发上传的。

                        while (true)
                        {
                            var sources = NeedAddFiles.Skip((pageIndex - 1) * pageSize).Take(pageSize).ToList();
                            if (sources.Count == 0) break;
    
                            pageIndex++;
    
                            var task = Task.Run(function: async () =>
                            {
                                foreach (var item in sources)
                                {
                                    await FileUpload(item.Key, item.Value);
                                    await Task.Delay(5);
                                }
                            });
    
                            tasks.Add(task);
                        }
    
                        //等待所有线程完成
                        await Task.WhenAll(tasks).ContinueWith(t =>
                        {
                            Console.WriteLine("多线程上传任务已完成");
                        });
    
                        Console.WriteLine("等待线程已完成");

     

  • 相关阅读:
    Java中编写代码出现异常,如何抛出异常,如何捕获异常
    用Java制作斗地主
    Java—Map接口中的常用方法
    Java—增强for循环与for循环的区别/泛型通配符/LinkedList集合
    Java—包装类/System类/Math类/Arrays类/大数据运算/Collection接口/Iterator迭代器
    Java—时间的原点 计算时间所使用的 Date类/DateFormat类/Calendar类
    Java—匿名对象/内部类/访问修饰符/代码块
    Java—构造方法及this/super/final/static关键字
    Java—接口
    URL Protocol打开应用程序并传递程序启动参数(Windows、Mac)
  • 原文地址:https://www.cnblogs.com/wangqiang3311/p/14898985.html
Copyright © 2011-2022 走看看