773 lines
29 KiB
C#
773 lines
29 KiB
C#
using Applications;
|
||
using ICSharpCode.SharpZipLib.Zip;
|
||
using Microsoft.Win32;
|
||
using MySql.Data.MySqlClient;
|
||
using System;
|
||
using System.Collections.Concurrent;
|
||
using System.Collections.Generic;
|
||
using System.Diagnostics;
|
||
using System.IO;
|
||
using System.IO.Compression;
|
||
using System.Net;
|
||
using System.Net.NetworkInformation;
|
||
using System.Security.Cryptography;
|
||
using System.Security.Principal;
|
||
using System.ServiceProcess;
|
||
using System.Text;
|
||
using System.Text.Json;
|
||
using System.Xml.Linq;
|
||
|
||
class Program
|
||
{
|
||
static ConcurrentDictionary<string, int> fileProcessMap = new(StringComparer.OrdinalIgnoreCase);
|
||
static void Main(string[] args)
|
||
{
|
||
HttpListener listener = new HttpListener();
|
||
listener.Prefixes.Add("http://localhost:48081/");
|
||
listener.Start();
|
||
// Console.WriteLine("Listening on http://localhost:48081/");
|
||
|
||
while (true)
|
||
{
|
||
var context = listener.GetContext();
|
||
ThreadPool.QueueUserWorkItem(_ => HandleRequestAsync(context));
|
||
}
|
||
}
|
||
static async Task HandleRequestAsync(HttpListenerContext context)
|
||
{
|
||
// 添加 CORS 响应头
|
||
context.Response.AddHeader("Access-Control-Allow-Origin", "*");
|
||
context.Response.AddHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
|
||
context.Response.AddHeader("Access-Control-Allow-Headers", "Content-Type, tenant-id");
|
||
|
||
// 处理预检请求
|
||
if (context.Request.HttpMethod == "OPTIONS")
|
||
{
|
||
context.Response.StatusCode = (int)HttpStatusCode.OK;
|
||
context.Response.Close();
|
||
return;
|
||
}
|
||
string path = context.Request.QueryString["path"];
|
||
string dbName = context.Request.QueryString["dbName"];
|
||
string filepath = context.Request.QueryString["filepath"];
|
||
string fileToOpen = context.Request.QueryString["file"];
|
||
string openType = context.Request.QueryString["type"];
|
||
string exePath = context.Request.QueryString["exepath"];
|
||
string workingDirectory = context.Request.QueryString["workingdirectory"];
|
||
// 生成文件夹
|
||
string folderPath = context.Request.QueryString["folderPath"];
|
||
// 文件URL
|
||
string filesurl = context.Request.QueryString["filesurl"];
|
||
// 文件名称
|
||
string desiredFileName = context.Request.QueryString["filename"];
|
||
// 需要被打包的 源目录路径
|
||
string sourceDir = context.Request.QueryString["sourceDir"];
|
||
string ip = context.Request.QueryString["ip"];
|
||
// 方案ID
|
||
string taskId = context.Request.QueryString["taskId"];
|
||
string action = context.Request.Url.AbsolutePath.ToLower();
|
||
string responseMessage = "";
|
||
// 压缩文件方法
|
||
if (action == "/zipfiles")
|
||
{
|
||
try
|
||
{
|
||
if (!Directory.Exists(sourceDir))
|
||
{
|
||
throw new DirectoryNotFoundException("源目录不存在!");
|
||
}
|
||
|
||
// 构造ZIP文件路径(同级目录,同名.zip)
|
||
string parentDir = Directory.GetParent(sourceDir).FullName;
|
||
string folderName = new DirectoryInfo(sourceDir).Name;
|
||
string outputZipPath = Path.Combine(parentDir, $"{folderName}.zip");
|
||
|
||
// 如果ZIP已存在,先删除(可选)
|
||
if (File.Exists(outputZipPath))
|
||
{
|
||
File.Delete(outputZipPath);
|
||
}
|
||
|
||
// 使用SharpZipLib打包
|
||
using (var zipStream = new ZipOutputStream(File.Create(outputZipPath)))
|
||
{
|
||
zipStream.SetLevel(5); // 压缩级别 (0-9)
|
||
|
||
foreach (var file in Directory.GetFiles(sourceDir, "*", SearchOption.AllDirectories))
|
||
{
|
||
string relativePath = file.Substring(sourceDir.Length + 1);
|
||
var entry = new ZipEntry(relativePath)
|
||
{
|
||
DateTime = File.GetLastWriteTime(file),
|
||
IsUnicodeText = true
|
||
};
|
||
|
||
// 保留文件属性(隐藏、只读等)
|
||
var fileAttributes = File.GetAttributes(file);
|
||
entry.ExternalFileAttributes = (int)fileAttributes << 16;
|
||
|
||
zipStream.PutNextEntry(entry);
|
||
|
||
using (var fileStream = File.OpenRead(file))
|
||
{
|
||
fileStream.CopyTo(zipStream);
|
||
}
|
||
|
||
zipStream.CloseEntry();
|
||
}
|
||
}
|
||
|
||
// 返回ZIP文件的绝对路径
|
||
responseMessage = Path.GetFullPath(outputZipPath);
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
throw new Exception($"压缩失败: {ex.Message}");
|
||
}
|
||
}
|
||
|
||
|
||
// 下载文件方法
|
||
if (action == "/downloadfiles")
|
||
{
|
||
try
|
||
{
|
||
string downloadedFilePath = await DownloadFileAsync(filesurl, folderPath);
|
||
Console.WriteLine($"文件已下载到: {downloadedFilePath}");
|
||
|
||
if (IsZipFile(downloadedFilePath))
|
||
{
|
||
Console.WriteLine("检测到ZIP文件,开始解压...");
|
||
string extractPath = Path.Combine(folderPath, Path.GetFileNameWithoutExtension(downloadedFilePath));
|
||
//System.IO.Compression.ZipFile.ExtractToDirectory(downloadedFilePath, folderPath);
|
||
UnzipWithChineseNames(downloadedFilePath, folderPath);
|
||
Console.WriteLine($"解压完成,文件保存在: {extractPath}");
|
||
|
||
// 删除原ZIP文件
|
||
File.Delete(downloadedFilePath);
|
||
Console.WriteLine("已删除原ZIP文件");
|
||
}
|
||
else
|
||
{
|
||
Console.WriteLine("文件不是ZIP格式,保留原文件");
|
||
// 获取原文件扩展名
|
||
string originalExtension = Path.GetExtension(downloadedFilePath);
|
||
|
||
// 构建新文件名(保留原扩展名)
|
||
string newFileName = $"{desiredFileName}{originalExtension}";
|
||
string newFilePath = Path.Combine(folderPath, newFileName);
|
||
|
||
// 如果目标文件已存在,先删除
|
||
if (File.Exists(newFilePath))
|
||
{
|
||
File.Delete(newFilePath);
|
||
}
|
||
|
||
File.Move(downloadedFilePath, newFilePath);
|
||
Console.WriteLine($"文件已重命名为: {newFilePath}");
|
||
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Console.WriteLine($"操作失败: {ex.Message}");
|
||
}
|
||
}
|
||
if (action == "/openjudgement")
|
||
{
|
||
string relativePath = "";
|
||
if (openType == "1")
|
||
{
|
||
string exeName = exePath;
|
||
relativePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, exeName);
|
||
}
|
||
else
|
||
{
|
||
var startInfo = new ProcessStartInfo
|
||
{
|
||
FileName = exePath,
|
||
WorkingDirectory = workingDirectory, // 必须!
|
||
UseShellExecute = true // 或 false,根据需要
|
||
};
|
||
// 检测一下端口是否被占用48080
|
||
if (!IsPortInUse("judgement"))
|
||
{
|
||
Process.Start(startInfo);
|
||
}
|
||
|
||
}
|
||
}
|
||
else if (action == "/start")
|
||
{
|
||
if (path == "navicat")
|
||
{
|
||
// 启动指定服务(MySQL 服务)//需要用管理员身份启动
|
||
string serviceName = "wampmysqld"; // ← 请确认服务名
|
||
try
|
||
{
|
||
using (var service = new ServiceController(serviceName))
|
||
{
|
||
if (service.Status != ServiceControllerStatus.Running && service.Status != ServiceControllerStatus.StartPending)
|
||
{
|
||
Console.WriteLine($"🟡 正在启动服务:{serviceName}...");
|
||
service.Start();
|
||
service.WaitForStatus(ServiceControllerStatus.Running, TimeSpan.FromSeconds(10));
|
||
Console.WriteLine($"✅ 服务 {serviceName} 启动成功");
|
||
}
|
||
else
|
||
{
|
||
Console.WriteLine($"✅ 服务 {serviceName} 已在运行中");
|
||
}
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Console.WriteLine($"❌ 启动服务失败:{ex.Message}");
|
||
}
|
||
// 获取当前用户的 SID
|
||
string userSid = WindowsIdentity.GetCurrent().User?.Value;
|
||
if (userSid == null)
|
||
{
|
||
Console.WriteLine("❌ 无法获取当前用户 SID");
|
||
return;
|
||
}
|
||
string subKeyPath = $@"{userSid}\Software\PremiumSoft\Navicat\Servers\答题专用";
|
||
Console.WriteLine("当前用户 subKeyPath" + subKeyPath);
|
||
|
||
try
|
||
{
|
||
// 打开HKEY_USERS根键
|
||
using (RegistryKey baseKey = Registry.Users)
|
||
{
|
||
// 创建或打开子项,写权限需要true
|
||
using (RegistryKey key = baseKey.CreateSubKey(subKeyPath, true))
|
||
{
|
||
if (key == null)
|
||
{
|
||
Console.WriteLine("❌ 无法创建或打开注册表项");
|
||
return;
|
||
}
|
||
|
||
// 写入键值示例
|
||
key.SetValue("Host", "localhost");
|
||
key.SetValue("Port", 6033, RegistryValueKind.DWord);
|
||
key.SetValue("User", "root");
|
||
key.SetValue("Password", ""); // 注意密码如果要存可能是加密的
|
||
// 其他必要键值写入
|
||
Console.WriteLine("✅ 注册表写入成功");
|
||
}
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Console.WriteLine($"❌ 写入注册表失败:{ex.Message}");
|
||
}
|
||
|
||
|
||
// 执行数据库创建与初始化
|
||
string connectionString = "server=localhost;port=6033;user=root;password=;charset=utf8mb4;";
|
||
|
||
try
|
||
{
|
||
using (var connection = new MySqlConnection(connectionString))
|
||
{
|
||
connection.Open();
|
||
var createDbCmd = connection.CreateCommand();
|
||
createDbCmd.CommandText = $"CREATE DATABASE IF NOT EXISTS `{dbName}` DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_general_ci;";
|
||
createDbCmd.ExecuteNonQuery();
|
||
Console.WriteLine($"✅ 数据库 {dbName} 创建成功");
|
||
}
|
||
|
||
string sqlScript = File.ReadAllText(filepath);
|
||
|
||
using (var dbConn = new MySqlConnection(connectionString + $"database={dbName};"))
|
||
{
|
||
dbConn.Open();
|
||
|
||
var sqlStatements = SplitSqlStatements(sqlScript);
|
||
|
||
foreach (var statement in sqlStatements)
|
||
{
|
||
if (string.IsNullOrWhiteSpace(statement)) continue;
|
||
|
||
using var cmd = dbConn.CreateCommand();
|
||
cmd.CommandText = statement.Trim();
|
||
cmd.ExecuteNonQuery();
|
||
Console.WriteLine($"执行成功:{Truncate(statement, 80)}");
|
||
}
|
||
|
||
Console.WriteLine("✅ 所有 SQL 语句执行完成,数据库初始化成功");
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Console.WriteLine($"❌ 出错了:{ex.Message}");
|
||
}
|
||
}
|
||
try
|
||
{
|
||
string? resolvedPath = ResolveAppPath(path);
|
||
if (!string.IsNullOrEmpty(resolvedPath) && File.Exists(resolvedPath))
|
||
{
|
||
ProcessStartInfo psi = new ProcessStartInfo
|
||
{
|
||
FileName = resolvedPath,
|
||
Arguments = string.IsNullOrEmpty(fileToOpen) ? "" : $"\"{fileToOpen}\"",
|
||
UseShellExecute = true
|
||
};
|
||
|
||
var process = Process.Start(psi);
|
||
if (process != null)
|
||
{
|
||
if (!string.IsNullOrEmpty(fileToOpen))
|
||
{
|
||
fileProcessMap[fileToOpen] = process.Id;
|
||
responseMessage = $"Started {resolvedPath} with {fileToOpen}";
|
||
}
|
||
else
|
||
{
|
||
responseMessage = $"Started {resolvedPath} (no file)";
|
||
}
|
||
}
|
||
else
|
||
{
|
||
responseMessage = "Failed to start process.";
|
||
}
|
||
}
|
||
else if (!string.IsNullOrEmpty(fileToOpen) && File.Exists(fileToOpen))
|
||
{
|
||
// 使用默认程序打开文件
|
||
ProcessStartInfo psi = new ProcessStartInfo
|
||
{
|
||
FileName = fileToOpen,
|
||
UseShellExecute = true
|
||
};
|
||
var process = Process.Start(psi);
|
||
if (process != null)
|
||
{
|
||
fileProcessMap[fileToOpen] = process.Id;
|
||
responseMessage = $"Opened {fileToOpen} with default program (PID: {process.Id})";
|
||
}
|
||
else
|
||
{
|
||
responseMessage = $"Failed to open {fileToOpen}.";
|
||
}
|
||
}
|
||
else
|
||
{
|
||
responseMessage = "Missing or invalid 'path' or 'file'.";
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
responseMessage = "Error: " + ex.Message;
|
||
}
|
||
}
|
||
else if (action == "/stop")
|
||
{
|
||
if (!string.IsNullOrEmpty(fileToOpen))
|
||
{
|
||
if (fileProcessMap.TryRemove(fileToOpen, out int pid))
|
||
{
|
||
try
|
||
{
|
||
var proc = Process.GetProcessById(pid);
|
||
proc.Kill();
|
||
responseMessage = $"Stopped process for file: {fileToOpen} (PID: {pid})";
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
responseMessage = $"Failed to stop process: {ex.Message}";
|
||
}
|
||
}
|
||
else
|
||
{
|
||
responseMessage = $"No tracked process for file: {fileToOpen}";
|
||
}
|
||
}
|
||
else
|
||
{
|
||
responseMessage = "Missing 'file' parameter.";
|
||
}
|
||
}
|
||
else if (action == "/check")
|
||
{
|
||
string apiUrl = "http://" + ip + ":48080/admin-api/exam/app/getAppCheckList/" + taskId; // ← 替换成你的接口地址
|
||
List<AppCheck> softwareList = await FetchSoftwareListFromApi(apiUrl);
|
||
List<string> result = new List<string>();
|
||
foreach (AppCheck softwareLine in softwareList)
|
||
{
|
||
var keywords = softwareLine.appName;
|
||
string? resolvedPath = ResolveAppPath(keywords);
|
||
if (!string.IsNullOrEmpty(resolvedPath) && File.Exists(resolvedPath))
|
||
{
|
||
result.Add(keywords + ":True");
|
||
}
|
||
else
|
||
{
|
||
result.Add(keywords + ":False");
|
||
}
|
||
}
|
||
// 将 result 转换为 JSON 并返回
|
||
responseMessage = JsonSerializer.Serialize(result);
|
||
}
|
||
else if (action == "/closeapps")
|
||
{
|
||
foreach (var proc in Process.GetProcesses())
|
||
{
|
||
try
|
||
{
|
||
// 主窗口句柄不为 0,表示是一个窗口程序(用户级应用)
|
||
if (proc.MainWindowHandle != IntPtr.Zero &&
|
||
proc.SessionId != 0 &&
|
||
proc.Responding)
|
||
{
|
||
Console.WriteLine($"App: {proc.ProcessName} (PID: {proc.Id}) - Title: {proc.MainWindowTitle}");
|
||
if (!proc.MainWindowTitle.Contains("ExamStudent") && !proc.MainWindowTitle.Contains("Applications"))
|
||
{
|
||
var procs = Process.GetProcessById(proc.Id);
|
||
procs.Kill();
|
||
}
|
||
}
|
||
}
|
||
catch
|
||
{
|
||
// 一些系统进程可能抛异常,忽略
|
||
}
|
||
}
|
||
}
|
||
else if (action == "/allapps")
|
||
{
|
||
List<string> result = new List<string>();
|
||
foreach (var proc in Process.GetProcesses())
|
||
{
|
||
try
|
||
{
|
||
// 主窗口句柄不为 0,表示是一个窗口程序(用户级应用)
|
||
if (proc.MainWindowHandle != IntPtr.Zero &&
|
||
proc.SessionId != 0 &&
|
||
proc.Responding)
|
||
{
|
||
Console.WriteLine($"App: {proc.ProcessName} (PID: {proc.Id}) - Title: {proc.MainWindowTitle}");
|
||
result.Add(proc.ProcessName);
|
||
}
|
||
}
|
||
catch
|
||
{
|
||
// 一些系统进程可能抛异常,忽略
|
||
}
|
||
}
|
||
responseMessage = JsonSerializer.Serialize(result);
|
||
}
|
||
|
||
|
||
try
|
||
{
|
||
context.Response.ContentType = "text/plain";
|
||
context.Response.ContentEncoding = System.Text.Encoding.UTF8;
|
||
byte[] buffer = System.Text.Encoding.UTF8.GetBytes(CreateJsonResponse(200, responseMessage, "") ?? "No response");
|
||
context.Response.ContentLength64 = buffer.Length;
|
||
await context.Response.OutputStream.WriteAsync(buffer, 0, buffer.Length);
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Console.WriteLine("Response write error: " + ex.Message);
|
||
}
|
||
finally
|
||
{
|
||
context.Response.OutputStream.Close();
|
||
}
|
||
}
|
||
|
||
static string? ResolveAppPath(string appName)
|
||
{
|
||
var fromReg = FindInRegistry(appName);
|
||
if (!string.IsNullOrEmpty(fromReg)) return fromReg;
|
||
|
||
return null;
|
||
}
|
||
static string? FindInRegistry(string exeName)
|
||
{
|
||
using var regKey = Registry.ClassesRoot.OpenSubKey("Applications");
|
||
if (regKey != null)
|
||
{
|
||
// 获取所有子项
|
||
foreach (var subKeyName in regKey.GetSubKeyNames())
|
||
{
|
||
// 打开每个应用程序子键
|
||
using var appKey = regKey.OpenSubKey(subKeyName);
|
||
if (appKey != null)
|
||
{
|
||
// 查看是否有 "shell\open\command" 子键,这里存储着启动路径
|
||
using var commandKey = appKey.OpenSubKey(@"shell\open\command");
|
||
if (commandKey != null)
|
||
{
|
||
var commandValue = commandKey.GetValue("") as string;
|
||
if (commandValue != null)
|
||
{
|
||
if (subKeyName.IndexOf(exeName, StringComparison.OrdinalIgnoreCase) >= 0)
|
||
{
|
||
return commandValue.Split('"')[1];
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
return null;
|
||
}
|
||
static async Task<List<AppCheck>> FetchSoftwareListFromApi(string url)
|
||
{
|
||
try
|
||
{
|
||
using HttpClient client = new();
|
||
var response = await client.GetStringAsync(url);
|
||
var apiResponse = JsonSerializer.Deserialize<ApiResponse>(response);
|
||
|
||
if (apiResponse != null && apiResponse.code == 0)
|
||
{
|
||
// 如果 code 为 0,说明数据有效,返回 softwareList
|
||
return apiResponse.data ?? new List<AppCheck>();
|
||
}
|
||
else
|
||
{
|
||
// Console.WriteLine($"API error: {apiResponse?.msg}");
|
||
return new List<AppCheck>();
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
// Console.WriteLine("获取接口数据失败: " + ex.Message);
|
||
return new List<AppCheck>();
|
||
}
|
||
}
|
||
static string CreateJsonResponse<T>(int code, T data, string msg)
|
||
{
|
||
var response = new StandardResponse<T>
|
||
{
|
||
code = code,
|
||
data = data,
|
||
msg = msg
|
||
};
|
||
return JsonSerializer.Serialize(response);
|
||
}
|
||
static string Truncate(string value, int maxLength)
|
||
{
|
||
if (string.IsNullOrEmpty(value)) return value;
|
||
return value.Length <= maxLength ? value : value.Substring(0, maxLength) + "...";
|
||
}
|
||
|
||
static List<string> SplitSqlStatements(string sqlScript)
|
||
{
|
||
var statements = new List<string>();
|
||
string delimiter = ";";
|
||
int pos = 0, lastPos = 0, length = sqlScript.Length;
|
||
|
||
while (pos < length)
|
||
{
|
||
if (MatchKeyword(sqlScript, pos, "DELIMITER"))
|
||
{
|
||
string prevSegment = sqlScript.Substring(lastPos, pos - lastPos);
|
||
var prevStatements = SplitByDelimiter(prevSegment, delimiter);
|
||
statements.AddRange(prevStatements);
|
||
|
||
int delimStart = pos + "DELIMITER".Length;
|
||
while (delimStart < length && char.IsWhiteSpace(sqlScript[delimStart])) delimStart++;
|
||
int delimEnd = delimStart;
|
||
while (delimEnd < length && !char.IsWhiteSpace(sqlScript[delimEnd]) && sqlScript[delimEnd] != '\r' && sqlScript[delimEnd] != '\n') delimEnd++;
|
||
delimiter = sqlScript.Substring(delimStart, delimEnd - delimStart);
|
||
pos = delimEnd;
|
||
while (pos < length && sqlScript[pos] != '\n') pos++;
|
||
pos++;
|
||
lastPos = pos;
|
||
}
|
||
else
|
||
{
|
||
pos++;
|
||
}
|
||
}
|
||
|
||
if (lastPos < length)
|
||
{
|
||
string lastSegment = sqlScript.Substring(lastPos);
|
||
var lastStatements = SplitByDelimiter(lastSegment, delimiter);
|
||
statements.AddRange(lastStatements);
|
||
}
|
||
|
||
return statements;
|
||
}
|
||
|
||
static bool MatchKeyword(string text, int pos, string keyword)
|
||
{
|
||
if (pos + keyword.Length > text.Length) return false;
|
||
var segment = text.Substring(pos, keyword.Length);
|
||
return string.Equals(segment, keyword, StringComparison.OrdinalIgnoreCase);
|
||
}
|
||
|
||
static List<string> SplitByDelimiter(string sql, string delimiter)
|
||
{
|
||
var list = new List<string>();
|
||
int start = 0, idx;
|
||
while ((idx = sql.IndexOf(delimiter, start, StringComparison.Ordinal)) >= 0)
|
||
{
|
||
list.Add(sql.Substring(start, idx - start));
|
||
start = idx + delimiter.Length;
|
||
}
|
||
if (start < sql.Length)
|
||
list.Add(sql.Substring(start));
|
||
return list;
|
||
}
|
||
public static bool IsPortInUse(string name)
|
||
{
|
||
foreach (var proc in Process.GetProcesses())
|
||
{
|
||
try
|
||
{
|
||
// 主窗口句柄不为 0,表示是一个窗口程序(用户级应用)
|
||
if (proc.SessionId != 0 &&
|
||
proc.Responding)
|
||
{
|
||
if (proc.ProcessName.Contains(name))
|
||
{
|
||
var procs = Process.GetProcessById(proc.Id);
|
||
return true;
|
||
}
|
||
}
|
||
}
|
||
|
||
catch
|
||
{
|
||
// 一些系统进程可能抛异常,忽略
|
||
}
|
||
}
|
||
return false; // 端口可用
|
||
}
|
||
/// <summary>
|
||
/// 解压ZIP文件并保留中文文件名
|
||
/// </summary>
|
||
/// <param name="zipFilePath">ZIP文件路径</param>
|
||
/// <param name="outputFolder">解压目标文件夹</param>
|
||
public static void UnzipWithChineseNames(string zipFilePath, string outputFolder)
|
||
{
|
||
// ✅ 注册编码提供程序(仅.NET Core / .NET 5 + 需要)
|
||
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
|
||
|
||
// ✅ 设置 GBK 编码(或 UTF-8)
|
||
ZipStrings.CodePage = Encoding.GetEncoding("GBK").CodePage; // 或 936 / UTF8
|
||
|
||
if (!Directory.Exists(outputFolder))
|
||
Directory.CreateDirectory(outputFolder);
|
||
|
||
using (var fs = File.OpenRead(zipFilePath))
|
||
using (var zipInputStream = new ZipInputStream(fs))
|
||
{
|
||
ZipEntry entry;
|
||
while ((entry = zipInputStream.GetNextEntry()) != null)
|
||
{
|
||
// 处理路径(统一替换路径分隔符为当前系统的分隔符)
|
||
string entryName = entry.Name.Replace('/', Path.DirectorySeparatorChar);
|
||
string fullPath = Path.Combine(outputFolder, entryName);
|
||
|
||
if (entry.IsDirectory)
|
||
{
|
||
// 如果是目录,确保创建该目录
|
||
if (!Directory.Exists(fullPath))
|
||
{
|
||
Directory.CreateDirectory(fullPath);
|
||
// 设置目录的修改时间(可选)
|
||
Directory.SetLastWriteTime(fullPath, entry.DateTime);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
// 如果是文件,确保父目录存在
|
||
string directoryName = Path.GetDirectoryName(fullPath);
|
||
if (!string.IsNullOrEmpty(directoryName) && !Directory.Exists(directoryName))
|
||
{
|
||
Directory.CreateDirectory(directoryName);
|
||
}
|
||
|
||
// 写入文件内容
|
||
using (var streamWriter = File.Create(fullPath))
|
||
{
|
||
byte[] buffer = new byte[4096];
|
||
int size;
|
||
while ((size = zipInputStream.Read(buffer, 0, buffer.Length)) > 0)
|
||
{
|
||
streamWriter.Write(buffer, 0, size);
|
||
}
|
||
}
|
||
|
||
// 设置文件的修改时间
|
||
File.SetLastWriteTime(fullPath, entry.DateTime);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
static async Task<string> DownloadFileAsync(string url, string saveDir)
|
||
{
|
||
using (var httpClient = new HttpClient())
|
||
{
|
||
// 确保目录存在
|
||
Directory.CreateDirectory(saveDir);
|
||
|
||
// 从URL获取文件名
|
||
Uri uri = new Uri(url);
|
||
string fileName = Path.GetFileName(uri.LocalPath);
|
||
if (string.IsNullOrEmpty(fileName))
|
||
{
|
||
fileName = $"downloaded_{DateTime.Now:yyyyMMddHHmmss}";
|
||
}
|
||
|
||
string fullPath = Path.Combine(saveDir, fileName);
|
||
|
||
// 下载文件
|
||
var response = await httpClient.GetAsync(url);
|
||
response.EnsureSuccessStatusCode();
|
||
using (var fileStream = File.Create(fullPath))
|
||
{
|
||
await response.Content.CopyToAsync(fileStream);
|
||
}
|
||
|
||
return fullPath;
|
||
}
|
||
}
|
||
// 检查文件是否是ZIP格式
|
||
static bool IsZipFile(string filePath)
|
||
{
|
||
try
|
||
{
|
||
if (filePath.Contains("zip"))
|
||
{
|
||
return true;
|
||
} else
|
||
{
|
||
return false;
|
||
}
|
||
}
|
||
catch
|
||
{
|
||
return false;
|
||
}
|
||
}
|
||
|
||
// 获取当前登录用户 SID
|
||
static string GetCurrentUserSid()
|
||
{
|
||
var sid = System.Security.Principal.WindowsIdentity.GetCurrent().User?.ToString();
|
||
if (sid == null) throw new Exception("无法获取当前用户 SID");
|
||
return $"HKEY_USERS\\{sid}";
|
||
}
|
||
|
||
class StandardResponse<T>
|
||
{
|
||
public int code { get; set; } // 0 表示成功,其他表示失败
|
||
public T? data { get; set; } // 实际数据内容
|
||
public string msg { get; set; } = ""; // 消息提示
|
||
}
|
||
|
||
}
|
||
|
||
|
||
|