This commit is contained in:
2025-05-16 23:14:52 +08:00
parent 5080850523
commit c6d62a68ca
25 changed files with 543 additions and 60 deletions

View File

@@ -9,8 +9,11 @@ global using System.Net;
global using System.Runtime.Serialization;
global using System.Security.Authentication;
global using System.Security.Claims;
global using System.Security.Cryptography;
global using System.Text;
global using System.Text.RegularExpressions;
global using System.Web;
global using System.Xml.Linq;
global using Dapper;
global using FluentMigrator;
global using FluentMigrator.Runner;
@@ -47,9 +50,6 @@ global using Yes.Infrastructure.Authorizations.Identity;
global using Yes.Infrastructure.Authorizations.Identity.Authroizations;
global using Yes.Infrastructure.Authorizations.Identity.Context;
global using Yes.Infrastructure.Data.DbContexts;
global using Yes.Infrastructure.Migrator.Migrations;
global using System.Security;
global using System.Security.Cryptography;
global using Yes.Infrastructure.Helpers;
global using Microsoft.AspNetCore.Authentication.JwtBearer;
global using System.Xml.Linq;
global using Yes.Infrastructure.Migrator.Migrations;
global using static ICSharpCode.SharpZipLib.Zip.Compression.Deflater;

View File

@@ -1,7 +1,91 @@
namespace Yes.Infrastructure.Helpers

