支持重复文件利用

This commit is contained in:
root 2024-11-22 23:59:40 +08:00
parent d22404bdbb
commit e530cb46fe
10 changed files with 193 additions and 22 deletions

View file

@ -9,5 +9,5 @@
- [x] 基于IP查询提交的任务状态 - [x] 基于IP查询提交的任务状态
- [x] 基于IP和路由的请求次数限制 - [x] 基于IP和路由的请求次数限制
- [ ] 任务队列 - [ ] 任务队列
- [ ] 已经存在对应文件时候直接跳过代理下载 直接下载已经缓存的内容 - [x] 已经存在对应文件时候直接跳过代理下载 直接下载已经缓存的内容
- [ ] 捐赠 - [ ] 捐赠

View file

@ -110,7 +110,7 @@ namespace iFileProxy.Helpers
/// <param name="sql"></param> /// <param name="sql"></param>
/// <param name="conn"></param> /// <param name="conn"></param>
/// <returns></returns> /// <returns></returns>
public static string GetTableData(MySqlCommand sqlCmd) public static string QueryTableData(MySqlCommand sqlCmd)
{ {
DataTable dataTable = new(); DataTable dataTable = new();
using (MySqlDataAdapter adapter = new (sqlCmd)) using (MySqlDataAdapter adapter = new (sqlCmd))
@ -118,6 +118,34 @@ namespace iFileProxy.Helpers
return JsonConvert.SerializeObject(dataTable); return JsonConvert.SerializeObject(dataTable);
} }
/// <summary>
/// 获取一个json格式的数据表
/// </summary>
/// <param name="sql"></param>
/// <param name="conn"></param>
/// <returns></returns>
public string QueryTableData(string sql,string dbConfName)
{
DataTable dataTable = new();
using (MySqlDataAdapter adapter = new(new MySqlCommand(sql,GetAndOpenDBConn(dbConfName))))
adapter.Fill(dataTable);
return JsonConvert.SerializeObject(dataTable);
}
/// <summary>
/// 内部查询数据专用 当此方法暴露给C端可能造成sql注入等安全问题
/// </summary>
/// <param name="sql">SQL语句</param>
/// <param name="dbConfName">配置文件中的Description字段</param>
/// <returns>影响的行数</returns>
public int Query(string sql, string dbConfName)
{
using MySqlCommand sqlCmd = new (sql, GetAndOpenDBConn(dbConfName));
int n = sqlCmd.ExecuteNonQuery();
_logger.Debug($"查询完成, 受影响的行数: {n}");
return n;
}
public string GetTaskListByIP(string ipAddr) public string GetTaskListByIP(string ipAddr)
{ {
string sql = $"SELECT * FROM t_tasks_info WHERE client_ip = @ip_addr"; string sql = $"SELECT * FROM t_tasks_info WHERE client_ip = @ip_addr";
@ -126,7 +154,7 @@ namespace iFileProxy.Helpers
{ {
using MySqlCommand sqlCmd = new (sql,conn); using MySqlCommand sqlCmd = new (sql,conn);
sqlCmd.Parameters.AddWithValue("@ip_addr", ipAddr); sqlCmd.Parameters.AddWithValue("@ip_addr", ipAddr);
return GetTableData(sqlCmd); return QueryTableData(sqlCmd);
} }
catch (Exception e) catch (Exception e)
{ {
@ -143,7 +171,7 @@ namespace iFileProxy.Helpers
{ {
using MySqlCommand sqlCmd = new(sql, conn); using MySqlCommand sqlCmd = new(sql, conn);
sqlCmd.Parameters.AddWithValue("@tid", tid); sqlCmd.Parameters.AddWithValue("@tid", tid);
return GetTableData(sqlCmd); return QueryTableData(sqlCmd);
} }
catch (Exception e) catch (Exception e)
{ {
@ -153,13 +181,39 @@ namespace iFileProxy.Helpers
finally { conn.Close(); } finally { conn.Close(); }
} }
public TaskInfo? QueryTaskInfo(string fileName, string url, long size, TaskState status)
{
string sql = $"SELECT * FROM t_tasks_info WHERE url = @url AND size = @size AND `status` = @status AND file_name = @fileName";
MySqlConnection conn = GetAndOpenDBConn("iFileProxy_Db");
try {
MySqlCommand sqlCmd = new MySqlCommand(sql, conn);
sqlCmd.Parameters.AddWithValue("@url", url);
sqlCmd.Parameters.AddWithValue("@size", size);
sqlCmd.Parameters.AddWithValue("@status", status);
sqlCmd.Parameters.AddWithValue("@fileName", fileName);
string result = QueryTableData(sqlCmd);
List<TaskInfo>? r = JsonConvert.DeserializeObject<List<TaskInfo>>(result);
if (r != null)
{
return r[0];
}
else
return null;
}
catch (Exception e){
return null;
}
finally {
conn.Close();
}
}
public bool InsertTaskData(TaskInfo taskInfo) public bool InsertTaskData(TaskInfo taskInfo)
{ {
_logger.Debug(System.Text.Json.JsonSerializer.Serialize(taskInfo)); _logger.Debug(System.Text.Json.JsonSerializer.Serialize(taskInfo));
string sql = "INSERT INTO `t_tasks_info` (`tid`, `file_name`, `client_ip`, `add_time`, `update_time`, `status`, `url`, `size`, `hash`) " + string sql = "INSERT INTO `t_tasks_info` (`tid`, `file_name`, `client_ip`, `add_time`, `update_time`, `status`, `url`, `size`, `hash`, `tag`) " +
"VALUES (@tid, @file_name, @client_ip, @add_time, @update_time, @status, @url, @size, @hash)"; "VALUES (@tid, @file_name, @client_ip, @add_time, @update_time, @status, @url, @size, @hash, @tag)";
MySqlConnection conn = GetAndOpenDBConn("iFileProxy_Db"); MySqlConnection conn = GetAndOpenDBConn("iFileProxy_Db");
try try
@ -174,6 +228,7 @@ namespace iFileProxy.Helpers
sqlCmd.Parameters.AddWithValue("@url", taskInfo.Url); sqlCmd.Parameters.AddWithValue("@url", taskInfo.Url);
sqlCmd.Parameters.AddWithValue("@size", taskInfo.Size); sqlCmd.Parameters.AddWithValue("@size", taskInfo.Size);
sqlCmd.Parameters.AddWithValue("@hash", taskInfo.Hash); sqlCmd.Parameters.AddWithValue("@hash", taskInfo.Hash);
sqlCmd.Parameters.AddWithValue("@tag", taskInfo.Tag);
sqlCmd.ExecuteNonQuery(); sqlCmd.ExecuteNonQuery();
} }
@ -225,13 +280,13 @@ namespace iFileProxy.Helpers
using MySqlCommand sqlCmd = new (sql, conn); using MySqlCommand sqlCmd = new (sql, conn);
sqlCmd.Parameters.AddWithValue("@status", taskInfo.Status); sqlCmd.Parameters.AddWithValue("@status", taskInfo.Status);
sqlCmd.Parameters.AddWithValue("@tid", taskInfo.TaskId); sqlCmd.Parameters.AddWithValue("@tid", taskInfo.TaskId);
if (sqlCmd.ExecuteNonQuery() == 1) if (sqlCmd.ExecuteNonQuery() >= 1)
{ {
_logger.Debug($"Task: {taskInfo.TaskId} Task Status Change to {taskInfo.Status}"); _logger.Debug($"Task: {taskInfo.TaskId} Status Change to {taskInfo.Status}");
return true; return true;
} }
else else
_logger.Warning($"Task: {taskInfo.TaskId} Task Status Change Failed."); _logger.Warning($"Task: {taskInfo.TaskId} Status Change Failed.");
return false; return false;
} }
catch (Exception) catch (Exception)

View file

@ -1,4 +1,5 @@
using iFileProxy.Models; using iFileProxy.Config;
using iFileProxy.Models;
using System.Security.Cryptography; using System.Security.Cryptography;
using System.Text; using System.Text;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
@ -56,6 +57,13 @@ namespace iFileProxy.Helpers
return clientIp; return clientIp;
} }
public static bool CheckDownloadFileExists(string fileName)
{
if (File.Exists(Path.Combine(AppConfig.GetCurrConfig().DownloadOptions.SavePath,fileName)))
return true;
return false;
}
public static string GetFileHash(string fileName, FileHashAlgorithm algorithm) public static string GetFileHash(string fileName, FileHashAlgorithm algorithm)
{ {
byte[] hash = []; byte[] hash = [];

View file

@ -1,37 +1,57 @@
using System.Text.Json; using Newtonsoft.Json;
using System.Text.Json;
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
namespace iFileProxy.Models namespace iFileProxy.Models
{ {
public class TaskInfo public class TaskInfo
{ {
[JsonProperty("id")]
[JsonPropertyName("id")] [JsonPropertyName("id")]
public uint Id { get; set; } public uint Id { get; set; }
[JsonProperty("tid")]
[JsonPropertyName("tid")] [JsonPropertyName("tid")]
public string TaskId { get; set; } public string TaskId { get; set; }
[JsonProperty("file_name")]
[JsonPropertyName("file_name")] [JsonPropertyName("file_name")]
public string FileName { get; set; } public string FileName { get; set; }
[JsonProperty("client_ip")]
[JsonPropertyName("client_ip")] [JsonPropertyName("client_ip")]
public string? ClientIp { get; set; } public string? ClientIp { get; set; }
[JsonProperty("add_time")]
[JsonPropertyName("add_time")] [JsonPropertyName("add_time")]
public DateTime AddTime { get; set; } public DateTime AddTime { get; set; }
[JsonProperty("update_time")]
[JsonPropertyName("update_time")] [JsonPropertyName("update_time")]
public DateTime UpdateTime { get; set; } public DateTime UpdateTime { get; set; }
[JsonProperty("status")]
[JsonPropertyName("status")] [JsonPropertyName("status")]
public TaskState Status { get; set; } public TaskState Status { get; set; }
[JsonProperty("url")]
[JsonPropertyName("url")] [JsonPropertyName("url")]
public string Url { get; set; } public string Url { get; set; }
[JsonProperty("size")]
[JsonPropertyName("size")] [JsonPropertyName("size")]
public long Size { get; set; } public long Size { get; set; }
[JsonProperty("hash")]
[JsonPropertyName("hash")] [JsonPropertyName("hash")]
public string Hash { get; set; } public string Hash { get; set; }
[JsonProperty("tag")]
[JsonPropertyName("tag")]
public string Tag { get; set; }
}
public class DbConfigName {
public static string iFileProxy = "iFileProxy_Db";
} }
} }

View file

@ -6,6 +6,7 @@
Error = 2, // 任务执行时候发生错误 已经结束 Error = 2, // 任务执行时候发生错误 已经结束
End = 3, // 任务正常结束 End = 3, // 任务正常结束
Cached = 4, // 要下载的内容已经缓存 Cached = 4, // 要下载的内容已经缓存
Cleaned =5, // 内容过期已被清理
} }
public enum TaskAddState { public enum TaskAddState {
Success = 0, Success = 0,

View file

@ -1,6 +1,7 @@
using iFileProxy.Config; using iFileProxy.Config;
using iFileProxy.Middleware; using iFileProxy.Middleware;
using iFileProxy.Services;
using Serilog; using Serilog;
namespace iFileProxy namespace iFileProxy
@ -12,10 +13,13 @@ namespace iFileProxy
SerilogConfig.CreateLogger(); SerilogConfig.CreateLogger();
Serilog.ILogger logger = Log.Logger.ForContext<Program>(); Serilog.ILogger logger = Log.Logger.ForContext<Program>();
Console.Write(" "); Console.Write(" "); // 强迫症,看着开始的那条日志不对齐不得劲
AppConfig.CheckAppConfig(); AppConfig.CheckAppConfig();
// 初始化缓存管理服务
LocalCacheManager localCacheManager = new ();
var builder = WebApplication.CreateBuilder(args); var builder = WebApplication.CreateBuilder(args);
// Add services to the container. // Add services to the container.

View file

@ -15,7 +15,7 @@ https://go.microsoft.com/fwlink/?LinkID=208121.
<_TargetId>Folder</_TargetId> <_TargetId>Folder</_TargetId>
<SiteUrlToLaunchAfterPublish /> <SiteUrlToLaunchAfterPublish />
<TargetFramework>net8.0</TargetFramework> <TargetFramework>net8.0</TargetFramework>
<RuntimeIdentifier>win-x64</RuntimeIdentifier> <RuntimeIdentifier>linux-x64</RuntimeIdentifier>
<ProjectGuid>e343bd8a-27ed-47e2-b50d-e3000730e65e</ProjectGuid> <ProjectGuid>e343bd8a-27ed-47e2-b50d-e3000730e65e</ProjectGuid>
<SelfContained>false</SelfContained> <SelfContained>false</SelfContained>
<PublishSingleFile>true</PublishSingleFile> <PublishSingleFile>true</PublishSingleFile>

View file

@ -1,9 +1,74 @@
namespace iFileProxy.Services using iFileProxy.Config;
using iFileProxy.Helpers;
using iFileProxy.Models;
using Newtonsoft.Json;
using Serilog;
namespace iFileProxy.Services
{ {
/// <summary> /// <summary>
/// 本地缓存管理器 /// 本地缓存管理器
/// </summary> /// </summary>
public class LocalCacheManager public class LocalCacheManager
{ {
// 禁用空警告 因为初始化的时候就已经检查过相关的内容了
#pragma warning disable CS8601
private readonly AppConfig _appConfig = AppConfig.GetCurrConfig();
private readonly static Serilog.ILogger _logger = Log.Logger.ForContext<LocalCacheManager>();
private readonly Timer _timer;
private readonly DatabaseHelper _dbHelper;
private readonly int CACHE_LIFETIME;
public LocalCacheManager()
{
_logger.Information("Initializing LocalCacheManager.");
CACHE_LIFETIME = _appConfig.DownloadOptions.CacheLifetime;
_dbHelper = new DatabaseHelper(_appConfig);
_timer = new Timer(CheckAndCleanCache, null, TimeSpan.FromSeconds(6), TimeSpan.FromSeconds(60));
_logger.Information("succ.");
}
/// <summary>
/// 检查并且清理缓存数据
/// </summary>
public void CheckAndCleanCache(object state)
{
// 获取数据库中超出生命周期的缓存数据
string result = _dbHelper.QueryTableData($"SELECT * FROM t_tasks_info WHERE UNIX_TIMESTAMP(NOW()) - UNIX_TIMESTAMP(update_time) > {CACHE_LIFETIME} AND (tag <> 'CLEANED' OR tag IS NULL)", DbConfigName.iFileProxy);
List<TaskInfo>? taskInfos = JsonConvert.DeserializeObject<List<TaskInfo>>(result);
if (taskInfos != null)
{
foreach (TaskInfo taskInfo in taskInfos)
{
string cacheFileName = Path.Combine(_appConfig.DownloadOptions.SavePath, taskInfo.FileName);
if (File.Exists(cacheFileName))
{
_logger.Information($"正在清理缓存文件: {cacheFileName}");
try
{
File.Delete(cacheFileName);
}
catch (Exception e)
{
_logger.Error("缓存文件删除失败: {e}", e);
throw;
}
}
_dbHelper.Query($"UPDATE t_tasks_info SET `tag` = \"CLEANED\" WHERE `tid` = '{taskInfo.TaskId}'", DbConfigName.iFileProxy);
taskInfo.Status = TaskState.Cleaned;
_dbHelper.UpdateTaskStatus(taskInfo);
}
}
}
/// <summary>
/// 停止定时清理服务
/// </summary>
public void StopScheduledCleanupService()
{
_timer.Dispose();
}
} }
} }

View file

@ -72,12 +72,28 @@ namespace iFileProxy.Services
Status = TaskState.NoInit, Status = TaskState.NoInit,
UpdateTime = DateTime.Now UpdateTime = DateTime.Now
}; };
if (MasterHelper.CheckDownloadFileExists(taskInfo.FileName))
{
var r = _dbHelper.QueryTaskInfo(taskInfo.FileName, taskInfo.Url, taskInfo.Size, TaskState.End);
if (r != null)
{
taskInfo.Status = TaskState.Cached;
taskInfo.Hash = r.Hash;
taskInfo.Size = r.Size;
taskInfo.Tag = $"REDIRECT:{r.TaskId}";
}
}
if (_dbHelper.InsertTaskData(taskInfo)) if (_dbHelper.InsertTaskData(taskInfo))
{
if (taskInfo.Status != TaskState.Cached)
{ {
StartTask(taskInfo); StartTask(taskInfo);
taskInfo.Status = TaskState.Running; taskInfo.Status = TaskState.Running;
_dbHelper.UpdateTaskStatus(taskInfo); _dbHelper.UpdateTaskStatus(taskInfo);
_logger.Debug("数据插入成功"); }
_logger.Debug("任务添加成功.");
return TaskAddState.Success; return TaskAddState.Success;
} }
else else

View file

@ -122,7 +122,9 @@
[0, "排队中"], [0, "排队中"],
[1, "进行中"], [1, "进行中"],
[2, "错误"], [2, "错误"],
[3, "已完成"] [3, "已完成"],
[4, "已缓存"],
[5, "已被清理"]
]) ])
data = []; data = [];
$.ajax({ $.ajax({
@ -159,7 +161,7 @@
data.forEach(item => { data.forEach(item => {
const row = document.createElement('tr'); const row = document.createElement('tr');
row.innerHTML = ` row.innerHTML = `
<td>${item.status == 3 ? `<a href="/Download/${item.tid}">${item.file_name}</a>` : item.file_name}</td> <td>${item.status == 3 || item.status == 4 ? `<a href="/Download/${item.tid}">${item.file_name}</a>` : item.file_name}</td>
<td class="hidden-on-small">${formatBytes(item.size)}</td> <td class="hidden-on-small">${formatBytes(item.size)}</td>
<td>${item.add_time}</td> <td>${item.add_time}</td>
<td>${statusStrDict.get(item.status)}</td> <td>${statusStrDict.get(item.status)}</td>