|
|
|
@@ -1,115 +1,147 @@
|
|
|
|
|
#include "TempFileManager.h"
|
|
|
|
|
#include <thread>
|
|
|
|
|
#include <filesystem>
|
|
|
|
|
#include <shared_mutex>
|
|
|
|
|
#include <system_error>
|
|
|
|
|
#include <unordered_map>
|
|
|
|
|
#include "ServerLogger.h"
|
|
|
|
|
|
|
|
|
|
TempFileManager::TempFileManager() noexcept
|
|
|
|
|
class TempFileManager::Impl
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ================= 更新:核心析构函数实现 =================
|
|
|
|
|
TempFileManager::~TempFileManager() noexcept
|
|
|
|
|
{
|
|
|
|
|
// 1. 必须第一步:发送停止信号并阻塞等待后台清理线程彻底退出
|
|
|
|
|
// 这样能确保后面遍历 file_map 时,绝对没有第二个线程在并发访问它
|
|
|
|
|
SCLOG_INFO("TempFileManager Destruction Begin");
|
|
|
|
|
cleanup_thread.request_stop();
|
|
|
|
|
if (cleanup_thread.joinable())
|
|
|
|
|
cleanup_thread.join();
|
|
|
|
|
// 2. 此时属于单线程环境,无需加锁。遍历并销毁所有非永久文件
|
|
|
|
|
std::error_code ec;
|
|
|
|
|
for (const auto& [_, fcb] : file_map)
|
|
|
|
|
public:
|
|
|
|
|
struct FileControlBlock
|
|
|
|
|
{
|
|
|
|
|
WebFileInfo info;
|
|
|
|
|
std::chrono::steady_clock::time_point expire_time;
|
|
|
|
|
std::chrono::steady_clock::time_point active_start_time;
|
|
|
|
|
std::chrono::seconds remaining_timeout;
|
|
|
|
|
std::chrono::seconds max_processing_timeout;
|
|
|
|
|
bool is_active = false;
|
|
|
|
|
bool is_permanent = false; // 是否永久保留
|
|
|
|
|
bool is_invalid = false;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
std::string base_dir;
|
|
|
|
|
std::unordered_map<std::string, FileControlBlock> file_map;
|
|
|
|
|
mutable std::shared_mutex rw_mutex;
|
|
|
|
|
|
|
|
|
|
// C++20 jthread
|
|
|
|
|
std::jthread cleanup_thread;
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
Impl() noexcept
|
|
|
|
|
{
|
|
|
|
|
if (!fcb.is_permanent && !base_dir.empty())
|
|
|
|
|
{
|
|
|
|
|
std::string path = fcb.info.MakePath(base_dir);
|
|
|
|
|
// 使用无异常重载版本,即使磁盘物理删除失败(如文件被外层强行独占锁死)也绝不抛出异常
|
|
|
|
|
std::filesystem::remove(path, ec);
|
|
|
|
|
if (!ec)
|
|
|
|
|
SCLOGF_INFO("Temp File {} Deleted.", path);
|
|
|
|
|
else
|
|
|
|
|
SCLOGF_INFO("Temp File {} Delete Failed: {}({})", path, ec.value(), ec.message());
|
|
|
|
|
}
|
|
|
|
|
// 如果 is_permanent 为 true(永久化文件),则跳过不处理,物理文件将安全留在磁盘上
|
|
|
|
|
}
|
|
|
|
|
// 3. 析构结束,file_map 内存控制块会自动退栈销毁
|
|
|
|
|
SCLOG_INFO("TempFileManager Destruction Finished");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ================= 更新:高灵敏度的后台扫描逻辑 =================
|
|
|
|
|
void TempFileManager::CleanupLoop(std::stop_token st) noexcept
|
|
|
|
|
{
|
|
|
|
|
while (!st.stop_requested())
|
|
|
|
|
~Impl() noexcept
|
|
|
|
|
{
|
|
|
|
|
// 改进:引入分段休眠(10次*100ms),让析构函数调用 join() 时能在最大 100ms 内瞬间响应退出
|
|
|
|
|
// 避免传统的 sleep_for(1s) 导致服务器内核关闭时卡顿 1 秒
|
|
|
|
|
for (int i = 0; i < 10; i++)
|
|
|
|
|
// 1. 必须第一步:发送停止信号并阻塞等待后台清理线程彻底退出
|
|
|
|
|
// 这样能确保后面遍历 file_map 时,绝对没有第二个线程在并发访问它
|
|
|
|
|
SCLOG_INFO("TempFileManager Destruction Begin");
|
|
|
|
|
cleanup_thread.request_stop();
|
|
|
|
|
if (cleanup_thread.joinable())
|
|
|
|
|
cleanup_thread.join();
|
|
|
|
|
// 2. 此时属于单线程环境,无需加锁。遍历并销毁所有非永久文件
|
|
|
|
|
std::error_code ec;
|
|
|
|
|
for (const auto& [_, fcb] : file_map)
|
|
|
|
|
{
|
|
|
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
|
|
|
|
if (st.stop_requested())
|
|
|
|
|
return; // 随时收到终止信号随时退出
|
|
|
|
|
}
|
|
|
|
|
std::unique_lock lock(rw_mutex);
|
|
|
|
|
if (base_dir.empty())
|
|
|
|
|
continue;
|
|
|
|
|
auto now = std::chrono::steady_clock::now();
|
|
|
|
|
for (auto it = file_map.begin(); it != file_map.end(); )
|
|
|
|
|
{
|
|
|
|
|
bool should_delete = false;
|
|
|
|
|
if (it->second.is_invalid)
|
|
|
|
|
should_delete = true; //手动设置的无条件立即删除
|
|
|
|
|
if (!it->second.is_permanent)
|
|
|
|
|
if (!fcb.is_permanent && !base_dir.empty())
|
|
|
|
|
{
|
|
|
|
|
if (it->second.is_active)
|
|
|
|
|
{
|
|
|
|
|
if ((it->second.max_processing_timeout > std::chrono::seconds(0)) && ((now - it->second.active_start_time) > it->second.max_processing_timeout))
|
|
|
|
|
should_delete = true;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if (now > it->second.expire_time)
|
|
|
|
|
should_delete = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (should_delete)
|
|
|
|
|
{
|
|
|
|
|
std::error_code ec;
|
|
|
|
|
std::string path = it->second.info.MakePath(base_dir);
|
|
|
|
|
std::string path = fcb.info.MakePath(base_dir);
|
|
|
|
|
// 使用无异常重载版本,即使磁盘物理删除失败(如文件被外层强行独占锁死)也绝不抛出异常
|
|
|
|
|
std::filesystem::remove(path, ec);
|
|
|
|
|
if (!ec)
|
|
|
|
|
SCLOGF_INFO("Temp File {} {}, Deleted.", path, (it->second.is_invalid ? "Invalid" : "Expried"));
|
|
|
|
|
SCLOGF_INFO("Temp File {} Deleted.", path);
|
|
|
|
|
else
|
|
|
|
|
SCLOGF_INFO("Temp File {} {}, Delete Failed: {}({})", path, (it->second.is_invalid ? "Invalid" : "Expried"), ec.value(), ec.message());
|
|
|
|
|
it = file_map.erase(it);
|
|
|
|
|
SCLOGF_WARNING("Temp File {} Delete Failed: {}({})", path, ec.value(), ec.message());
|
|
|
|
|
}
|
|
|
|
|
// 如果 is_permanent 为 true(永久化文件),则跳过不处理,物理文件将安全留在磁盘上
|
|
|
|
|
}
|
|
|
|
|
// 3. 析构结束,file_map 内存控制块会自动退栈销毁
|
|
|
|
|
SCLOG_INFO("TempFileManager Destruction Finished");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
void CleanupLoop(std::stop_token st) noexcept
|
|
|
|
|
{
|
|
|
|
|
while (!st.stop_requested())
|
|
|
|
|
{
|
|
|
|
|
// 改进:引入分段休眠(10次*100ms),让析构函数调用 join() 时能在最大 100ms 内瞬间响应退出
|
|
|
|
|
// 避免传统的 sleep_for(1s) 导致服务器内核关闭时卡顿 1 秒
|
|
|
|
|
for (int i = 0; i < 10; i++)
|
|
|
|
|
{
|
|
|
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
|
|
|
|
if (st.stop_requested())
|
|
|
|
|
return; // 随时收到终止信号随时退出
|
|
|
|
|
}
|
|
|
|
|
std::unique_lock lock(rw_mutex);
|
|
|
|
|
if (base_dir.empty())
|
|
|
|
|
continue;
|
|
|
|
|
auto now = std::chrono::steady_clock::now();
|
|
|
|
|
for (auto it = file_map.begin(); it != file_map.end(); )
|
|
|
|
|
{
|
|
|
|
|
bool should_delete = false;
|
|
|
|
|
if (it->second.is_invalid)
|
|
|
|
|
should_delete = true; //手动设置的无条件立即删除
|
|
|
|
|
if (!it->second.is_permanent)
|
|
|
|
|
{
|
|
|
|
|
if (it->second.is_active)
|
|
|
|
|
{
|
|
|
|
|
if ((it->second.max_processing_timeout > std::chrono::seconds(0)) && ((now - it->second.active_start_time) > it->second.max_processing_timeout))
|
|
|
|
|
should_delete = true;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if (now > it->second.expire_time)
|
|
|
|
|
should_delete = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (should_delete)
|
|
|
|
|
{
|
|
|
|
|
std::error_code ec;
|
|
|
|
|
std::string path = it->second.info.MakePath(base_dir);
|
|
|
|
|
std::filesystem::remove(path, ec);
|
|
|
|
|
if (!ec)
|
|
|
|
|
SCLOGF_INFO("Temp File {} {}, Deleted.", path, (it->second.is_invalid ? "Invalid" : "Expried"));
|
|
|
|
|
else
|
|
|
|
|
SCLOGF_WARNING("Temp File {} {}, Delete Failed: {}({})", path, (it->second.is_invalid ? "Invalid" : "Expried"), ec.value(), ec.message());
|
|
|
|
|
it = file_map.erase(it);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
++it;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
++it;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
TempFileManager::TempFileManager() noexcept : pimpl(std::make_unique<Impl>())
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TempFileManager::~TempFileManager() noexcept = default;
|
|
|
|
|
|
|
|
|
|
// ================= 以下其余基础业务接口保持不变 =================
|
|
|
|
|
|
|
|
|
|
bool TempFileManager::SetBaseDirectory(std::string dir) noexcept
|
|
|
|
|
{
|
|
|
|
|
std::unique_lock lock(rw_mutex); // 加上写锁,防止与其他文件操作并发
|
|
|
|
|
base_dir = std::move(dir);
|
|
|
|
|
std::unique_lock lock(pimpl->rw_mutex); // 加上写锁,防止与其他文件操作并发
|
|
|
|
|
pimpl->base_dir = std::move(dir);
|
|
|
|
|
std::error_code ec;
|
|
|
|
|
// 物理创建目录(无异常版本)
|
|
|
|
|
std::filesystem::create_directories(base_dir, ec);
|
|
|
|
|
std::filesystem::create_directories(pimpl->base_dir, ec);
|
|
|
|
|
if (ec)
|
|
|
|
|
SCLOGF_INFO("TempFileManager: create_directories error: {}({})", ec.value(), ec.message());
|
|
|
|
|
SCLOGF_ERROR("TempFileManager: create_directories error: {}({})", ec.value(), ec.message());
|
|
|
|
|
// 只有在线程未启动时才启动后台清理线程,确保整个生命周期只启动一次
|
|
|
|
|
if (!cleanup_thread.joinable() && !ec)
|
|
|
|
|
if (!pimpl->cleanup_thread.joinable() && !ec)
|
|
|
|
|
{
|
|
|
|
|
cleanup_thread = std::jthread([this] (std::stop_token st)
|
|
|
|
|
pimpl->cleanup_thread = std::jthread([this] (std::stop_token st)
|
|
|
|
|
{
|
|
|
|
|
this->CleanupLoop(st);
|
|
|
|
|
this->pimpl->CleanupLoop(st);
|
|
|
|
|
});
|
|
|
|
|
SCLOGF_INFO("TempFileManager: Thread Started.");
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
SCLOGF_ERROR("TempFileManager: Failed To Start Thread (Thread Joinable: {}, Error: {})", cleanup_thread.joinable(), ec.message());
|
|
|
|
|
SCLOGF_ERROR("TempFileManager: Failed To Start Thread (Thread Joinable: {}, Error: {})", pimpl->cleanup_thread.joinable(), ec.message());
|
|
|
|
|
return !ec;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@@ -117,36 +149,36 @@ bool TempFileManager::RegisterFile(const WebFileInfo& info, std::chrono::seconds
|
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
std::unique_lock lock(rw_mutex);
|
|
|
|
|
if (file_map.contains(info.GetStorageFileName()))
|
|
|
|
|
std::unique_lock lock(pimpl->rw_mutex);
|
|
|
|
|
if (pimpl->file_map.contains(info.GetStorageFileName()))
|
|
|
|
|
{
|
|
|
|
|
SCLOGF_INFO("RegisterFile Failed: File {} Already Exists", info.GetStorageFileName());
|
|
|
|
|
SCLOGF_WARNING("RegisterFile Failed: File {} Already Exists", info.GetStorageFileName());
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
FileControlBlock fcb
|
|
|
|
|
Impl::FileControlBlock fcb
|
|
|
|
|
{
|
|
|
|
|
.info = info,
|
|
|
|
|
.expire_time = std::chrono::steady_clock::now() + timeout,
|
|
|
|
|
.remaining_timeout = timeout,
|
|
|
|
|
.max_processing_timeout = max_proc_timeout
|
|
|
|
|
};
|
|
|
|
|
file_map[info.GetStorageFileName()] = std::move(fcb);
|
|
|
|
|
pimpl->file_map[info.GetStorageFileName()] = std::move(fcb);
|
|
|
|
|
SCLOGF_INFO("File {}({}) Registered", info.GetOriginalFileName(), info.GetStorageFileName());
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
catch (...)
|
|
|
|
|
{
|
|
|
|
|
SCLOG_INFO("RegisterFile Failed: Exception");
|
|
|
|
|
SCLOG_WARNING("RegisterFile Failed: Exception");
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool TempFileManager::InvalidateFile(const WebFileInfo& info)
|
|
|
|
|
{
|
|
|
|
|
std::unique_lock lock(rw_mutex);
|
|
|
|
|
if (file_map.contains(info.GetStorageFileName()))
|
|
|
|
|
std::unique_lock lock(pimpl->rw_mutex);
|
|
|
|
|
if (pimpl->file_map.contains(info.GetStorageFileName()))
|
|
|
|
|
{
|
|
|
|
|
file_map[info.GetStorageFileName()].is_invalid = true;
|
|
|
|
|
pimpl->file_map[info.GetStorageFileName()].is_invalid = true;
|
|
|
|
|
SCLOGF_INFO("File [{}]({}) Marked AS Invalid", info.GetStorageFileName(), info.GetOriginalFileName());
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
@@ -158,12 +190,12 @@ bool TempFileManager::InvalidateFile(const WebFileInfo& info)
|
|
|
|
|
bool TempFileManager::InvalidateFile(const std::map<std::string, std::string>& info)
|
|
|
|
|
{
|
|
|
|
|
size_t succ_cnt = 0;
|
|
|
|
|
std::unique_lock lock(rw_mutex);
|
|
|
|
|
std::unique_lock lock(pimpl->rw_mutex);
|
|
|
|
|
for (const auto& [on, sn] : info)
|
|
|
|
|
{
|
|
|
|
|
if (file_map.contains(sn))
|
|
|
|
|
if (pimpl->file_map.contains(sn))
|
|
|
|
|
{
|
|
|
|
|
file_map[sn].is_invalid = true;
|
|
|
|
|
pimpl->file_map[sn].is_invalid = true;
|
|
|
|
|
succ_cnt++;
|
|
|
|
|
SCLOGF_INFO("File [{}]({}) Marked AS Invalid", sn, on);
|
|
|
|
|
}
|
|
|
|
@@ -175,16 +207,16 @@ bool TempFileManager::InvalidateFile(const std::map<std::string, std::string>& i
|
|
|
|
|
|
|
|
|
|
bool TempFileManager::CopyFileTo(const std::string& storage_name, const std::string& dest_absolute_path) noexcept
|
|
|
|
|
{
|
|
|
|
|
std::shared_lock lock(rw_mutex);
|
|
|
|
|
auto it = file_map.find(storage_name);
|
|
|
|
|
std::shared_lock lock(pimpl->rw_mutex);
|
|
|
|
|
auto it = pimpl->file_map.find(storage_name);
|
|
|
|
|
auto dest_file = std::filesystem::path(dest_absolute_path) / storage_name;
|
|
|
|
|
if (it == file_map.end())
|
|
|
|
|
if (it == pimpl->file_map.end())
|
|
|
|
|
{
|
|
|
|
|
SCLOGF_INFO("CopyFileTo({}, {}) Failed: File Not Exists", storage_name, dest_file);
|
|
|
|
|
SCLOGF_WARNING("CopyFileTo({}, {}) Failed: File Not Exists", storage_name, dest_file);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
std::error_code ec;
|
|
|
|
|
std::filesystem::copy(it->second.info.MakePath(base_dir), dest_file, std::filesystem::copy_options::overwrite_existing, ec);
|
|
|
|
|
std::filesystem::copy(it->second.info.MakePath(pimpl->base_dir), dest_file, std::filesystem::copy_options::overwrite_existing, ec);
|
|
|
|
|
if (ec)
|
|
|
|
|
SCLOGF_WARNING("CopyFileTo({}, {}) Failed: {}({})", storage_name, dest_file, ec.value(), ec.message());
|
|
|
|
|
else
|
|
|
|
@@ -194,23 +226,23 @@ bool TempFileManager::CopyFileTo(const std::string& storage_name, const std::str
|
|
|
|
|
|
|
|
|
|
bool TempFileManager::CutFileTo(const std::string& storage_name, const std::string& dest_absolute_path) noexcept
|
|
|
|
|
{
|
|
|
|
|
std::unique_lock lock(rw_mutex);
|
|
|
|
|
auto it = file_map.find(storage_name);
|
|
|
|
|
std::unique_lock lock(pimpl->rw_mutex);
|
|
|
|
|
auto it = pimpl->file_map.find(storage_name);
|
|
|
|
|
auto dest_file = std::filesystem::path(dest_absolute_path) / storage_name;
|
|
|
|
|
if (it == file_map.end())
|
|
|
|
|
if (it == pimpl->file_map.end())
|
|
|
|
|
{
|
|
|
|
|
SCLOGF_INFO("CutFileTo({}, {}) Failed: File Not Exists", storage_name, dest_file);
|
|
|
|
|
SCLOGF_WARNING("CutFileTo({}, {}) Failed: File Not Exists", storage_name, dest_file);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
std::error_code ec;
|
|
|
|
|
std::filesystem::rename(it->second.info.MakePath(base_dir), dest_file, ec);
|
|
|
|
|
std::filesystem::rename(it->second.info.MakePath(pimpl->base_dir), dest_file, ec);
|
|
|
|
|
if (ec)
|
|
|
|
|
SCLOGF_WARNING("CutFileTo({}, {}) Failed: {}({})", storage_name, dest_file, ec.value(), ec.message());
|
|
|
|
|
else
|
|
|
|
|
SCLOGF_INFO("CutFileTo({}, {}) Success", storage_name, dest_file);
|
|
|
|
|
if (ec)
|
|
|
|
|
return false;
|
|
|
|
|
file_map.erase(it);
|
|
|
|
|
pimpl->file_map.erase(it);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@@ -218,18 +250,18 @@ bool TempFileManager::RenameFile(const std::string& old_storage_name, const std:
|
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
std::unique_lock lock(rw_mutex);
|
|
|
|
|
auto it = file_map.find(old_storage_name);
|
|
|
|
|
if ((it == file_map.end()) || file_map.contains(new_storage_name))
|
|
|
|
|
std::unique_lock lock(pimpl->rw_mutex);
|
|
|
|
|
auto it = pimpl->file_map.find(old_storage_name);
|
|
|
|
|
if ((it == pimpl->file_map.end()) || pimpl->file_map.contains(new_storage_name))
|
|
|
|
|
{
|
|
|
|
|
SCLOGF_INFO("RenameFile({}, {}) Failed: File Not Exists", old_storage_name, new_storage_name);
|
|
|
|
|
SCLOGF_WARNING("RenameFile({}, {}) Failed: File Not Exists", old_storage_name, new_storage_name);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
std::error_code ec;
|
|
|
|
|
std::string old_path = it->second.info.MakePath(base_dir);
|
|
|
|
|
FileControlBlock fcb = std::move(it->second);
|
|
|
|
|
std::string old_path = it->second.info.MakePath(pimpl->base_dir);
|
|
|
|
|
Impl::FileControlBlock fcb = std::move(it->second);
|
|
|
|
|
fcb.info.SetStorageFileName(new_storage_name);
|
|
|
|
|
std::string new_path = fcb.info.MakePath(base_dir);
|
|
|
|
|
std::string new_path = fcb.info.MakePath(pimpl->base_dir);
|
|
|
|
|
std::filesystem::rename(old_path, new_path, ec);
|
|
|
|
|
if (ec)
|
|
|
|
|
SCLOGF_WARNING("RenameFile({}, {}) Failed: {}({})", old_storage_name, new_storage_name, ec.value(), ec.message());
|
|
|
|
@@ -237,43 +269,43 @@ bool TempFileManager::RenameFile(const std::string& old_storage_name, const std:
|
|
|
|
|
SCLOGF_INFO("RenameFile({}, {}) Success", old_storage_name, new_storage_name);
|
|
|
|
|
if (ec)
|
|
|
|
|
return false;
|
|
|
|
|
file_map.erase(it);
|
|
|
|
|
file_map[new_storage_name] = std::move(fcb);
|
|
|
|
|
pimpl->file_map.erase(it);
|
|
|
|
|
pimpl->file_map[new_storage_name] = std::move(fcb);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
catch (...)
|
|
|
|
|
{
|
|
|
|
|
SCLOG_INFO("RenameFile Failed: Exception");
|
|
|
|
|
SCLOG_WARNING("RenameFile Failed: Exception");
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool TempFileManager::DeleteFile(const std::string& storage_name) noexcept
|
|
|
|
|
{
|
|
|
|
|
std::unique_lock lock(rw_mutex);
|
|
|
|
|
auto it = file_map.find(storage_name);
|
|
|
|
|
if (it == file_map.end())
|
|
|
|
|
std::unique_lock lock(pimpl->rw_mutex);
|
|
|
|
|
auto it = pimpl->file_map.find(storage_name);
|
|
|
|
|
if (it == pimpl->file_map.end())
|
|
|
|
|
{
|
|
|
|
|
SCLOGF_WARNING("DeleteFile({}) Failed: File Not Exists", storage_name);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
std::error_code ec;
|
|
|
|
|
std::filesystem::remove(it->second.info.MakePath(base_dir), ec);
|
|
|
|
|
std::filesystem::remove(it->second.info.MakePath(pimpl->base_dir), ec);
|
|
|
|
|
if (ec)
|
|
|
|
|
SCLOGF_WARNING("DeleteFile({}) Failed: {}({})", storage_name, ec.value(), ec.message());
|
|
|
|
|
else
|
|
|
|
|
SCLOGF_INFO("DeleteFile({}) Success", storage_name);
|
|
|
|
|
if (ec)
|
|
|
|
|
return false;
|
|
|
|
|
file_map.erase(it);
|
|
|
|
|
pimpl->file_map.erase(it);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool TempFileManager::ActiveFile(const std::string& storage_name) noexcept
|
|
|
|
|
{
|
|
|
|
|
std::unique_lock lock(rw_mutex);
|
|
|
|
|
auto it = file_map.find(storage_name);
|
|
|
|
|
if ((it == file_map.end()) || it->second.is_active)
|
|
|
|
|
std::unique_lock lock(pimpl->rw_mutex);
|
|
|
|
|
auto it = pimpl->file_map.find(storage_name);
|
|
|
|
|
if ((it == pimpl->file_map.end()) || it->second.is_active)
|
|
|
|
|
{
|
|
|
|
|
SCLOGF_WARNING("ActiveFile({}) Failed: {}", storage_name, (it->second.is_active ? "Already Actived" : "File Not Exists"));
|
|
|
|
|
return false;
|
|
|
|
@@ -291,11 +323,11 @@ bool TempFileManager::ActiveFile(const std::string& storage_name) noexcept
|
|
|
|
|
|
|
|
|
|
bool TempFileManager::DeactiveFile(const std::string& storage_name) noexcept
|
|
|
|
|
{
|
|
|
|
|
std::unique_lock lock(rw_mutex);
|
|
|
|
|
auto it = file_map.find(storage_name);
|
|
|
|
|
if ((it == file_map.end()) || !it->second.is_active)
|
|
|
|
|
std::unique_lock lock(pimpl->rw_mutex);
|
|
|
|
|
auto it = pimpl->file_map.find(storage_name);
|
|
|
|
|
if ((it == pimpl->file_map.end()) || !it->second.is_active)
|
|
|
|
|
{
|
|
|
|
|
SCLOGF_WARNING("DeactiveFile({}) Failed: {}", storage_name, ((it != file_map.end()) ? "Already Deactived" : "File Not Exists"));
|
|
|
|
|
SCLOGF_WARNING("DeactiveFile({}) Failed: {}", storage_name, ((it != pimpl->file_map.end()) ? "Already Deactived" : "File Not Exists"));
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
it->second.is_active = false;
|
|
|
|
@@ -306,9 +338,9 @@ bool TempFileManager::DeactiveFile(const std::string& storage_name) noexcept
|
|
|
|
|
|
|
|
|
|
bool TempFileManager::SetPermanent(const std::string& storage_name, bool permanent) noexcept
|
|
|
|
|
{
|
|
|
|
|
std::unique_lock lock(rw_mutex);
|
|
|
|
|
auto it = file_map.find(storage_name);
|
|
|
|
|
if (it == file_map.end())
|
|
|
|
|
std::unique_lock lock(pimpl->rw_mutex);
|
|
|
|
|
auto it = pimpl->file_map.find(storage_name);
|
|
|
|
|
if (it == pimpl->file_map.end())
|
|
|
|
|
{
|
|
|
|
|
SCLOGF_WARNING("SetPermanent({}) Failed: File Not Exists", storage_name);
|
|
|
|
|
return false;
|
|
|
|
@@ -320,31 +352,31 @@ bool TempFileManager::SetPermanent(const std::string& storage_name, bool permane
|
|
|
|
|
|
|
|
|
|
bool TempFileManager::FileExists(const std::string& storage_name) const noexcept
|
|
|
|
|
{
|
|
|
|
|
std::shared_lock lock(rw_mutex);
|
|
|
|
|
return file_map.contains(storage_name);
|
|
|
|
|
std::shared_lock lock(pimpl->rw_mutex);
|
|
|
|
|
return pimpl->file_map.contains(storage_name);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool TempFileManager::GetFileInfo(const std::string& storage_name, WebFileInfo& out_info) const noexcept
|
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
std::shared_lock lock(rw_mutex);
|
|
|
|
|
auto it = file_map.find(storage_name);
|
|
|
|
|
if (it == file_map.end())
|
|
|
|
|
std::shared_lock lock(pimpl->rw_mutex);
|
|
|
|
|
auto it = pimpl->file_map.find(storage_name);
|
|
|
|
|
if (it == pimpl->file_map.end())
|
|
|
|
|
return false;
|
|
|
|
|
out_info = it->second.info;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
catch (...)
|
|
|
|
|
{
|
|
|
|
|
SCLOGF_INFO("GetFileInfo({}) Failed: Exception", storage_name);
|
|
|
|
|
SCLOGF_WARNING("GetFileInfo({}) Failed: Exception", storage_name);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
size_t TempFileManager::GetFileCount() const noexcept
|
|
|
|
|
{
|
|
|
|
|
return file_map.size();
|
|
|
|
|
return pimpl->file_map.size();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::vector<WebFileInfo> TempFileManager::GetAllFileInfos() const noexcept
|
|
|
|
@@ -352,9 +384,9 @@ std::vector<WebFileInfo> TempFileManager::GetAllFileInfos() const noexcept
|
|
|
|
|
std::vector<WebFileInfo> list;
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
std::shared_lock lock(rw_mutex); // 申请读锁,支持高并发并发读取
|
|
|
|
|
list.reserve(file_map.size()); // 提前预留空间,减少内存重分配次数
|
|
|
|
|
for (const auto& [_, fcb] : file_map)
|
|
|
|
|
std::shared_lock lock(pimpl->rw_mutex); // 申请读锁,支持高并发并发读取
|
|
|
|
|
list.reserve(pimpl->file_map.size()); // 提前预留空间,减少内存重分配次数
|
|
|
|
|
for (const auto& [_, fcb] : pimpl->file_map)
|
|
|
|
|
list.push_back(fcb.info); // 拷贝文件元数据到外部
|
|
|
|
|
}
|
|
|
|
|
catch (...)
|
|
|
|
|