From d09dcf7ecf065d06e8a73ed0ead2178df71d8205 Mon Sep 17 00:00:00 2001 From: root Date: Sat, 7 Dec 2024 17:02:41 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96=E4=B8=8B=E8=BD=BD=E5=86=85?= =?UTF-8?q?=E5=AD=98=E5=8D=A0=E7=94=A8=20=E6=B7=BB=E5=8A=A0=E4=B8=8B?= =?UTF-8?q?=E8=BD=BD=E8=80=85=E7=BB=9F=E8=AE=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Controllers/ManagementController.cs | 39 +++++++++ src/Controllers/iProxyController.cs | 110 ++++++++++++++++-------- src/Models/Db.cs | 12 +++ src/Services/DatabaseGateService.cs | 37 ++++++++ 4 files changed, 164 insertions(+), 34 deletions(-) diff --git a/src/Controllers/ManagementController.cs b/src/Controllers/ManagementController.cs index 3ebbd49..65e9be8 100644 --- a/src/Controllers/ManagementController.cs +++ b/src/Controllers/ManagementController.cs @@ -436,5 +436,44 @@ namespace iFileProxy.Controllers }); } } + + /// + /// 获取任务的下载历史 + /// + [HttpGet("GetDownloadHistory/{taskId}")] + public async Task> GetDownloadHistory(string taskId) + { + try + { + // 先检查任务是否存在 + var taskInfo = JsonSerializer.Deserialize>(_dbGateService.GetTaskInfoByTid(taskId))?[0]; + if (taskInfo == null) + { + return Ok(new CommonRsp + { + Retcode = 1, + Message = "任务不存在" + }); + } + + var history = await _dbGateService.GetDownloadHistoryByTaskIdAsync(taskId); + + return Ok(new CommonRsp + { + Retcode = 0, + Message = "success", + Data = history + }); + } + catch (Exception ex) + { + _logger.Error(ex, "获取下载历史失败"); + return Ok(new CommonRsp + { + Retcode = 1, + Message = "获取下载历史失败" + }); + } + } } } diff --git a/src/Controllers/iProxyController.cs b/src/Controllers/iProxyController.cs index f534b04..1576e76 100644 --- a/src/Controllers/iProxyController.cs +++ b/src/Controllers/iProxyController.cs @@ -2,7 +2,7 @@ using iFileProxy.Helpers; using iFileProxy.Models; using iFileProxy.Services; - +using Serilog; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.StaticFiles; @@ -12,11 +12,16 @@ namespace iFileProxy.Controllers { private readonly TaskManager _taskManager; private readonly AppConfig _appConfig; + private readonly DatabaseGateService _dbGate; - public iProxyController(TaskManager taskManager, AppConfig appConfig) + private readonly static Serilog.ILogger _logger = Log.Logger.ForContext(); + + + public iProxyController(TaskManager taskManager, AppConfig appConfig, DatabaseGateService databaseGateService) { _taskManager = taskManager; _appConfig = appConfig; + _dbGate = databaseGateService; } [HttpPost] @@ -72,44 +77,81 @@ namespace iFileProxy.Controllers [Route("/Download/{taskID}")] public async Task DownloadFile(string taskID) { - string fileName = ""; - var d = _taskManager.GetTaskListByIpAddr(HttpContext); - var taskInfo = _taskManager.GetTaskInfo(taskID); - - if ((!_appConfig.SecurityOptions.AllowDifferentIPsForDownload && d.Where(x => x.TaskId == taskID).Any()) || taskInfo != null) + try { - if (_appConfig.SecurityOptions.AllowDifferentIPsForDownload) - { - fileName = taskInfo[0].FileName; - } - else - { - fileName = d.Where(x => x.TaskId == taskID).FirstOrDefault()?.FileName; - } - if (fileName == null) - return Ok(new CommonRsp() { Message = "file not exists or taskId error", Retcode = -1 }); + string? fileName = null; + var d = _taskManager.GetTaskListByIpAddr(HttpContext); + var taskInfo = _taskManager.GetTaskInfo(taskID); - var filePath = Path.Combine(_appConfig.DownloadOptions.SavePath, fileName); - if (!MasterHelper.CheckDownloadFileExists(fileName)) + if ((!_appConfig.SecurityOptions.AllowDifferentIPsForDownload && d.Where(x => x.TaskId == taskID).Any()) || taskInfo != null) { - return Ok(new CommonRsp() { Message = "file not exists", Retcode = 1 }); - } - // 获取文件的 MIME 类型 - var provider = new FileExtensionContentTypeProvider(); - string contentType; - if (!provider.TryGetContentType(filePath, out contentType)) - { - contentType = "application/octet-stream"; // 默认 MIME 类型 + if (_appConfig.SecurityOptions.AllowDifferentIPsForDownload) + { + fileName = taskInfo[0].FileName; + } + else + { + fileName = d.Where(x => x.TaskId == taskID).FirstOrDefault()?.FileName; + } + + if (fileName == null) + { + return Ok(new CommonRsp() { Message = "file not exists or taskId error", Retcode = -1 }); + } + + var filePath = Path.Combine(_appConfig.DownloadOptions.SavePath, fileName); + if (!MasterHelper.CheckDownloadFileExists(fileName)) + { + return Ok(new CommonRsp() { Message = "file not exists", Retcode = 1 }); + } + + // 获取文件的 MIME 类型 + var provider = new FileExtensionContentTypeProvider(); + string contentType; + if (!provider.TryGetContentType(filePath, out contentType)) + { + contentType = "application/octet-stream"; + } + + // 获取文件信息 + var fileInfo = new FileInfo(filePath); + + // 设置响应头 + Response.Headers.Add("Content-Disposition", $"attachment; filename=\"{fileName}\""); + Response.Headers.Add("Content-Length", fileInfo.Length.ToString()); + + // 使用 FileStream 进行流式传输 + var fileStream = new FileStream( + filePath, + FileMode.Open, + FileAccess.Read, + FileShare.Read, + bufferSize: 81920, // 80KB 缓冲区 + useAsync: true + ); + + // 记录下载历史 + await _dbGate.AddDownloadHistoryAsync(taskID, MasterHelper.GetClientIPAddr(HttpContext)); + + // 返回文件流 + return new FileStreamResult(fileStream, contentType) + { + EnableRangeProcessing = true, // 支持断点续传 + FileDownloadName = fileName + }; } - // 读取文件内容 - var fileContents = await System.IO.File.ReadAllBytesAsync(filePath); - - // 返回文件内容 - return File(fileContents, contentType, Path.GetFileName(filePath)); + return Ok(new CommonRsp() { Message = "task_id not exists", Retcode = -1 }); + } + catch (Exception ex) + { + _logger.Error("处理下载请求失败: {ex}", ex); + return Ok(new CommonRsp() + { + Message = $"download failed: {ex.Message}", + Retcode = -1 + }); } - return Ok(new CommonRsp() { Message = "task_id not exists", Retcode = -1 }); - } } } diff --git a/src/Models/Db.cs b/src/Models/Db.cs index 575ba04..2ec69bc 100644 --- a/src/Models/Db.cs +++ b/src/Models/Db.cs @@ -183,4 +183,16 @@ namespace iFileProxy.Models /// public string Info { get; set; } = string.Empty; } + + public class DownloadHistory + { + [JsonProperty("tid")] + public string TaskId { get; set; } = string.Empty; + + [JsonProperty("time")] + public DateTime Time { get; set; } + + [JsonProperty("client_ip")] + public string ClientIP { get; set; } = string.Empty; + } } diff --git a/src/Services/DatabaseGateService.cs b/src/Services/DatabaseGateService.cs index 20c9463..b434be5 100644 --- a/src/Services/DatabaseGateService.cs +++ b/src/Services/DatabaseGateService.cs @@ -80,6 +80,16 @@ namespace iFileProxy.Services ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; """; + private const string CREATE_DOWNLOAD_HISTORY_TABLE_SQL = """ + CREATE TABLE IF NOT EXISTS `t_download_history` ( + `tid` varchar(48) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '任务UUID', + `time` datetime NULL DEFAULT NULL COMMENT '触发时间', + `client_ip` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '客户端IP', + PRIMARY KEY (`tid`) USING BTREE, + CONSTRAINT `t_download_history_ibfk_1` FOREIGN KEY (`tid`) REFERENCES `t_tasks_info` (`tid`) ON DELETE CASCADE ON UPDATE CASCADE + ) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic; + """; + public DatabaseGateService(AppConfig appConfig) { _logger.Information("Initializing DatabaseGateService..."); @@ -1065,5 +1075,32 @@ namespace iFileProxy.Services Data = logs }; } + + public async Task> GetDownloadHistoryByTaskIdAsync(string taskId) + { + var sql = "SELECT * FROM t_download_history WHERE tid = @taskId ORDER BY time DESC"; + var parameters = new Dictionary + { + { "@taskId", taskId } + }; + + return await ExecuteQueryAsync(sql, parameters); + } + + public async Task AddDownloadHistoryAsync(string taskId, string clientIp) + { + var sql = @"INSERT INTO t_download_history (tid, time, client_ip) + VALUES (@taskId, @time, @clientIp)"; + + var parameters = new Dictionary + { + { "@taskId", taskId }, + { "@time", DateTime.Now }, + { "@clientIp", clientIp } + }; + + var result = await ExecuteNonQueryAsync(sql, parameters); + return result > 0; + } } }