1
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user