C# FileStream实现多线程断点续传
作者:airforce094 发布时间:2022-06-19 06:50:56
标签:C#,FileStream,断点续传
一、前言
网上有许多的多线程断点续传操作,但总是写的很云里雾里,或者写的比较坑长。由于这几个月要负责公司的在线升级项目,所以正好顺便写了一下
代码如下:
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
namespace TestCenter
{
class Program
{
static void Main(string[] args)
{
string LocalSavePath = @"E:\Test\TestFile\Local\1.msi"; //本地目标文件路径
FileInfo SeverFilePath = new FileInfo(@"E:\Test\TestFile\Server\1.msi"); //服务器待文件路径
long FileLength = SeverFilePath.Length; //待下载文件大小
Console.WriteLine("Start Configuration");
int PackCount = 0; //初始化数据包个数
long PackSize = 1024000; //数据包大小
if (FileLength % PackSize > 0)
{
PackCount = (int)(FileLength / PackSize) + 1;
}
else
{
PackCount = (int)(FileLength / PackSize);
}
Console.WriteLine("Start Recieve");
var tasks = new Task[PackCount]; //多线程任务
for (int index = 0; index < PackCount; index++)
{
int Threadindex = index; //这步很关键,在Task()里的绝对不能直接使用index
var task = new Task(() =>
{
string tempfilepath = @"E:\Test\TestFile\Temp\" + "QS_" + Threadindex + "_" + PackCount; //临时文件路径
using (FileStream tempstream = new FileStream(tempfilepath, FileMode.Create, FileAccess.Write, FileShare.Write))
{
int length = (int)Math.Min(PackSize, FileLength - Threadindex * PackSize);
var bytes = GetFile(Threadindex*PackCount, length);
tempstream.Write(bytes, 0, length);
tempstream.Flush();
tempstream.Close();
tempstream.Dispose();
}
});
tasks[Threadindex] = task;
task.Start();
}
Task.WaitAll(tasks); //等待所有线程完成
Console.WriteLine("Recieve End");
//检测有哪些数据包未下载
Console.WriteLine("Start Compare");
DirectoryInfo TempDir = new DirectoryInfo(@"E:\Test\TestFile\temp"); //临时文件夹路径
List<string> Comparefiles = new List<string>();
for (int i = 0; i < PackCount; i++)
{
bool hasfile = false;
foreach (FileInfo Tempfile in TempDir.GetFiles())
{
if (Tempfile.Name.Split('_')[1] == i.ToString())
{
hasfile = true;
break;
}
}
if (hasfile == false)
{
Comparefiles.Add(i.ToString());
}
}
//最后补上这些缺失的文件
if (Comparefiles.Count > 0)
{
foreach (string com_index in Comparefiles)
{
string tempfilepath = @"E:\Test\TestFile\Temp\" + "QS_" + com_index+ "_" + PackCount;
using (FileStream Compstream = new FileStream(tempfilepath, FileMode.Create, FileAccess.Write, FileShare.Write))
{
int length = (int)Math.Min(PackSize, FileLength - Convert.ToInt32(com_index) * PackSize);
var bytes = GetFile(Convert.ToInt32(com_index)*PackCount, length);
Compstream.Write(bytes, 0, length);
Compstream.Flush();
Compstream.Close();
Compstream.Dispose();
}
}
}
Console.WriteLine("Compare End");
//准备将临时文件融合并写到1.msi中
Console.WriteLine("Start Write");
using (FileStream writestream = new FileStream(LocalSavePath, FileMode.Create, FileAccess.Write, FileShare.Write))
{
foreach (FileInfo Tempfile in TempDir.GetFiles())
{
using (FileStream readTempStream = new FileStream(Tempfile.FullName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
{
long onefileLength = Tempfile.Length;
byte[] buffer = new byte[Convert.ToInt32(onefileLength)];
readTempStream.Read(buffer, 0, Convert.ToInt32(onefileLength));
writestream.Write(buffer, 0, Convert.ToInt32(onefileLength));
}
}
writestream.Flush();
writestream.Close();
writestream.Dispose();
}
Console.WriteLine("Write End");
//删除临时文件
Console.WriteLine("Start Delete Temp Files");
foreach (FileInfo Tempfile in TempDir.GetFiles())
{
Tempfile.Delete();
}
Console.WriteLine("Delete Success");
Console.ReadKey();
}
//这个方法可以放到Remoting或者WCF服务中去,然后本地调用该方法即可实现多线程断点续传
public static byte[] GetFile(int start, int length)
{
string SeverFilePath = @"E:\Test\TestFile\Server\1.msi";
using (FileStream ServerStream = new FileStream(SeverFilePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, 1024*80, true))
{
byte[] buffer = new byte[length];
ServerStream.Position = start;
//ServerStream.Seek(start, SeekOrigin.Begin);
ServerStream.Read(buffer, 0, length);
return buffer;
}
}
}
}
二、讨论
1)需要注意的是第44行,不能直接使用index变量在Task()里进行操作,而是要将它赋给Threadindex,让Threadindex在Task()里,不然会直接报错,为什么呢?查看链接
2)70至108行代码可以在外面再套一层while循环,循环检测临时文件是否下完整了,然后再定义一个检测最大上限,超过这个上限就放弃本次更新,当用户的网络恢复正常后下次再做更新操作。所以说放临时文件的文件夹最好要包含版本信息,不会把2.0.0的临时文件和1.0.0的临时文件搞混。
3) FileStream.Position 与 FileStream.Seek(long offset, SeekOrigin seekorigin) 的作用都是获取流的指针位置,当文件路径使用绝对路径时使用Position;相对路径时使用Seek方法,查看链接
来源:http://www.cnblogs.com/lovecsharp094/p/5727141.html
0
投稿
猜你喜欢
- 前言Condition是在Spring4.0增加的条件判断功能,通过这个功能可以实现选择性的创建Bean对象。引入一个例子SpringBoo
- 一. 关键字Java中的关键字是由特定的单词组成,单词全为小写字母,每个都有特殊的含义,其实Java关键字也就那几十个,这个不需要背,以后都
- 本文我将要介绍一下mybatis的框架原理,以及mybatis的入门程序,实现用户的增删改查,她有什么优缺点以及mybatis和hibern
- QDownloadQDownload是基于Android平台实现的下载框架。API简洁易上手,只需5分钟即可实现一个多任务、多线程、断点下载
- 背景介绍在实际项目中,特别是一些管理后台类的项目,会遇到底层数据是按照一对多关系的数据表存储的管理界面。列表页是一对多关系中一对应的数据列表
- 本文纯干货,贴上PDF文档操作类C#代码,需要添加iTextSharp.dll引用才可以正常通过编译。废话不多说了,直接给大家贴代码了。代码
- 用Android studio做一个简易计算器,供大家参考,具体内容如下长话短说,先建立一个Android项目;创建完成后打开activit
- 本文以一个C#的SQL数据库字串操作函数为例,说明如何实现对SQL字符串过滤、检测SQL是否有危险字符、修正sql语句中的转义字符,确保SQ
- 以下代码为一个工具类package com.imooc.reflect;import java.lang.reflect.Method;pu
- 这篇文章主要介绍了如何使用java修改文件所有者及其权限,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的
- cpu是时分(time division)的,操作系统里有很多线程,每个线程的运行时间由cpu决定,cpu会分给每个线程一个时间片,时间片是
- 问题场景之前写过一篇文章: 2.@JvmOverloads快捷实现函数重载, 借助于Kotlin的默认参数+@JvmOverloads简化自
- LeetCode -- Path Sum III分析及实现方法题目描述:You are given a binary tree in whi
- public class InnerClass: Form { private Shell_NotifyIconEx servi
- 前言最近测试给我提了一个bug,说我之前提供的一个批量复制商品的接口,产生了重复的商品数据。追查原因之后发现,这个事情没想象中简单,可以说一
- feign传输List的坑无法直接传输List错误方法1@RequestMapping(value = "/stat/mercha
- AutoGenerator 是 MyBatis-Plus 的代码生成器,通过 AutoGenerator 可以快速生成 Entity、Map
- 1.Spring IOC容器可以管理bean的生命周期,Spring允许在bean生命周期内特定的时间点执行指定的任务。2.Spring I
- Mybatis使用@Select注解sql中使用inmapper@Select("SELECT u.* , ur.ro
- 获取map的值主要有四种方法,这四种方法又分为两类,一类是调用map.keySet()方法来获取key和value的值,另一类则是通过map