优化下载内存占用 添加下载者统计

This commit is contained in:
root 2024-12-07 17:02:41 +08:00
parent c4fbd572cc
commit d09dcf7ecf
4 changed files with 164 additions and 34 deletions

View file

@ -436,5 +436,44 @@ namespace iFileProxy.Controllers
}); });
} }
} }
/// <summary>
/// 获取任务的下载历史
/// </summary>
[HttpGet("GetDownloadHistory/{taskId}")]
public async Task<ActionResult<CommonRsp>> GetDownloadHistory(string taskId)
{
try
{
// 先检查任务是否存在
var taskInfo = JsonSerializer.Deserialize<List<TaskInfo>>(_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 = "获取下载历史失败"
});
}
}
} }
} }

View file

@ -2,7 +2,7 @@
using iFileProxy.Helpers; using iFileProxy.Helpers;
using iFileProxy.Models; using iFileProxy.Models;
using iFileProxy.Services; using iFileProxy.Services;
using Serilog;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.StaticFiles; using Microsoft.AspNetCore.StaticFiles;
@ -12,11 +12,16 @@ namespace iFileProxy.Controllers
{ {
private readonly TaskManager _taskManager; private readonly TaskManager _taskManager;
private readonly AppConfig _appConfig; private readonly AppConfig _appConfig;
private readonly DatabaseGateService _dbGate;
public iProxyController(TaskManager taskManager, AppConfig appConfig) private readonly static Serilog.ILogger _logger = Log.Logger.ForContext<iProxyController>();
public iProxyController(TaskManager taskManager, AppConfig appConfig, DatabaseGateService databaseGateService)
{ {
_taskManager = taskManager; _taskManager = taskManager;
_appConfig = appConfig; _appConfig = appConfig;
_dbGate = databaseGateService;
} }
[HttpPost] [HttpPost]
@ -72,44 +77,81 @@ namespace iFileProxy.Controllers
[Route("/Download/{taskID}")] [Route("/Download/{taskID}")]
public async Task<IActionResult> DownloadFile(string taskID) public async Task<IActionResult> DownloadFile(string taskID)
{ {
string fileName = ""; try
var d = _taskManager.GetTaskListByIpAddr(HttpContext);
var taskInfo = _taskManager.GetTaskInfo(taskID);
if ((!_appConfig.SecurityOptions.AllowDifferentIPsForDownload && d.Where(x => x.TaskId == taskID).Any()) || taskInfo != null)
{ {
if (_appConfig.SecurityOptions.AllowDifferentIPsForDownload) string? fileName = null;
{ var d = _taskManager.GetTaskListByIpAddr(HttpContext);
fileName = taskInfo[0].FileName; var taskInfo = _taskManager.GetTaskInfo(taskID);
}
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 ((!_appConfig.SecurityOptions.AllowDifferentIPsForDownload && d.Where(x => x.TaskId == taskID).Any()) || taskInfo != null)
if (!MasterHelper.CheckDownloadFileExists(fileName))
{ {
return Ok(new CommonRsp() { Message = "file not exists", Retcode = 1 }); if (_appConfig.SecurityOptions.AllowDifferentIPsForDownload)
} {
// 获取文件的 MIME 类型 fileName = taskInfo[0].FileName;
var provider = new FileExtensionContentTypeProvider(); }
string contentType; else
if (!provider.TryGetContentType(filePath, out contentType)) {
{ fileName = d.Where(x => x.TaskId == taskID).FirstOrDefault()?.FileName;
contentType = "application/octet-stream"; // 默认 MIME 类型 }
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
};
} }
// 读取文件内容 return Ok(new CommonRsp() { Message = "task_id not exists", Retcode = -1 });
var fileContents = await System.IO.File.ReadAllBytesAsync(filePath); }
catch (Exception ex)
// 返回文件内容 {
return File(fileContents, contentType, Path.GetFileName(filePath)); _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 });
} }
} }
} }

View file

@ -183,4 +183,16 @@ namespace iFileProxy.Models
/// </summary> /// </summary>
public string Info { get; set; } = string.Empty; 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;
}
} }

View file

@ -80,6 +80,16 @@ namespace iFileProxy.Services
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; ) 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) public DatabaseGateService(AppConfig appConfig)
{ {
_logger.Information("Initializing DatabaseGateService..."); _logger.Information("Initializing DatabaseGateService...");
@ -1065,5 +1075,32 @@ namespace iFileProxy.Services
Data = logs Data = logs
}; };
} }
public async Task<List<DownloadHistory>> GetDownloadHistoryByTaskIdAsync(string taskId)
{
var sql = "SELECT * FROM t_download_history WHERE tid = @taskId ORDER BY time DESC";
var parameters = new Dictionary<string, object>
{
{ "@taskId", taskId }
};
return await ExecuteQueryAsync<DownloadHistory>(sql, parameters);
}
public async Task<bool> AddDownloadHistoryAsync(string taskId, string clientIp)
{
var sql = @"INSERT INTO t_download_history (tid, time, client_ip)
VALUES (@taskId, @time, @clientIp)";
var parameters = new Dictionary<string, object>
{
{ "@taskId", taskId },
{ "@time", DateTime.Now },
{ "@clientIp", clientIp }
};
var result = await ExecuteNonQueryAsync(sql, parameters);
return result > 0;
}
} }
} }