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;
+ }
}
}