• 处理文件分片
  • 处理缺失的分片文件
  • 合并分片文件
  • MD5验证文件
using System;
using System.Collections.Generic;
using System.IO;
using System.Security.Cryptography;
using System.Threading.Tasks;
using System.Linq;
using System.Text; public class FileTransfer
{ /// <summary>
/// 文件源路径
/// </summary>
public string SrcPath { get; private set; } /// <summary>
/// 文件的目标路径
/// </summary>
public string TgtPath { get; private set; } /// <summary>
/// 临时目录(存放断点数据文件)
/// </summary>
public string TempDir { get; private set; } /// <summary>
/// 文件的目标目录
/// </summary>
public string TgtDir { get; private set; } /// <summary>
/// 数据包大小(默认16mb)
/// </summary>
public long PackSize { get; set; } = * * ; /// <summary>
/// 文件大小
/// </summary>
public long FileLength { get; private set; } /// <summary>
/// 传输包大小
/// </summary>
public int PackCount { get; private set; } /// <summary>
/// 断点续传
/// </summary>
/// <param name="srcPath">文件源路径</param>
/// <param name="tgtPath">文件的目标路径</param>
public FileTransfer(string srcPath, string tgtPath)
: this(srcPath, tgtPath, * * )
{ } /// <summary>
/// 断点续传
/// </summary>
/// <param name="srcPath">文件源路径</param>
/// <param name="tgtPath">文件的目标路径</param>
/// <param name="packSize">数据包大小</param>
public FileTransfer(string srcPath, string tgtPath, int packSize)
{
this.SrcPath = srcPath;
this.TgtPath = tgtPath;
this.PackSize = packSize; FileInfo fileInfo = new FileInfo(this.SrcPath);
if (!fileInfo.Exists)
{
throw new ArgumentException("文件不存在!", "srcPath");
} this.TgtDir = Path.GetDirectoryName(tgtPath); if (!Directory.Exists(this.TgtDir))
{
Directory.CreateDirectory(this.TgtDir);
} this.FileLength = fileInfo.Length; if ((this.FileLength % this.PackSize) > )
{
this.PackCount = (int)(this.FileLength / this.PackSize) + ;
} else
{
this.PackCount = (int)(this.FileLength / this.PackSize);
} this.TempDir = Path.Combine(this.TgtDir, StrMD5(Path.GetFileName(this.TgtPath))); //新new 对象时,删除临时文件夹
if (Directory.Exists(this.TempDir))
{
Directory.Delete(this.TempDir, true);
}
} /// <summary>
/// 检测临时目录是否存在,不存在则创建
/// </summary>
private void CheckTempDir()
{
if (!Directory.Exists(this.TempDir))
{
Directory.CreateDirectory(this.TempDir);
}
} /// <summary>
/// md5比对文件
/// </summary>
/// <returns></returns>
public bool Md5Compare()
{
string md51 = FileMD5(this.SrcPath);
string md52 = FileMD5(this.TgtPath); if (md51 == null || md52 == null)
{
return false;
}
return md51.Equals(md52);
} /// <summary>
/// 文件分片传输
/// </summary>
public void Transfer(bool isMerge = true)
{
CheckTempDir();
//多线程任务
var tasks = new Task[this.PackCount];
var fy = Task.Factory;
for (int index = ; index < this.PackCount; index++)
{
long Threadindex = index; //这步很关键,在Task()里的绝对不能直接使用index
var task = fy.StartNew(() =>
{
//临时文件路径
string tempfilepath = Path.Combine(this.TempDir, GenerateTempName(Threadindex));
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 * PackSize, length); tempstream.Write(bytes, , length);
tempstream.Flush();
tempstream.Close();
tempstream.Dispose();
}
});
tasks[Threadindex] = task;
}
//等待所有线程完成
Task.WaitAll(tasks); // 合并文件
if (isMerge)
{
Merge();
}
} /// <summary>
/// 比对缓存文件,并进行分片
/// </summary>
public void CompareTransfer(bool isMerge = true)
{
CheckTempDir();
//临时文件夹路径
var tempfiles = new DirectoryInfo(this.TempDir).GetFiles();
List<string> Comparefiles = new List<string>();
for (int j = ; j < PackCount; j++)
{
bool hasfile = false;
foreach (FileInfo Tempfile in tempfiles)
{
if (Tempfile.Name.Split('_')[] == j.ToString())
{
hasfile = true;
break;
}
}
if (hasfile == false)
{
Comparefiles.Add(j.ToString());
}
} //最后补上这些缺失的文件
if (Comparefiles.Count > )
{
var tasks = new List<Task>();
var fy = Task.Factory;
foreach (string com_index in Comparefiles)
{
string strIndex = com_index;
var task = fy.StartNew(() =>
{
string tempfilepath = Path.Combine(this.TempDir, GenerateTempName(strIndex));
using (FileStream Compstream = new FileStream(tempfilepath, FileMode.Create, FileAccess.Write, FileShare.Write))
{
int length = (int)Math.Min(PackSize, this.FileLength - Convert.ToInt32(strIndex) * this.PackSize);
var bytes = GetFile(Convert.ToInt64(strIndex) * PackSize, length);
Compstream.Write(bytes, , length);
Compstream.Flush();
Compstream.Close();
Compstream.Dispose();
}
});
tasks.Add(task);
}
//等待所有线程完成
Task.WaitAll(tasks.ToArray());
} // 合并文件
if (isMerge)
{
Merge();
}
} /// <summary>
/// 合并分片文件
/// </summary>
/// <param name="isDelTemp">是否删除临时文件夹(文件)</param>
public void Merge(bool isDelTemp = true)
{
//var tempDirInfo = new DirectoryInfo(this.TempDir);
//using (FileStream writestream = new FileStream(this.TgtPath, FileMode.Create, FileAccess.Write, FileShare.Write))
//{
// var tempfiles = tempDirInfo.GetFiles();
// foreach (FileInfo fileInfo in tempfiles)
// {
// Console.WriteLine(fileInfo.Name);
// using (FileStream readTempStream = new FileStream(fileInfo.FullName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
// {
// long onefileLength = fileInfo.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();
//}
//tempDirInfo.Delete(isDelTemp); var tempDirInfo = new DirectoryInfo(this.TempDir);
using (FileStream writestream = new FileStream(this.TgtPath, FileMode.Create, FileAccess.Write, FileShare.Write))
{
var tempfiles = tempDirInfo.GetFiles();
foreach (FileInfo fileInfo in tempfiles)
{
using (FileStream readTempStream = new FileStream(fileInfo.FullName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
{
long onefileLength = fileInfo.Length;
byte[] buffer = new byte[Convert.ToInt32(onefileLength)];
readTempStream.Read(buffer, , Convert.ToInt32(onefileLength));
writestream.Write(buffer, , Convert.ToInt32(onefileLength));
} if (isDelTemp)
{
fileInfo.Delete();
}
}
writestream.Flush();
writestream.Close();
writestream.Dispose();
} if (isDelTemp)
{
tempDirInfo.Delete(isDelTemp);
}
} /// <summary>
/// 根据开始位置获取文件字节流
/// </summary>
/// <param name="start"></param>
/// <param name="length"></param>
/// <returns></returns>
private byte[] GetFile(long start, int length)
{
using (FileStream ServerStream = new FileStream(this.SrcPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, * , true))
{
byte[] buffer = new byte[length];
ServerStream.Position = start;
//ServerStream.Seek(start, SeekOrigin.Begin);
ServerStream.Read(buffer, , length);
return buffer;
}
} /// <summary>
/// 生成临时文件名称
/// </summary>
/// <param name="index"></param>
/// <returns></returns>
private string GenerateTempName(object index)
{
string res = index.ToString().PadLeft(this.PackCount.ToString().Length, '') + "_" + this.PackCount;
Console.WriteLine(res);
return res;
} /// <summary>
/// 计算文件的Md5
/// </summary>
/// <param name="path"></param>
/// <returns></returns>
private static string FileMD5(string path)
{
if (!File.Exists(path))
{
return null;
}
int bufferSize = * ;
byte[] buffer = new byte[bufferSize];
Stream inputStream = File.Open(path, FileMode.Open, FileAccess.Read, FileShare.Read);
HashAlgorithm hashAlgorithm = new MD5CryptoServiceProvider();
int readLength = ;//每次读取长度
var output = new byte[bufferSize];
while ((readLength = inputStream.Read(buffer, , buffer.Length)) > )
{
//计算MD5
hashAlgorithm.TransformBlock(buffer, , readLength, output, );
}
//完成最后计算,必须调用(由于上一部循环已经完成所有运算,所以调用此方法时后面的两个参数都为0)
hashAlgorithm.TransformFinalBlock(buffer, , );
string md5 = BitConverter.ToString(hashAlgorithm.Hash).Replace("-", "");
hashAlgorithm.Clear();
inputStream.Close();
inputStream.Dispose();
return md5;
} /// <summary>
/// 字符串Md5
/// </summary>
/// <param name="source"></param>
/// <returns></returns>
private static string StrMD5(string source)
{
byte[] sor = Encoding.UTF8.GetBytes(source);
MD5 md5 = MD5.Create();
byte[] result = md5.ComputeHash(sor);
StringBuilder strbul = new StringBuilder();
for (int i = ; i < result.Length; i++)
{
strbul.Append(result[i].ToString("x2"));//加密结果"x2"结果为32位,"x3"结果为48位,"x4"结果为64位 }
return strbul.ToString();
} }

FileStream实现多线程断点续传(已封装)的更多相关文章

  1. C#基础-FileStream实现多线程断点续传

    一.前言 网上有许多的多线程断点续传操作,但总是写的很云里雾里,或者写的比较坑长.由于这几个月要负责公司的在线升级项目,所以正好顺便写了一下 代码如下: using System; using Sys ...

  2. AsyncTask实现多任务多线程断点续传下载

    这篇博客是AsyncTask下载系列的最后一篇文章,前面写了关于断点续传的和多线程下载的博客,这篇是在前两篇的基础上面实现的,有兴趣的可以去看下. 一.AsyncTask实现断点续传 二.AsyncT ...

  3. 实现android支持多线程断点续传下载器功能

    多线程断点下载流程图: 多线程断点续传下载原理介绍: 在下载的时候多个线程并发可以占用服务器端更多资源,从而加快下载速度手机端下载数据时难免会出现无信号断线.电量不足等情况,所以需要断点续传功能根据下 ...

  4. android 多线程断点续传下载

    今天跟大家一起分享下Android开发中比较难的一个环节,可能很多人看到这个标题就会感觉头很大,的确如果没有良好的编码能力和逻辑思维,这块是很难搞明白的,前面2次总结中已经为大家分享过有关技术的一些基 ...

  5. Android开发多线程断点续传下载器

    使用多线程断点续传下载器在下载的时候多个线程并发可以占用服务器端更多资源,从而加快下载速度,在下载过程中记录每个线程已拷贝数据的数量,如果下载中断,比如无信号断线.电量不足等情况下,这就需要使用到断点 ...

  6. 多线程更新已排序的Datagridview数据,造成数据错位

    多线程更新已排序的Datagridview数据,触发Datagridview的auto-sort时间,数据重新排序,造成后面更新数据的更新错误. 解决方法: 方法一.设置Datagridview的表头 ...

  7. Android多线程断点续传下载

    这个月接到一个项目.要写一个像360助手一样的对于软件管理的APP:当中.遇到了一个问题:多线程断点下载 这个 ,因为之前没有写过这方面的应用功能.所以.不免要自学了. 然后就在各个昂站上收索并整理了 ...

  8. Android 多线程断点续传同时下载多个大文件

    最近学习在Android环境中一些网络请求方面的知识,其中有一部分是关于网络下载方面的知识.在这里解析一下自己写的demo,总结一下自己所学的知识.下图为demo的效果图,仿照一些应用下载商城在Lis ...

  9. android多线程断点续传下载文件

    一.目标 1.多线程抢占服务器资源下载. 2.断点续传. 二.实现思路. 假设分为三个线程: 1.各个线程分别向服务器请求文件的不同部分. 这个涉及Http协议,可以在Header中使用Range参数 ...

随机推荐

  1. mysql 两台主主复制配置

    A.服务器 [mysqld] # The TCP/IP Port the MySQL Server will listen on port= server-id= #master-host=10.1. ...

  2. Ckeditor的JS的加载和取值和赋值方法

    Ckeditor 就是原来的Fckeditor. JS加载: $(function() { CKEDITOR.replace('FContent'); //FContent:这个对应文本域 }); J ...

  3. linux proc目录介绍

    https://www.cnblogs.com/DswCnblog/p/5780389.html

  4. Linux+Redis实战教程_day02_3、redis数据类型_4、String命令_5、hash命令_6、java操作redis数据库技术

    3. redis数据类型[重点] redis 使用的是键值对保存数据.(map) key:全部都是字符串 value:有五种数据类型 Key名:自定义,key名不要过长,否则影响使用效率 Key名不要 ...

  5. Log4net用法(.config文件)

    1.引用log4net.dll 2.在AssemblyInfo.cs中添加初始化: [assembly: log4net.Config.XmlConfigurator(ConfigFile = &qu ...

  6. Burp Post、Get数据包转为上传multipart/form-data格式数据包

    方法一: 新建一个网页进行上传,代码代码如下: <html> <head></head> <body> <form method="po ...

  7. linux实现开机自启动脚本

    Linux下(以RedHat为范本)添加开机自启动脚本有两种方法,先来简单的; 一.在/etc/rc.local中添加如果不想将脚本粘来粘去,或创建链接什么的,则:step1. 先修改好脚本,使其所有 ...

  8. 【消息队列】windows下安装RabbitMQ消息队列服务器

    RabbitMQ是什么 ? RabbitMQ是一个在AMQP基础上完整的,可复用的企业消息系统.他遵循Mozilla Public License开源协议. 1:安装RabbitMQ需要先安装Erla ...

  9. 解决Devexpress ChartControl的CalcHitInfo当中SeriesPoint为Null的问题

    Winform程序 ChartControl的RuntimeHitTesting属性一定要设为True. Line Series markers的Visible一定要弄成True.CalcHitInf ...

  10. Elasticsearch学习之深入搜索一 --- 提高查询的精准度

    1. 为帖子增加标题字段 POST /forum/article/_bulk { "} } { "doc" : {"title" : "th ...