RaUI/Source/rycUpdate/BigFileOp.cs
zilinsoft 690d2651f5 ## 📅2025-08-16 星期六更新
### rycUpdate    V1.0.2506.0401
- *.[修复]修复文件替换失败,不会失败提示的BUG。
### RaUI    V4.0.2508.1601
- *.[新增]ChromeTabControl控件支持设置SizeMode属性。
- *.[新增]HotkeyTextBox新增支持传入热键文本进行自动转换。
- *.[新增]ryQuickSQL类新增GetJsonData方法。
- *.[新增]ryQuickSQL类新增DateToTimeStamp属性,导出时自动将时间类型转为时间戳。
- *.[新增]自动更新模块新增支持版本类型,区分正式版和测试版。
- *.[改进]HotkeyTextBox控件渲染方式改成全画布渲染。
- *.[改进]TextBoxEx2开启多行模式后,空文本改成在第一行显示。
- *.[修复]修复截图功能某些情况下会报错的BUG。
- *.[修复]修复HotkeyValue类处理多功能键文本时转换错误的BUG。
- *.[修复]修复RyComboBox控件在某些情况下边框会丢失的BUG。
- *.[修复]修复Hosts类针对删除hosts规则处理出错的BUG。
- *.[修复]修复当升级文件Url无法访问时,升级模块会无限期等待的BUG。
2025-08-16 14:27:11 +08:00

444 lines
19 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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="sourcePath">原始文件路径</param>
/// <param name="targetPath">复制目标文件路径</param>
/// <param name="Tag">携带的参数(可选)</param>
/// <returns></returns>
public bool CopyBigFile(string sourcePath, string targetPath,object Tag=null)
{
try
{
if(SkipSameFiles)
{
if (System.IO.File.Exists(targetPath))
{
try
{
var target_info = new System.IO.FileInfo(targetPath);
var source_info = new System.IO.FileInfo(sourcePath);
if (target_info.Length == source_info.Length && target_info.LastWriteTimeUtc == source_info.LastWriteTimeUtc)
{
return true;
}
}
catch { }
}
}
var TranSize = 1024 * 1024 * 2;//每次读取2M
//读取复制文件流
using (FileStream fsRead = new FileStream(sourcePath, 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;
}
try
{
var source_info = new System.IO.FileInfo(sourcePath);
var target_info = new System.IO.FileInfo(targetPath);
target_info.LastWriteTime = source_info.LastWriteTime;
target_info.CreationTime = source_info.CreationTime;
}
catch{ }
}
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 { }
}
}
/// <summary>
/// 是否跳过相同文件,默认不跳过(根据文件大小和修改时间来判断)
/// </summary>
public bool SkipSameFiles { get; set; } = false;
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; }
}
}
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 "";
}
}
}
}