RaUI/Source/rycUpdate/BigFileOp.cs

424 lines
18 KiB
C#
Raw Normal View History

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Text;
//using TheArtOfDev.HtmlRenderer.Adapters.Entities;
namespace ryCommon
{
/// <summary>
/// 大文件与批量文件操作类,支持中途取消操作,本类需要实例化操作
/// </summary>
public class BigFileOp
{
/// <summary>
/// 传输进度
/// </summary>
/// <param name="curValue">当前传输完成的值</param>
/// <param name="totalValue">全部值,为-1表示无法获取</param>
/// <param name="percent">当前传输的百分比,为-1表示无法获取</param>
/// <param name="Speed">当前下载速度(值为每秒下载的字节数),为-1表示无法获取</param>
/// <param name="Tag">携带的参数(可选)</param>
public delegate void TranProgressHandler(long curValue, long totalValue, double percent,long Speed=-1, object Tag=null);
/// <summary>
/// 传输失败的事件
/// </summary>
/// <param name="ex">错误描述</param>
/// <param name="Tag">携带的参数(可选)</param>
public delegate void TranErrorHandler(Exception ex, object Tag = null);
/// <summary>
/// 文件传输出错
/// </summary>
public event TranErrorHandler OnError;
/// <summary>
/// 文件传输进度
/// </summary>
public event TranProgressHandler OnFileProgress;
/// <summary>
/// 文件夹传输进度
/// </summary>
public event TranProgressHandler OnFolderProgress;
/// <summary>
/// 取消事件
/// </summary>
public event CancelEventHandler OnCanel;
/// <summary>
/// 删除文件或文件夹
/// </summary>
/// <param name="Path"></param>
/// <returns></returns>
public bool DelFileOrFolder(string Path)
{
var result = false;
try
{
if (System.IO.Directory.Exists(Path))
{
System.IO.Directory.Delete(Path, true);
result = !System.IO.Directory.Exists(Path);
}
else if (System.IO.File.Exists(Path))
{
System.IO.File.Delete(Path);
result = !System.IO.File.Exists(Path);
}
}
catch { }
return result;
}
/// <summary>
/// 大文件按流复制文件,支持自动创建目标文件夹。 true复制成功 false复制失败
/// </summary>
/// <param name="soucrePath">原始文件路径</param>
/// <param name="targetPath">复制目标文件路径</param>
/// <param name="Tag">携带的参数(可选)</param>
/// <returns></returns>
public bool CopyBigFile(string soucrePath, string targetPath,object Tag=null)
{
try
{
var TranSize = 1024 * 1024 * 2;//每次读取2M
//读取复制文件流
using (FileStream fsRead = new FileStream(soucrePath, FileMode.Open, FileAccess.Read))
{
if (System.IO.File.Exists(targetPath))
{
if (!DelFileOrFolder(targetPath))
{
return false;
}
}
CreateDirectory(System.IO.Path.GetDirectoryName(targetPath));
OnFileProgress?.Invoke(0, fsRead.Length, 0,0, Tag);
var totalSize = fsRead.Length;
var curSize = 0L;
var dt = DateTime.Now;
var speed = 0L;//复制速度
var last_size = 0L;
//写入文件复制流
using (FileStream fsWrite = new FileStream(targetPath, FileMode.OpenOrCreate, FileAccess.Write))
{
byte[] buffer = new byte[TranSize];
CancelEventArgs e1 = new CancelEventArgs();
//可能文件比较大要循环读取每次读取2M
while (true)
{
if (OnCanel != null)
{
OnCanel?.Invoke(this, e1);
if (e1.Cancel) {
fsWrite.Close();
DelFileOrFolder(targetPath);
break;
}
}
//每次读取的数据 n是每次读取到的实际数据大小
int n = fsRead.Read(buffer, 0, buffer.Count());
//如果n=0说明读取的数据为空已经读取到最后了跳出循环
if (n == 0)
{
break;
}
curSize += n;
var dt_now = DateTime.Now;
if (dt.AddSeconds(1)<dt_now)
{
speed = (curSize - last_size)*1000 /Convert.ToInt64((dt_now - dt).TotalMilliseconds);
dt = DateTime.Now;
last_size = curSize;
}
if (curSize == totalSize)
{
OnFileProgress?.Invoke(totalSize, totalSize, 100, speed, Tag);
}
else
{
OnFileProgress?.Invoke(curSize, totalSize, totalSize == 0 ? 0 : curSize / (double)totalSize, speed, Tag);
}
//写入每次读取的实际数据大小
fsWrite.Write(buffer, 0, n);
}
}
if (curSize != totalSize)
{
OnFileProgress?.Invoke(totalSize, totalSize, 100, 0, Tag);
}
if (!System.IO.File.Exists(targetPath))
{
return false;
}
}
return true;
}
catch(Exception ex)
{
OnError?.Invoke(ex,Tag);
OnFileProgress?.Invoke(0, 0, 100, 0, Tag);
DelFileOrFolder(targetPath);
return false;
}
}
/// <summary>
/// 复制文件夹到目标文件夹,不支持进度事件,支持中途取消
/// </summary>
/// <param name="fromDir">源文件夹</param>
/// <param name="ToDir">目标文件夹</param>
/// <returns>返回复制操作是否成功的标识成功返回0负数表示复制失败的文件数量。1表示源文件夹不存在</returns>
public int CopyFolder(string fromDir, string ToDir)
{
return CopyFolder(fromDir,ToDir,-1,out _);
}
/// <summary>
/// 复制文件或文件夹到目标路径,不支持进度事件,支持中途取消
/// </summary>
/// <param name="fromPath">源路径</param>
/// <param name="ToPath">目标路径</param>
/// <returns>返回复制操作是否成功的标识成功返回0负数表示复制失败的文件数量。1表示源文件夹不存在</returns>
public int CopyFileOrFolder(string fromPath, string ToPath)
{
if(System.IO.File.Exists(fromPath))
{
return CopyBigFile(fromPath, ToPath)?0:-1;
}
else if (System.IO.Directory.Exists(fromPath))
{
return CopyFolder(fromPath, ToPath, -1, out _);
}
else
{
return 1;
}
}
/// <summary>
/// 复制文件夹到目标文件夹,支持进度事件,支持中途取消
/// </summary>
/// <param name="fromDir">源文件夹</param>
/// <param name="ToDir">目标文件夹</param>
/// <returns>返回复制操作是否成功的标识成功返回0负数表示复制失败的文件数量。1表示源文件夹不存在</returns>
public int CopyFolderByProgress(string fromDir, string ToDir)
{
var FileCount = GetFilesCount(fromDir);
return CopyFolder(fromDir, ToDir, FileCount, out _);
}
/// <summary>
/// 如果指定文件夹不存在,则创建文件夹
/// </summary>
/// <param name="path"></param>
public static void CreateDirectory(string path)
{
if (!System.IO.File.Exists(path) && !System.IO.Directory.Exists(path))
{
try
{
System.IO.Directory.CreateDirectory(path);
}
catch { }
}
}
public readonly List<string> ErrFilesList = new List<string>();
/// <summary>
/// 复制文件夹到目标文件夹,支持中途取消
/// </summary>
/// <param name="fromDir">源文件夹</param>
/// <param name="ToDir">目标文件夹</param>
/// <param name="TotalCount">总数量,如果不计算,则使用-1</param>
/// <param name="Count">操作的数量,包含成功的和失败的</param>
/// <param name="Tag">携带的参数(可选)</param>
/// <returns>返回复制操作是否成功的标识成功返回0负数表示复制失败的文件数量。1表示源文件夹不存在</returns>
private int CopyFolder(string fromDir, string ToDir,int TotalCount, out int Count,object Tag = null)
{
Count = 0;
var _count = 0;
var error = 0;
Copy(fromDir, ToDir);
Count = _count;
return error;
int Copy(string From_Dir, string To_Dir)
{
var _fromDir = From_Dir.TrimEnd('\\');
var _ToDir = To_Dir.TrimEnd('\\');
if (!Directory.Exists(_fromDir)) { return 1; }
var files = Directory.GetFiles(_fromDir);
CancelEventArgs e1 = new CancelEventArgs();
CreateDirectory(_ToDir);
foreach (var file in files)
{
if (OnCanel != null)
{
OnCanel?.Invoke(this, e1);
if (e1.Cancel) { break; }
}
if (!CopyBigFile(file, _ToDir + "\\" + Path.GetFileName(file), Tag))
{
error++;
ErrFilesList.Add(_ToDir + "\\" + Path.GetFileName(file));
}
_count++;
var percent = 0d;
if (TotalCount > 0) { percent = _count / (double)TotalCount; }
else if (TotalCount == -1) { percent = -1; }
OnFolderProgress?.Invoke(_count, TotalCount, percent, -1, Tag);
}
var dirs = Directory.GetDirectories(_fromDir);
foreach (var dir in dirs)
{
int result = Copy(dir, _ToDir + "\\" + Path.GetFileName(dir));
if (OnCanel != null)
{
OnCanel?.Invoke(this, e1);
if (e1.Cancel) { break; }
}
if (result > 0)
{
error++;
}
else if (result < 0)
{
error += -result;
}
}
return -error;
}
}
/// <summary>
/// 获取文件夹中的文件数量
/// </summary>
/// <param name="Dir">文件夹路径</param>
/// <returns>返回文件数量,中途取消则返回-1</returns>
public int GetFilesCount(string Dir)
{
var _fromDir = Dir.TrimEnd('\\');
if (!Directory.Exists(_fromDir)) { return 0; }
var files = Directory.GetFiles(_fromDir);
var count = 0;
CancelEventArgs e1 = new CancelEventArgs();
count += files.Length;
var dirs = Directory.GetDirectories(_fromDir);
foreach (var dir in dirs)
{
if (OnCanel != null)
{
OnCanel?.Invoke(this, e1);
if (e1.Cancel) { count = -1; break; }
}
var result= GetFilesCount(dir);
if (result == -1) { count = -1;break; }
count+= result;
}
return count;
}
/// <summary>
/// 获取文件大小
/// </summary>
/// <param name="path"></param>
/// <returns></returns>
public static long GetFileSize(string path)
{
try
{
FileInfo fileInfo = new FileInfo(path);
return fileInfo.Length;
}
catch { return -1; }
}
/// <summary>
/// 获取文件夹的大小
/// </summary>
/// <param name="Dir">文件夹路径</param>
/// <returns>返回文件数量,中途取消则返回-1</returns>
public long GetFolderSize(string Dir)
{
var _fromDir = Dir.TrimEnd('\\');
if (!Directory.Exists(_fromDir)) { return 0; }
var files = Directory.GetFiles(_fromDir);
var size = 0L;
CancelEventArgs e1 = new CancelEventArgs();
for (int i = 0; i < files.Length; i++)
{
if (OnCanel != null && i %5==0)
{
OnCanel?.Invoke(this, e1);
if (e1.Cancel) { size = -1; break; }
}
size +=GetFileSize(files[i]);
}
var dirs = Directory.GetDirectories(_fromDir);
foreach (var dir in dirs)
{
if (OnCanel != null)
{
OnCanel?.Invoke(this, e1);
if (e1.Cancel) { size = -1; break; }
}
var result = GetFilesCount(dir);
if (result == -1) { size = -1; break; }
size += result;
}
return size;
}
/// <summary>
/// 添加文件到指定文件夹,会进行自动重命名,并返回重命名后的文件名(含路径)
/// </summary>
/// <param name="filepath">要添加的文件路径</param>
/// <param name="toFolder">要添加到的文件夹</param>
/// <param name="RenameByTime">是否根据时间进行重命名</param>
/// <param name="OK">返回是否成功执行</param>
/// <returns>成功执行则返回路径,否则返回空</returns>
public string AddFileToFolder(string filepath, string toFolder, bool RenameByTime, out bool OK)
{
OK = false;
var _toFolder = toFolder.TrimEnd('\\');
CreateDirectory(_toFolder);
if (!System.IO.Directory.Exists(_toFolder)) { return ""; }
if (!System.IO.File.Exists(filepath) && !System.IO.Directory.Exists(filepath)) { return ""; }
var filename = System.IO.Path.GetFileName(filepath);
if (RenameByTime) { filename = DateTime.Now.ToString("yyyyMMddHHmmss") + System.IO.Path.GetExtension(filepath); }
if (!System.IO.File.Exists(_toFolder + "\\" + filename) && !System.IO.Directory.Exists(_toFolder + "\\" + filename))
{
if (CopyBigFile(filepath, _toFolder + "\\" + filename))
{
OK = true;
return _toFolder + "\\" + filename;
}
return "";//如果复制失败,则返回空路径
}
else //如果目标路径已经存在同名文件
{
int index = 0;
string filename_noext;
if (RenameByTime) { filename_noext = DateTime.Now.ToString("yyyyMMddHHmmss"); }
else { filename_noext = System.IO.Path.GetFileNameWithoutExtension(filepath); }
var ext = System.IO.Path.GetExtension(filepath);
CancelEventArgs e1 = new CancelEventArgs();
while (index < 20) //最多重试20次
{
if (OnCanel != null)
{
OnCanel?.Invoke(this, e1);
if (e1.Cancel)
{
break;
}
}
Random rd = new Random(Guid.NewGuid().GetHashCode());
if (RenameByTime) { filename_noext = DateTime.Now.ToString("yyyyMMddHHmmss"); }
var to_path = _toFolder + "\\" + filename_noext + "_" + rd.Next(1000, 9999) + ext;
if (!System.IO.File.Exists(to_path) && !System.IO.Directory.Exists(to_path))
{
if (CopyBigFile(filepath, to_path))
{
OK = true;
return to_path;
}
return "";
}
index++;
}
return "";
}
}
}
}