C++云存储项目

📅 2026/6/26 5:05:54
C++云存储项目
来源程序员老廖1. 项目概述1.1 功能特性用户系统支持用户注册、登录、登出文件管理文件上传、下载、删除、列表展示媒体预览视频在线播放、图片预览文件分享支持多种分享模式公开/提取码/指定用户大文件优化分块上传、断点续传流式传输视频首帧快速播放优化1.2 项目特色相比普通 Web 服务器项目本项目的亮点大文件上传处理基于状态机的分块解析支持 GB 级文件断点续传下载完整支持 HTTP Range 请求视频流式优化智能缓冲控制首帧秒开企业级架构基于 muduo 的 Reactor 高性能网络框架2. 系统架构2.1 总体架构图┌──────────────────────────────────────────────────────────────────────────────────────┐ │ HTTP Client │ │ (Browser/移动端) │ └──────────────────────────────────────────────────────────────────────────────────────┘ │ ▼ ┌──────────────────────────────────────────────────────────────────────────────────────┐ │ HTTP Server Layer │ │ ┌────────────────┐ ┌───────────────┐ ┌────────────────┐ ┌───────────────┐ │ │ │ 静态文件 │ │ 文件上传 │ │ 文件下载 │ │ 用户认证 │ │ │ │ 服务器 │ │ 处理器 │ │ 处理器 │ │ 系统 │ │ │ └────────────────┘ └───────────────┘ └────────────────┘ └───────────────┘ │ │ │ │ │ ┌────────────────▼───────────────┐ │ │ │ HttpUploadHandler │ │ │ │ (业务逻辑核心) │ │ │ └────────────────┬───────────────┘ │ └────────────────────────────────────────────┼─────────────────────────────────────────┘ │ ┌────────────────────────────────────────────┼─────────────────────────────────────────┐ │ MyMuduo Network Framework │ │ ┌──────────────────────────────────────────▼─────────────────────────────────────┐ │ │ │ HttpServer │ │ │ │ (HTTP Protocol Parser Router) │ │ │ └──────────────────────────────────────────┬─────────────────────────────────────┘ │ │ │ │ │ ┌──────────────────────────────────────────▼─────────────────────────────────────┐ │ │ │ TcpServer │ │ │ │ (Connection Management Event Distribution) │ │ │ └──────────────────────────────────────────┬─────────────────────────────────────┘ │ │ │ │ │ ┌──────────────────────────────────────────▼─────────────────────────────────────┐ │ │ │ EventLoop │ │ │ │ (epoll-based Reactor Pattern) │ │ │ └────────────────────────────────────────────────────────────────────────────────┘ │ └──────────────────────────────────────────────────────────────────────────────────────┘ │ ▼ ┌──────────────────────────────────────────────────────────────────────────────────────┐ │ Data Layer │ │ ┌──────────────────────────────┐ ┌──────────────────────────────────────┐ │ │ │ MySQL 5.7 │ │ File System │ │ │ │ - users │ │ - uploads/ │ │ │ │ - sessions │ │ - filename_mapping.json │ │ │ │ - files │ └──────────────────────────────────────┘ │ │ │ - file_shares │ │ │ └──────────────────────────────┘ │ └──────────────────────────────────────────────────────────────────────────────────────┘2.2 模块依赖关系┌──────────────────────┐ │ application/ │ │ http_upload.cc │ └───────────┬──────────┘ │ 使用 ▼ ┌──────────────────────┐ │ net/http/ │ │ HttpServer │ └───────────┬──────────┘ │ 依赖 ▼ ┌─────────────────────────────┼──────────────────────────┐ ▼ ▼ ▼ ┌────────────────┐ ┌────────────────┐ ┌────────────────────┐ │ net/ │ │ base/ │ │ third_party │ │ TcpServer │ │ ThreadPool │ │ json.hpp │ │ Buffer │ │ Buffer │ │ │ │ EventLoop │ │ Logging │ │ │ └────────────────┘ └────────────────┘ └────────────────────┘2.3 网络模型架构Reactor 模式当前项目采用 单 Reactor 线程池 模型┌────────────────────────────────────────────────────────────────────────────────────┐ │ Main Thread (Reactor) │ │ ┌─────────────────────────────────────────────────────────────────────────────┐ │ │ │ EventLoop │ │ │ │ ┌───────────────┐ ┌────────────────┐ ┌─────────────────────┐ │ │ │ │ │ epoll │──▶ │ Channel │──▶ │ TcpConnection │ │ │ │ │ │ wait │ │ callback │ │ │ │ │ │ │ └───────────────┘ └────────────────┘ └───────────┬─────────┘ │ │ │ └────────────────────────────────────────────────────────────┼────────────────┘ │ └──────────────────────────────────────────────────────────────┼─────────────────────┘ │ IO Events (Read/Write/Accept) │ ▼ ┌─────────────────────────────────────────────────────────────────────────────────┐ │ Thread Pool (4 threads) │ │ ┌───────────────┐ ┌────────────────┐ ┌──────────────┐ ┌──────────────────┐ │ │ │ Worker 1 │ │ Worker 2 │ │ Worker 3 │ │ Worker 4 │ │ │ │ │ │ │ │ │ │ │ │ │ │ File Upload │ │ File Upload │ │ User Auth │ │ DB Query │ │ │ │ Processing │ │ Processing │ │ Processing │ │ Processing │ │ │ └───────────────┘ └────────────────┘ └──────────────┘ └──────────────────┘ │ │ │ │ Business Logic Processing │ └─────────────────────────────────────────────────────────────────────────────────┘项目代码领取C Linux学完后做什么项目推荐做C Linux云存储项目附带完整项目源码和文档3. 核心技术栈3.1 C11 特性应用本项目充分运用了 C11 的现代特性智能指针管理资源// TcpConnection 使用 shared_ptr 管理生命周期 using TcpConnectionPtr std::shared_ptrTcpConnection; // 连接上下文存储 std::shared_ptrHttpContext context std::make_sharedHttpContext(); conn-setContext(context); // 弱引用避免循环引用 std::weak_ptrTcpConnection weakConn conn;右值引用与移动语义// Buffer 类的移动构造函数优化数据传输 class Buffer { Buffer(Buffer other) noexcept; Buffer operator(Buffer other) noexcept; }; // 在 HttpRequest 中使用移动减少拷贝 void appendToBody(const char* data, size_t len) { body_.append(data, len); // 内部使用移动语义 }Lambda 表达式注册回调// 设置 HTTP 请求处理回调 server.setHttpCallback( [handler](const TcpConnectionPtr conn, HttpRequest req, HttpResponse* resp) { return handler-onRequest(conn, req, resp); }); // 设置写完成回调用于分块下载 conn-setWriteCompleteCallback([this, downContext](const TcpConnectionPtr connection) { std::string chunk; if (downContext-readNextChunk(chunk)) { connection-send(chunk); return true; // 继续写 } else { connection-shutdown(); // 完成关闭连接 return true; } });原子操作与线程安全// 活跃请求计数器线程安全 std::atomicint activeRequests_{0}; // 在多个线程中安全递增 activeRequests_.fetch_add(1, std::memory_order_relaxed);3.2 线程与线程池Thread 类封装基于 pthread 封装提供简洁的线程接口class Thread : noncopyable { public: explicit Thread(ThreadFunc func, const std::string name); void start(); int join(); private: std::shared_ptrpthread_t pthreadId_; ThreadFunc func_; };ThreadPool 线程池class ThreadPool : noncopyable { public: explicit ThreadPool(const std::string nameArg); void start(int numThreads); void run(Task task); // 提交任务到队列 private: std::vectorstd::unique_ptrThread threads_; BlockingQueueTask queue_; // 阻塞队列 };3.3 高性能组件Buffer 高性能缓冲区class Buffer { // 采用 vectorchar 作为底层存储 // 预留 prependable 空间用于高效头部插入 // 支持 scatter/gather IO std::vectorchar buffer_; size_t readerIndex_; size_t writerIndex_; public: void append(const char* data, size_t len); void retrieve(size_t len); std::string retrieveAllAsString(); };AsyncLogging 异步日志class AsyncLogging : noncopyable { // 双缓冲技术前端缓冲 后端缓冲 // 避免日志 IO 阻塞业务线程 void append(const char* logline, int len); private: BufferPtr currentBuffer_; // 当前写入缓冲 BufferPtr nextBuffer_; // 预备缓冲 BufferVector buffers_; // 待写入文件的缓冲队列 };4. 核心流程详解4.1 文件上传流程4.1.1 架构流程图┌──────────────┐ POST /upload ┌─────────────────┐ │ Client │ ──────────────────▶ │ HttpServer │ │ (Browser │ Content-Type: │ │ │ /App) │ multipart/ │ │ └──────────────┘ form-data └─────────┬───────┘ │ ▼ ┌──────────────────┐ │ HttpUpload │ │ Handler │ └─────────┬────────┘ │ ┌───────────────────────┼───────────────────┐ │ │ │ ▼ ▼ ▼ ┌────────────┐ ┌──────────┐ ┌────────────┐ │ Parse │ │ Create │ │ Write to │ │ Boundary │ ────▶ │ Upload │ ─────▶ │ Disk │ │ Headers │ │ Context │ │ (chunked) │ └────────────┘ └──────────┘ └────────────┘ │ ▼ ┌──────────────┐ │ MySQL Insert │ │ Save metadata│ └──────────────┘4.1.2 详细时序图4.1.3 状态机实现文件上传采用状态机处理多部分表单数据class FileUploadContext { public: enum class State { kExpectHeaders, // 等待解析 multipart headers kExpectContent, // 等待文件内容 kExpectBoundary, // 等待下一个 boundary kComplete // 上传完成 }; void writeData(const char* data, size_t len); State getState() const { return state_; } void setState(State state) { state_ state; } private: State state_; std::string boundary_; std::ofstream file_; uintmax_t totalBytes_; };状态转换逻辑┌──────────────────────────────────┐ │ kExpectHeaders │◀─────────────────┐ └─────────────────┬────────────────┘ │ │ 解析到 \r\n\r\n │ ▼ │ ┌──────────────────────────────────┐ 找到 boundary │ │ kExpectContent │──────────────────┘ └─────────────────┬────────────────┘ │ 找到结束 boundary (--) ▼ ┌──────────────────────────────────┐ │ kComplete │ └──────────────────────────────────┘4.1.4 关键代码解析分块数据处理http_upload.cc 第 540-620 行// 处理后续的数据块 std::string body req.body(); if (!body.empty()) { switch (uploadContext-getState()) { case FileUploadContext::State::kExpectBoundary: { // 检查是否是结束边界 std::string endBoundary uploadContext-getBoundary() --; size_t endPos body.find(endBoundary); if (endPos ! std::string::npos) { if (endPos 0) { uploadContext-writeData(body.data(), endPos); } uploadContext-setState(FileUploadContext::State::kComplete); break; } // 检查是否是普通边界 size_t boundaryPos body.find(uploadContext-getBoundary()); if (boundaryPos ! std::string::npos) { if (boundaryPos 0) { uploadContext-writeData(body.data(), boundaryPos); } uploadContext-setState(FileUploadContext::State::kExpectContent); } else { // 没有找到边界写入所有内容 uploadContext-writeData(body.data(), body.size()); } break; } // ... 其他状态处理 } }性能优化点直接写入磁盘避免内存累积支持 GB 级大文件边读边写无需等待完整请求体零拷贝优化使用 std::vectorchar 直接写入4.2 文件下载与断点续传4.2.1 流程图┌──────────────┐ GET /download/{filename} ┌─────────────────┐ │ Client │ ───────────────────────────▶ │ HttpServer │ │ (Browser │ Headers: │ │ │ /App) │ X-Session-ID: xxx │ │ └──────────────┘ Range: bytes0-1023 └────────┬────────┘ │ ▼ ┌───────────────┐ │ 权限检查 │ │ validate │ │ Session │ └───────┬───────┘ │ ┌─────────────────────┼───────────────────┐ │ │ │ 有权限 ▼ 无权限 ▼ 文件不存在 ▼ ┌────────────────┐ ┌──────────────┐ ┌────────────────┐ │ 创建下载 │ │ 403 │ │ 404 │ │ 上下文 │ │ Forbidden │ │ Not Found │ └───────┬────────┘ └──────────────┘ └────────────────┘ │ ▼ ┌─────────────────┐ │ 解析 Range │ │ 头部 │ └────────┬────────┘ │ ▼ ┌─────────────────┐ │ 设置响应头 │ │ 206/200 │ └────────┬────────┘ │ ▼ ┌─────────────┐ ┌─────────────────┐ │ 读取 1MB │ ──────▶ │ 发送数据 │ │ 数据块 │ │ │ └─────────────┘ └────────┬────────┘ │ 写完成 │ 回调 ▼ ┌─────────────────┐ │ 继续读/发送 │ │ 直到文件结束 │ └─────────────────┘4.2.2 时序图4.2.3 断点续传实现class FileDownContext { public: // 定位到指定位置 void seekTo(uintmax_t position) { file_.seekg(position); currentPosition_ position; isComplete_ false; } // 读取下一个数据块1MB bool readNextChunk(std::string chunk) { const uintmax_t chunkSize 1024 * 1024; // 1MB uintmax_t remainingBytes fileSize_ - currentPosition_; uintmax_t bytesToRead std::min(chunkSize, remainingBytes); if (bytesToRead 0) { isComplete_ true; return false; } std::vectorchar buffer(bytesToRead); file_.read(buffer.data(), bytesToRead); chunk.assign(buffer.data(), bytesToRead); currentPosition_ bytesToRead; return true; } private: std::ifstream file_; uintmax_t fileSize_; uintmax_t currentPosition_; bool isComplete_; };4.2.4 Range 请求处理// 解析 Range 头部 std::string rangeHeader req.getHeader(Range); uintmax_t startPos 0; uintmax_t endPos fileSize - 1; bool isRangeRequest false; if (!rangeHeader.empty()) { std::regex rangeRegex(bytes(\\d)-(\\d*)); std::smatch matches; if (std::regex_search(rangeHeader, matches, rangeRegex)) { startPos std::stoull(matches[1]); if (!matches[2].str().empty()) { endPos std::stoull(matches[2]); } isRangeRequest true; } } // 设置响应状态码 if (isRangeRequest) { resp-setStatusCode(HttpResponse::k206PartialContent); resp-addHeader(Content-Range, bytes std::to_string(startPos) - std::to_string(endPos) / std::to_string(fileSize)); } else { resp-setStatusCode(HttpResponse::k200Ok); } // 支持断点续传的头部 resp-addHeader(Accept-Ranges, bytes); resp-addHeader(Content-Length, std::to_string(endPos - startPos 1));4.3 用户认证与 Session 管理4.3.1 架构图┌──────────────────────────────────────────────────────────────────────────────┐ │ Session Management │ ├──────────────────────────────────────────────────────────────────────────────┤ │ │ │ ┌────────────┐ ┌───────────────────┐ ┌──────────────┐ │ │ │ Login │──────────▶│ Session │─────────▶ │ MySQL │ │ │ │ │ │ Created │ │ sessions │ │ │ └────────────┘ └─────────┬─────────┘ │ table │ │ │ │ └──────────────┘ │ │ │ │ │ ┌────────────────┐ │ Session ID (32 chars) │ │ │ Client │◀─────────────────┘ stored in: │ │ │ │ - localStorage (Browser) │ │ └───────┬────────┘ - X-Session-ID header │ │ │ │ │ │ Subsequent Requests │ │ │ X-Session-ID: xxx │ │ ▼ │ │ ┌────────────┐ ┌────────────┐ ┌────────────┐ │ │ │ API │─────────▶ │ Validate │─────────▶ │ Update │ │ │ │ Request │ │ Session │ │ Expire │ │ │ └────────────┘ └──────┬─────┘ │ Time │ │ │ │ └────────────┘ │ │ Valid / Invalid │ │ │ │ │ ┌──────────────────┼──────────────────┐ │ │ ▼ ▼ ▼ │ │ Continue 401 Unauthorized 403 Forbidden │ │ Processing │ │ │ └──────────────────────────────────────────────────────────────────────────────┘4.3.2 登录时序图4.3.3 Session 验证时序图4.3.4 核心代码实现Session ID 生成http_upload.cc 第 1421-1435 行std::string generateSessionId() { std::random_device rd; std::mt19937 gen(rd()); std::uniform_int_distribution dis(0, 35); const char* chars abcdefghijklmnopqrstuvwxyz0123456789; std::string sessionId; sessionId.reserve(32); for (int i 0; i 32; i) { sessionId chars[dis(gen)]; } return sessionId; }Session 验证http_upload.cc 第 1449-1479 行bool validateSession(const std::string sessionId, int userId, std::string username) { if (sessionId.empty()) { return false; } // 查询未过期的会话 std::string query SELECT user_id, username FROM sessions WHERE session_id escapeString(sessionId) AND expire_time NOW(); MYSQL_RES* result executeQueryWithResult(query); if (!result || mysql_num_rows(result) 0) { return false; } MYSQL_ROW row mysql_fetch_row(result); userId std::stoi(row[0]); username row[1]; mysql_free_result(result); // 更新过期时间滑动过期 std::string updateQuery UPDATE sessions SET expire_time DATE_ADD(NOW(), INTERVAL 30 MINUTE) WHERE session_id escapeString(sessionId) ; executeQuery(updateQuery); return true; }4.4 文件分享机制4.4.1 分享类型说明分享类型说明访问方式private私有不分享仅文件所有者public完全公开任何人通过链接访问protected需要提取码链接 6位提取码use指定用户仅指定用户可访问4.4.2 分享流程图┌───────────────────────────────────────────────────────────────────────────────┐ │ File Share Flow │ ├───────────────────────────────────────────────────────────────────────────────┤ │ │ │ 1. 创建分享 │ │ ┌───────────────┐ ┌────────────────┐ ┌────────────────┐ │ │ │ 用户选择 │───────▶│ 生成 │───────▶ │ 写入 │ │ │ │ 分享类型 │ │ shareCode │ │ file_shares │ │ │ │ 设置过期 │ │ extract │ │ 表 │ │ │ │ 时间 │ │ Code(保护) │ │ │ │ │ └───────────────┘ └────────────────┘ └────────────────┘ │ │ │ │ │ ▼ │ │ ┌─────────────────┐ │ │ │ 返回分享链接 │ │ │ │ /share/{code} │ │ │ └─────────────────┘ │ │ │ │ 2. 访问分享 │ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ │ │ 访问链接 │──────▶│ 查询 │──────▶│ 权限检查 │ │ │ │ /share/xx │ │ shareCode │ │ │ │ │ └──────────────┘ └──────────────┘ └───────┬──────┘ │ │ │ │ │ ┌───────────┼────────────┐ │ │ ▼ ▼ ▼ │ │ 公开文件 需要提取码 指定用户 │ │ │ │ │ │ │ ▼ ▼ ▼ │ │ 直接访问 验证extract 验证用户 │ │ Code 身份 │ │ │ └───────────────────────────────────────────────────────────────────────────────┘4.4.3 分享时序图4.5 视频流式传输优化4.5.1 问题背景传统视频下载会导致用户需要等待整个文件下载完成才能播放大视频文件消耗大量带宽用户可能只看几秒钟就关闭4.5.2 优化方案┌────────────────────────────────────────────────────────────────────┐ │ Video Streaming Optimization │ ├────────────────────────────────────────────────────────────────────┤ │ │ │ 传统方式 优化后 │ │ ┌───────────────┐ ┌──────────────────┐ │ │ │ 下载整个 │ 10秒 才能播放 │ 首帧缓冲 │ 1秒内 │ │ │ 文件 │ │ 2-5秒 │ 播放 │ │ │ (100MB) │ │ │ │ │ └───────────────┘ └────────┬─────────┘ │ │ │ │ │ ▼ │ │ ┌──────────────┐ │ │ │ 暂停下载 │ │ │ │ 保持连接 │ │ │ │ 可用状态 │ │ │ └───────┬──────┘ │ │ │ │ │ 用户点击播放 ────────▶│ │ │ ▼ │ │ ┌──────────────┐ │ │ │ 恢复下载 │ │ │ │ 流式传输 │ │ │ └──────────────┘ │ │ │ │ 关键技术 │ │ 1. HTTP 206 Partial Content (Range 请求) │ │ 2. 视频 moov atom 前置 (ffmpeg -movflags faststart) │ │ 3. 智能缓冲控制 (metadata 加载后暂停) │ │ │ └────────────────────────────────────────────────────────────────────┘4.5.3 前端缓冲控制逻辑index.html// 关键代码index.html 第 1206-1270 行 videoElement.addEventListener(loadedmetadata, function() { // 保存原始视频URL和时长信息 videoElement.dataset.originalSrc videoElement.src; videoElement.dataset.duration videoElement.duration; // 目标缓冲时间5秒 或 视频总时长的 10% const targetBufferTime 5; const minBufferPercent 0.1; const minBufferSeconds Math.min(targetBufferTime, videoElement.duration * minBufferPercent); // 设置缓冲检查定时器 const bufferCheckInterval setInterval(() { if (videoElement.hasAttribute(data-played)) { clearInterval(bufferCheckInterval); return; } // 检查缓冲进度 if (videoElement.buffered.length 0) { const bufferedEnd videoElement.buffered.end(0); // 缓冲足够数据后中断连接 if (bufferedEnd minBufferSeconds) { clearInterval(bufferCheckInterval); videoElement.pause(); videoElement.removeAttribute(src); videoElement.load(); // 终止网络请求 } } }, 500); // 最大等待10秒防止无限等待 setTimeout(() { if (!videoElement.hasAttribute(data-played)) { clearInterval(bufferCheckInterval); // 至少有1秒数据就中断 if (videoElement.buffered.length 0 videoElement.buffered.end(0) 1) { videoElement.pause(); videoElement.removeAttribute(src); videoElement.load(); } } }, 10000); }); // 用户点击播放时恢复下载 videoElement.addEventListener(play, function() { videoElement.setAttribute(data-played, true); if (!videoElement.hasAttribute(data-playing)) { videoElement.setAttribute(data-playing, true); if (!videoElement.src videoElement.dataset.originalSrc) { videoElement.src videoElement.dataset.originalSrc; } } });4.5.4 优化效果对比指标优化前优化后首帧时间10秒1-3秒初始流量消耗100% 文件大小2-5% 文件大小服务器连接占用持续直到完整下载按需恢复用户体验差长时间等待好秒开播放5.项目运行5.1 环境要求操作系统Linux (Ubuntu 18.04 / CentOS 7)编译器g 7.0 (支持 C17)CMake3.10MySQL5.7 或 8.0依赖库libmysqlclient-dev (MySQL C API)nlohmann/json (单头文件 JSON 库已包含)5.2 数据库初始化# 进入项目目录 # 导入数据库替换为你的 MySQL 用户名和密码 mysql -u root -p123456 file_manager.sql # 验证导入成功 mysql -u root -p123456 -e USE file_manager; SHOW TABLES;5.3 编译运行# 创建构建目录 mkdir build cd build # 生成构建文件 cmake .. # 编译 make -j4 # 运行服务器 ./bin/http_upload # 访问 http://localhost:80005.4 配置文件说明数据库连接配置在 http_upload.cc 中// HttpUploadHandler 构造函数第 277-291 行 HttpUploadHandler(int numThreads, const std::string dbHost localhost, const std::string dbUser root, const std::string dbPassword 123456, const std::string dbName file_manager, unsigned int dbPort 3306)修改默认配置// main 函数中创建 handler 时指定 auto handler std::make_sharedHttpUploadHandler(4, // 线程数 localhost, // 主机 root, // 用户名 your_password, // 密码 file_manager, // 数据库名 3306); // 端口项目代码领取C Linux学完后做什么项目推荐做C Linux云存储项目附带完整项目源码和文档