unmvyd

博客园 首页 新随笔 联系 订阅 管理
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Diagnostics;
using System.IO;
using SharpCompress.Archives.Zip;
using SharpCompress.Writers;
using SharpCompress.Archives;
using SharpCompress.Common;
using CommandLine;
using SharpCompress.Common.Zip;
using SharpCompress.Factories;
namespace Compress
{
    class Options
    {
        [Option('i', "input", Required = true, HelpText = "输入路径")]
        public string Input { get; set; }

        [Option('o', "output", Required = true, HelpText = "输出路径")]
        public string Output { get; set; }

        [Option('s', "size", Required = false, HelpText = "单个文件大小,单位MB,默认4096,最小10,最大8192")]
        public long VolumeSizeMB { get; set; }
    }
    class Program
    {
        const int MAX_CMD_LENGTH = 8000; // 命令行最大长度限制
        static long SINGLE_FILE_MAX = 8L * 1024 * 1024 * 1024; // 单文件8GB以上分卷
        static string _7zPath = "7za.exe";
        static IEnumerable<string> GetFilesDepthFirst(string rootDir)
        {
            // 先处理子目录(按创建时间排序)
            var dirs = Directory.GetDirectories(rootDir)
                          .OrderBy(d => Directory.GetCreationTimeUtc(d))
                          .ThenBy(d => d);
            foreach (var dir in dirs)
            {
                foreach (var file in GetFilesDepthFirst(dir))
                {
                    yield return file;
                }
            }
            // 最后处理当前目录文件(按修改时间排序)
            foreach (var file in Directory.GetFiles(rootDir)
                                     .OrderBy(f => File.GetLastWriteTimeUtc(f)))
            {
                yield return file;
            }
        }
        static void CompressWith7z(string workPath, string archivePath, List<string> files, long volumeSizeMB)
        {
            string args;
            string outputDir = Path.GetDirectoryName(archivePath);
            string fileName = Path.GetFileNameWithoutExtension(archivePath);
            files = files.Select(x => GetRelativePathManual(workPath, x).Substring(Path.GetFileName(workPath).Length + 1)).ToList();
            // 分卷参数(当需要分卷时)
            string volumeArg = volumeSizeMB > 0 ? $"-v{volumeSizeMB}m" : "";
            // 处理长文件列表
            if (GetCommandLength(files) > MAX_CMD_LENGTH)
            {
                string listFile = Path.GetTempFileName();
                File.WriteAllLines(listFile, files);
                args = $"a -mx=9 {volumeArg} \"{Path.Combine(outputDir, fileName)}\" @\"{listFile}\"";
            }
            else
            {
                args = $"a -mx=9 {volumeArg} \"{Path.Combine(outputDir, fileName)}\" {string.Join(" ", files.Select(f => $"\"{f}\""))}";
            }
            using (Process process = new Process())
            {
                process.StartInfo.FileName = _7zPath;
                process.StartInfo.Arguments = args;
                process.StartInfo.UseShellExecute = false;
                process.StartInfo.RedirectStandardOutput = true; // 允许重定向标准输出
                //process.StartInfo.CreateNoWindow = true;
                process.StartInfo.WorkingDirectory = workPath;
                process.Start();
                Console.WriteLine($"开始压缩:{fileName}");
                string output = process.StandardOutput.ReadToEnd(); // 读取输出内容
                process.WaitForExit();
                Console.WriteLine(output);
                if (process.ExitCode == 0)
                {
                    Console.WriteLine($"压缩完成:{fileName}");
                }
                else
                {
                    Console.WriteLine($"压缩失败:{fileName} (错误码: {process.ExitCode})");
                }
            }
        }
        static int GetCommandLength(List<string> files)
        {
            // 估算命令行长度(每个文件路径长度+3个引号空格)
            return files.Sum(f => f.Length + 3) + 50; // 50为其他参数长度余量
        }
        static void ProcessFiles(string inputDir, string outputDir, long volumeSizeMB)
        {
            var allFiles = GetFilesDepthFirst(inputDir).ToList();
            var fileGroups = new List<List<string>>();
            var currentGroup = new List<string>();
            long currentSize = 0;
            foreach (var file in allFiles)
            {
                var fileInfo = new FileInfo(file);
                long fileSize = fileInfo.Length;
                // 大文件单独处理
                if (fileSize > 4L * 1024 * 1024 * 1024)
                {
                    if (currentGroup.Count > 0)
                    {
                        fileGroups.Add(currentGroup);
                        currentGroup = new List<string>();
                    }
                    
                    fileGroups.Add(new List<string> { file });
                    continue;
                }
                // 普通文件分组
                if (currentSize + fileSize > volumeSizeMB * 1024L * 1024)
                {
                    fileGroups.Add(currentGroup);
                    currentGroup = new List<string>();
                    currentSize = 0;
                }
                currentGroup.Add(file);
                currentSize += fileSize;
            }
            if (currentGroup.Count > 0) fileGroups.Add(currentGroup);
            // 并行处理压缩任务
            Parallel.ForEach(fileGroups, (group, state, index) =>
            {
                string archiveName = $"{Path.GetFileName(inputDir)}_{index + 1:D4}";
                bool isSingleBigFile = group.Count == 1 &&
                    new FileInfo(group[0]).Length > 4L * 1024 * 1024 * 1024;
                // 确定分卷参数
                long volSize = 0;
                if (isSingleBigFile &&
                    new FileInfo(group[0]).Length > SINGLE_FILE_MAX)
                {
                    volSize = volumeSizeMB;
                }
                CompressWith7z(
                    inputDir,
                    Path.Combine(outputDir, $"{archiveName}.7z"),
                    group,
                    volSize
                );
            });
        }
        static void Main(string[] args)
        {
            // 检查7z是否可用
            try
            {
                using (Process process = new Process())
                {
                    process.StartInfo.FileName = _7zPath;
                    process.StartInfo.Arguments = "";
                    process.StartInfo.RedirectStandardOutput = true;
                    process.StartInfo.UseShellExecute = false;
                    process.Start();
                }
            }
            catch
            {
                Console.WriteLine("找不到7z.exe,请确保已安装并添加到环境变量");
                return;
            }
            Parser.Default.ParseArguments<Options>(args)
                .WithParsed(opts =>
                {
                    string inputDir = Path.GetFullPath(opts.Input).TrimEnd(Path.DirectorySeparatorChar, '/');
                    string outputDir = Path.Combine(
                        Path.GetFullPath(opts.Output),
                        Path.GetFileName(inputDir));
                    Directory.CreateDirectory(outputDir);
                    long volumeSize = opts.VolumeSizeMB > 10?opts.VolumeSizeMB : 10;
                    ProcessFiles(inputDir, outputDir, volumeSize);
                })
                .WithNotParsed(errs => errs.ToList().ForEach(e => Console.WriteLine(e)));
        }

        public static string GetRelativePathManual(string basePath, string fullPath)
        {
            if (basePath == fullPath) return "";
            var baseUri = new Uri(basePath);
            var fullUri = new Uri(fullPath);
            return Uri.UnescapeDataString(baseUri.MakeRelativeUri(fullUri).ToString().Replace('/', Path.DirectorySeparatorChar));
        }
    }
}

posted on 2025-02-11 13:57  准爵  阅读(24)  评论(0)    收藏  举报