• 处理文件分片
  • 处理缺失的分片文件
  • 合并分片文件
  • 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. Linux下安装或升级Python 2.7

    1.准备编译环境gcc 2.去官网下载要安装的对应版本的python的源代码 下载地址:https://www.python.org/downloads/source/ 你可以选择你要下载的版本,用w ...

  2. linux中date命令显示昨天的日期信息?以特定格式显示时间?

    需求描述: linux环境中,在使用date命令的时候,可以通过-d指定日期的字符串来显示日期 操作过程: 1.通过date显示昨天的日期 [root@redhat6 ~]# date -d 'yes ...

  3. Sublime text2插件

    Sublime插件: Sublime有好几种安装插件的方法,但是最好用也是最长用的是ctrl+shift+p. 第一步: 使用ctrl+` 调出Sublime控制台,在控制台中输入 import ur ...

  4. ios开发之 -- stringByAddingPercentEscapesUsingEncoding方法被替换 iOS9.0

    最近在项目中,发现之前的一个方法已经不被建议使用了. 该方法名即题目中提到的: stringByAddingPercentEscapesUsingEncoding,这个方法是用来进行转码的,即将汉字转 ...

  5. 01python初识—编辑器&版本&变量知识

    python2.0和3.0版本变化很大,要跟随脚步,学新的,用新的.3.0 python开发工具pycharm 5.0 python的交互器 python的程序一般放到Linux环境下运行. pyth ...

  6. linux 开机自启

    cd /home/dpf 1.vi startup.sh #!/bin/shecho "hello world" >> /home/dpf/test.txt vi mq ...

  7. css !important用法以及CSS样式使用优先级判断

    之前一直看到很多css中都有!important这个样式,一直不知道有什么作用的,今天在网上详细了解了一下,看了别人的博客,顺便转载收藏一下 css !important用法CSS样式使用优先级判断 ...

  8. IOS设计模式第五篇之装饰设计模式的代理设计模式

    版权声明:原创作品,谢绝转载!否则将追究法律责任. 代理: 另一个装饰设计模式,代理,是一个代表或者协调另一个对象的行为机制.例如当你用一个tableView,你必须实现他里面的一个tableView ...

  9. 【laravel5.6】laravel 自定义公共函数

    1.在 app/Helpers/ 新建一个文件 functions.php,当然这个文件位置和名称你可以自己定义,创建一些函数用于全局调用: 2.在composer.json中的autoload下增加 ...

  10. 【Spring源码分析系列】结构组成和容器的基本实现

    beans包的层级结构 src/main/java:用于展现Spring的主要逻辑 src/main/resources:用于存放系统的配置文件 src/test/java:用于对主要逻辑单元进行测试 ...