namespace Yes.Infrastructure.Helpers
{
public class ZipHelper
{
public static void ExtractAndProcessZip(string zipFilePath, string targetDirectory, string folderName)
{
#region 参数验证
if (string.IsNullOrEmpty(zipFilePath))
throw new ArgumentException("ZIP 文件路径不能为空。", nameof(zipFilePath));
if (string.IsNullOrEmpty(targetDirectory))
throw new ArgumentException("目标目录路径不能为空。", nameof(targetDirectory));
if (!File.Exists(zipFilePath))
throw new FileNotFoundException("指定的 ZIP 文件不存在。", zipFilePath);
if (!Directory.Exists(targetDirectory))
Directory.CreateDirectory(targetDirectory);
#endregion
string tempExtractPath = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
Directory.CreateDirectory(tempExtractPath);
try
{
// 解压到临时目录
System.IO.Compression.ZipFile.ExtractToDirectory(zipFilePath, tempExtractPath, overwriteFiles: true);
// 找到实际文件目录(跳过单层空文件夹)
string contentRoot = GetContentRootDirectory(tempExtractPath);
string targetFolderPath = Path.Combine(targetDirectory, folderName);
#region 创建目标文件夹并移动文件
Directory.CreateDirectory(targetFolderPath);
foreach (var file in Directory.GetFiles(contentRoot))
{
string destFile = Path.Combine(targetFolderPath, Path.GetFileName(file));
File.Move(file, destFile, true);
}
foreach (var dir in Directory.GetDirectories(contentRoot))
{
string destDir = Path.Combine(targetFolderPath, Path.GetFileName(dir));
// 若目标目录存在,先删除
if (Directory.Exists(destDir))
{
Directory.Delete(destDir, recursive: true);
}
Directory.Move(dir, destDir);
}
#endregion
}
finally
{
// 清理临时目录
if (Directory.Exists(tempExtractPath))
Directory.Delete(tempExtractPath, recursive: true);
}
}
/// <summary>
/// 递归查找实际内容目录(跳过仅包含单个文件夹的层级)
/// </summary>
private static string GetContentRootDirectory(string startPath)
{
string currentPath = startPath;
while (true)
{
string[] subDirs = Directory.GetDirectories(currentPath);
string[] files = Directory.GetFiles(currentPath);
if (files.Length > 0 || subDirs.Length == 0)
return currentPath;
if (subDirs.Length == 1)
currentPath = subDirs[0];
else
return currentPath; // 多个子目录,停止递归
}
}
public static void ExtractToDirectory(string zipPath, string targetDirectory, string themeName)
{
if (!Directory.Exists(targetDirectory))
@@ -100,24 +184,91 @@
}
private static string SanitizeEntryPath(string entryPath, string extractRoot)
{
// 路径安全处理
string fullPath = Path.GetFullPath(Path.Combine(extractRoot, entryPath));
// 验证是否在解压根目录内(防止路径遍历)
if (!fullPath.StartsWith(Path.GetFullPath(extractRoot)))
{
throw new SecurityException("非法路径访问!");
}
return fullPath;
}
private static bool IsSafeFile(string fileName)
{
var ext = Path.GetExtension(fileName).ToLower();
return new[] { ".liquid", ".js", ".css", ".html", ".md", ".ttf", ".svg", ".eot", ".woff", ".woff2", ".toml", ".xml" }.Contains(ext);
}
private static string GenerateSafeFolderName(string input, int maxLength = 200, char replaceSpaceWith = '_')
{
if (string.IsNullOrEmpty(input))
return "UnnamedFolder";
// 定义所有非法字符(Windows + Linux)
string invalidChars = Regex.Escape(new string(Path.GetInvalidFileNameChars()) + "<>:\"|?*");
Regex invalidCharRegex = new Regex($"[{invalidChars}]");
// 替换非法字符为空格
string sanitized = invalidCharRegex.Replace(input, " ");
// 替换连续空格为单个空格
sanitized = Regex.Replace(sanitized, @"\s+", " ").Trim();
// 替换空格为指定字符
sanitized = sanitized.Replace(" ", replaceSpaceWith.ToString());
// 处理保留名称(Windows)
string[] reservedNames = { "CON", "PRN", "AUX", "NUL", "COM1", "COM2", "COM3", "COM4", "COM5", "COM6", "COM7", "COM8", "COM9", "LPT1", "LPT2", "LPT3", "LPT4", "LPT5", "LPT6", "LPT7", "LPT8", "LPT9" };
if (Array.Exists(reservedNames, name => name.Equals(sanitized, StringComparison.OrdinalIgnoreCase)))
{
sanitized += "_safe";
}
// 截断到最大长度
if (sanitized.Length > maxLength)
{
sanitized = sanitized.Substring(0, maxLength);
}
// 如果最终结果为空,返回默认值
if (string.IsNullOrEmpty(sanitized))
{
sanitized = "UnnamedFolder";
}
return sanitized;
}
/// <summary>
/// 将指定路径的文件夹打包为 ZIP 文件
/// </summary>
/// <param name="sourceFolderPath">要压缩的文件夹路径</param>
/// <param name="destinationZipPath">生成的 ZIP 文件路径</param>
/// <param name="compressionLevel">压缩级别</param>
/// <param name="overwrite">是否覆盖已存在的 ZIP 文件</param>
public static void CompressFolderToZip(
string sourceFolderPath,
string destinationZipPath,
CompressionLevel compressionLevel = CompressionLevel.BEST_SPEED,
bool overwrite = true)
{
if (string.IsNullOrEmpty(sourceFolderPath))
throw new ArgumentException("源文件夹路径不能为空。", nameof(sourceFolderPath));
if (string.IsNullOrEmpty(destinationZipPath))
throw new ArgumentException("目标 ZIP 文件路径不能为空。", nameof(destinationZipPath));
if (!Directory.Exists(sourceFolderPath))
throw new DirectoryNotFoundException($"源文件夹 '{sourceFolderPath}' 不存在。");
var destinationDirectory = Path.GetDirectoryName(destinationZipPath);
if (!Directory.Exists(destinationDirectory))
Directory.CreateDirectory(destinationDirectory);
if (File.Exists(destinationZipPath) && overwrite)
File.Delete(destinationZipPath);
try
{
System.IO.Compression.ZipFile.CreateFromDirectory(sourceFolderPath,destinationZipPath);
}
catch (Exception ex)
{
throw new IOException($"压缩文件夹失败:{ex.Message}", ex);
}
}
}
}