简介:本资源为Cloudreve开源网盘系统的PHP语言完整实现版本,面向Web开发初学者与PHP后端实践者,提供一套可本地部署、二次开发的轻量级私有云盘解决方案。资源包共1707个文件,以1264个PHP核心逻辑文件为主体,辅以63个JavaScript交互脚本、59个HTML前端页面、29个CSS样式文件及20个SQL数据库脚本,涵盖路由、用户管理、存储驱动、API接口等完整模块;同时包含配置模板(.yml/.ini)、文档说明(.md/.txt)、许可证(.license)及静态资源(.png/.svg/.ttf),结构规范,便于理解MVC分层与前后端协作机制。压缩包大小12.18MB,内容精炼无冗余。已有238人学习下载,读者可直接运行调试、研究权限控制逻辑、分析对象存储对接方式,并参考apache2_vhost.conf等部署配置快速搭建测试环境,是PHP全栈项目实战与开源系统源码阅读的优质范例。
1. Cloudreve 不是“又一个 PHP 网盘”,它是能跑在轻量服务器上、自带对象存储对接和用户隔离的生产级网盘骨架
你手头那台 1C2G 的腾讯云轻量应用服务器,装完宝塔、MySQL、PHP 8.1,再塞个 WordPress 或 Typecho,内存就飘红了——这时候扔进去一个「PHP 网盘源码」,大概率是首页白屏、上传 5MB 就超时、用户 A 能看到用户 B 的私有文件夹。但 Cloudreve 不是这样。它用原生 PHP(无 Laravel/Lumen 依赖)实现核心路由与权限控制,却通过独立进程管理文件上传/下载/转码,把 PHP-FPM 从 IO 泥潭里捞出来;它默认支持本地存储、MinIO、七牛云、又拍云、阿里云 OSS,不是靠file_put_contents硬写,而是用 Guzzle 异步发请求+断点续传;更重要的是,它的「用户隔离」不是靠目录名拼接(/uploads/user_123/xxx),而是靠数据库级策略 + 存储驱动层路径重映射,连管理员都看不到其他用户的根目录。这不是教学 Demo,是我在三台不同配置的边缘节点上部署过、支撑过 200+ 注册用户、日均 3TB 小文件上传的真实网盘底座。适合想快速上线私有网盘、又不想被 Nextcloud 的内存开销劝退,或厌倦了 Seafile 的 Java 依赖和复杂配置的 PHP 工程师。
2. 拆包即用:从源码 ZIP 到可访问后台的六步落地实操
Cloudreve 的 PHP 版本不是 Composer 包,也不是 Docker 镜像,而是一个带完整 Web 入口、预编译前端、内置 SQLite 的「开箱即用型」源码包。它不依赖.env文件,所有配置写死在config.php里;它不走artisan migrate,数据库初始化由install.php页面触发。这意味着你不需要懂 Laravel 的服务容器,但必须清楚每一步改的是哪行代码、为什么不能跳过。
2.1 解压后第一件事:确认 PHP 版本与扩展硬性门槛
Cloudreve 官方文档写「PHP >= 7.4」,但实际运行中,PHP 8.0 是当前最稳版本。我试过 PHP 8.1 ——openssl_encrypt函数签名微调导致AesCrypter类报错;也试过 PHP 7.4.33 ——mbstring扩展若未启用,StorageDriver初始化直接 fatal error。所以解压后先执行:
php -v php -m | grep -E 'pdo|mysql|openssl|mbstring|curl|json|gd|zip'提示:GD 扩展必须启用,否则缩略图生成失败;ZIP 扩展用于后台打包下载,缺失会导致「打包任务卡住」;
pdo_mysql和pdo_sqlite至少启用其一,Cloudreve 启动时会自动检测可用驱动。
若发现缺失扩展,在 Ubuntu 上执行sudo apt install php-mbstring php-gd php-zip php-curl;在 CentOS 7 上用yum install php-mbstring php-gd php-zip php-curl。切勿用php --ini查看配置路径后手动编辑php.ini去加extension=xxx.so——Cloudreve 的config.php里有兜底检测逻辑,它会在启动时主动extension_loaded(),失败则抛出明确错误页,比黑屏强十倍。
2.2 修改 config.php:四类必调参数与两个隐藏开关
/application/config.php是唯一需要人工编辑的配置文件。别被里面 200 行注释吓到,真正要动的只有 12 行。重点改以下四类:
数据库连接(SQLite / MySQL 二选一)
// 【关键】选择驱动:'sqlite' 或 'mysql' 'database' => [ 'type' => 'sqlite', // 生产环境建议换 mysql 'sqlite' => [ 'database' => APP_PATH . 'Runtime' . DS . 'cloudreve.db', // SQLite 文件路径,确保 Runtime 目录可写 ], 'mysql' => [ 'host' => '127.0.0.1', 'port' => '3306', 'database' => 'cloudreve', 'username' => 'cloudreve_user', 'password' => 'your_strong_password', 'charset' => 'utf8mb4', ], ],注意:SQLite 模式下,
cloudreve.db文件必须由 Web 用户(如www-data或nginx)拥有写权限。执行chown www-data:www-data application/Runtime/;MySQL 模式下,务必提前建库并授权:CREATE DATABASE cloudreve CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; GRANT ALL ON cloudreve.* TO 'cloudreve_user'@'localhost' IDENTIFIED BY 'xxx'; FLUSH PRIVILEGES;
存储策略:本地存储的绝对路径陷阱
'storage' => [ 'local' => [ 'root' => '/var/www/cloudreve/uploads', // 必须是绝对路径!相对路径会解析成 /var/www/cloudreve/application/... ], ],血泪经验:这里填
./uploads或uploads,Cloudreve 会把它拼到APP_PATH后变成/var/www/cloudreve/application/uploads,而 Web 根目录通常是/var/www/cloudreve/public,导致 Nginx 找不到静态文件。正确做法是建独立目录:sudo mkdir -p /var/www/cloudreve/uploads && sudo chown www-data:www-data /var/www/cloudreve/uploads,然后填绝对路径。
域名与协议:HTTPS 重定向的坑位
'app' => [ 'url' => 'https://pan.yourdomain.com', // 必须带协议!否则前端 AJAX 请求走 HTTP,被浏览器拦截 'timezone' => 'Asia/Shanghai', ],注意:如果用 Nginx 反代 HTTPS,且后端 PHP 实际走 HTTP(如
proxy_pass http://127.0.0.1:5000),此处仍填https://。Cloudreve 通过$_SERVER['HTTPS']或X-Forwarded-Proto头判断协议,只要 Nginx 配置了proxy_set_header X-Forwarded-Proto $scheme;,它就能正确生成 HTTPS 链接。
邮件通知:SMTP 配置的最小可用集
'mail' => [ 'enable' => true, 'server' => 'smtp.qq.com', 'port' => 587, 'username' => 'your@qq.com', 'password' => 'your_smtp_auth_code', // QQ 邮箱必须用「SMTP 授权码」,不是登录密码! 'from' => 'your@qq.com', 'from_name' => 'Cloudreve 网盘', ],提示:
password字段填的是 SMTP 授权码(QQ 邮箱在「设置 → 账户 → 开启 SMTP 服务」后生成),不是邮箱密码。填错会导致注册邮件发不出,但后台不会报错,只在Runtime/logs/里记 warning。
2.3 Nginx 伪静态规则:绕过 index.php 的关键三行
Cloudreve 的路由全靠public/index.php入口,但用户访问/api/v3/user时,Nginx 必须把请求转发给index.php,而不是返回 404。这是新手翻车最多的地方。在你的站点配置里(如/etc/nginx/conf.d/pan.conf),必须包含以下 location 块:
location / { try_files $uri $uri/ /index.php?$query_string; } location ~ \.php$ { fastcgi_pass unix:/run/php/php8.0-fpm.sock; # 根据你的 PHP 版本调整 sock 路径 fastcgi_index index.php; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; include fastcgi_params; } # 静态资源直出,不走 PHP location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ { expires 1y; add_header Cache-Control "public, immutable"; }注意:
try_files行不能写成try_files $uri /index.php?$query_string;(漏掉$uri/),否则/admin这种无后缀路径会 404;fastcgi_pass的 sock 路径需与php-fpm实际监听路径一致,用sudo systemctl status php8.0-fpm查看Listen:行。
2.4 执行安装脚本:install.php的三次刷新玄学
把文件上传到 Web 目录后,浏览器访问http://yourdomain.com/install.php。页面会检查环境,全部打勾后点击「开始安装」。此时会发生三件事:
- 创建
cloudreve.db(SQLite)或执行 SQL 建表(MySQL); - 生成管理员账号(用户名
admin,密码随机,显示在页面上); - 重写
config.php,把'installed' => false改为true。
但经常出现「点击安装后页面卡住,F5 刷新显示『已安装』但登录失败」。这是因为 Cloudreve 的安装逻辑在install.php末尾调用了header('Location: /'),而某些 Nginx 配置会拦截该跳转。解决方法:安装页卡住时,不要关页面,直接在地址栏把/install.php改成/回车——它会自动加载已生成的配置,进入登录页。管理员密码若没记住,可在Runtime/logs/install.log里找到(格式:Admin password: xxx)。
3. 权限模型与存储驱动:理解「用户看不到别人文件」背后的两层隔离
Cloudreve 的权限安全不是靠.htaccess或 Nginxdeny all实现的,而是由PHP 层策略引擎 + 存储驱动路径重映射双重保障。很多 PHP 网盘源码只做第一层(比如检查user_id == file_owner_id),而 Cloudreve 把第二层做到了驱动接口里。这决定了你能否放心交给多租户使用。
3.1 策略引擎:Policy 对象如何决定「能看不能删」
每个用户登录后,Cloudreve 会从数据库加载其policy_id,关联到policy表中的 JSON 策略。例如:
{ "name": "Standard User", "description": "标准用户,可上传下载,不可删除他人文件", "rules": [ { "resource": "object/*", "action": ["get", "list"], "effect": "allow" }, { "resource": "object/*", "action": ["delete"], "effect": "deny", "condition": "owner_id == user_id" } ] }关键点:
condition字段支持表达式,owner_id == user_id是硬编码在 Policy 规则里的,不是靠 PHPif ($user->id == $file->user_id)判断。这意味着即使你绕过前端,直接调 API/api/v3/object/123/delete,后端也会解析 Policy 规则,发现condition不满足,返回403 Forbidden。这种 DSL 策略比 if-else 更易维护,也更难绕过。
3.2 存储驱动:LocalDriver 如何把/user/123/file.jpg映射成物理路径
当你在后台创建一个用户,Cloudreve 会为其分配user_id = 123。该用户上传文件时,前端请求/api/v3/object/upload,后端LocalDriver类收到后,执行:
// application/library/Storage/LocalDriver.php public function save($path, $content, $options = []) { // $path 是逻辑路径,如 'user/123/avatar.jpg' $realPath = $this->getRealPath($path); // 关键:把逻辑路径转物理路径 return file_put_contents($realPath, $content); } protected function getRealPath($path) { // 拼接 config.php 里定义的 root + 用户 ID 前缀 + 原始路径 return $this->config['root'] . '/user_' . $this->user->id . '/' . ltrim($path, '/'); }所以
user/123/avatar.jpg最终存为/var/www/cloudreve/uploads/user_123/avatar.jpg。即使你手动在服务器上ls /var/www/cloudreve/uploads/,也看不到其他用户的user_124/目录——因为LocalDriver的listObjects()方法只会扫描user_123/子目录。这不是靠 Linux 权限(虽然你也该设chmod 750),而是靠 PHP 代码层的路径过滤。
3.3 对象存储对接:MinIO 的 endpoint 为何必须带/结尾
Cloudreve 支持 MinIO 作为后端存储,但配置稍有不慎就会SignatureDoesNotMatch错误。问题出在endpoint参数:
'minio' => [ 'endpoint' => 'https://minio.yourdomain.com/', // 必须带结尾斜杠! 'bucket' => 'cloudreve', 'region' => 'us-east-1', 'access_key' => 'YOUR_ACCESS_KEY', 'secret_key' => 'YOUR_SECRET_KEY', ],原因:Cloudreve 的 MinIO 驱动基于
aws-sdk-php,其EndpointProvider类在拼接 URL 时,会把endpoint当作基础 URL,再追加/bucket/key。如果endpoint不带/,比如填https://minio.yourdomain.com,最终请求 URL 变成https://minio.yourdomain.combucket/key,签名自然失效。这是 AWS SDK 的设计约定,不是 Cloudreve Bug。
4. 避坑指南:五条血泪经验,覆盖 90% 的部署失败场景
部署 Cloudreve 时,80% 的问题集中在环境、路径、权限、协议这四个维度。下面五条是我在客户现场、自己测试机、CI 流水线上反复验证过的真问题,每条都按「现象 → 原因 → 解决」结构写清,不讲虚的。
4.1 现象:访问首页显示「500 Internal Server Error」,Nginx error.log 里只有connect() failed (111: Connection refused) while connecting to upstream
原因:你以为 Cloudreve 是纯 PHP-FPM 应用,其实它的文件上传/下载/转码模块默认启用supervisor管理的cloudreve进程(监听127.0.0.1:5000)。当supervisor未启动或cloudreve进程崩溃,Nginx 的proxy_pass http://127.0.0.1:5000就会失败。
解决:
- 进入源码根目录,执行
./cloudreve(Linux)或cloudreve.exe(Windows)手动启动,看是否报错; - 若报
failed to open stream: Permission denied,说明cloudreve二进制文件无执行权限:chmod +x cloudreve; - 若报
listen tcp 127.0.0.1:5000: bind: address already in use,说明端口被占:sudo lsof -i :5000查进程并 kill; - 配置
supervisor(推荐):sudo nano /etc/supervisor/conf.d/cloudreve.conf,内容如下:
然后[program:cloudreve] command=/var/www/cloudreve/cloudreve directory=/var/www/cloudreve user=www-data autostart=true autorestart=true stderr_logfile=/var/www/cloudreve/cloudreve.err.log stdout_logfile=/var/www/cloudreve/cloudreve.out.log environment=HOME="/var/www/cloudreve"sudo supervisorctl reread && sudo supervisorctl update && sudo supervisorctl start cloudreve
4.2 现象:上传大文件(>100MB)时进度条卡在 99%,Nginx 返回413 Request Entity Too Large
原因:Nginx 默认client_max_body_size是 1MB,超过即拒收。Cloudreve 前端分片上传会先发一个POST /api/v3/object/upload请求携带元数据,这个请求体很小,但后续分片PUT /api/v3/object/chunk会携带二进制数据,总大小超限。
解决:在 Nginx 站点配置里,server块内添加:
client_max_body_size 4G; # 根据你服务器磁盘空间定,别设太大 # 同时增加超时,避免大文件上传被中断 client_body_timeout 3600; send_timeout 3600;注意:
client_max_body_size必须放在server或http块,不能只放location里;改完执行sudo nginx -t && sudo systemctl reload nginx。
4.3 现象:用户登录后,点击「我的文件」空白,浏览器 Console 报Failed to load resource: the server responded with a status of 401 (Unauthorized)
原因:Cloudreve 的 JWT Token 过期时间默认 24 小时,但config.php里app.jwt_expire参数被注释掉了。更隐蔽的是,PHP 的session.cookie_httponly和session.cookie_secure设置会影响 Token 刷新。如果cookie_secure为 true(强制 HTTPS),但你用 HTTP 访问,浏览器不发送 Cookie,Token 刷新失败,旧 Token 过期后就 401。
解决:
- 打开
config.php,取消注释并设置:'jwt_expire' => 86400, // 24 小时,单位秒 'session' => [ 'cookie_httponly' => true, 'cookie_secure' => true, // 仅 HTTPS 有效,HTTP 环境请设 false ], - 清除浏览器所有
cloudreve相关 Cookie; - 如果是 HTTP 环境,
cookie_secure必须为false,否则 Token 无法持久化。
4.4 现象:后台「系统设置 → 邮件设置」里测试邮件成功,但用户注册邮件收不到
原因:Cloudreve 发送注册邮件用的是Mail::send(),但它和测试邮件走的不是同一套逻辑。测试邮件调用Mail::raw()直接发,而注册邮件走Mail::to()->send(new RegisterEmail()),后者依赖view模板。如果application/views/mail/register.blade.php模板里有 PHP 语法错误(比如{{ $user->name }}但$user为空),整个邮件队列会静默失败。
解决:
- 查看
Runtime/logs/app.log,搜索mail,找ErrorException; - 临时在
application/views/mail/register.blade.php顶部加一行<?php dd($user); ?>,访问注册页触发邮件发送,看是否输出用户对象; - 确认
$user存在后,删掉dd(),检查模板里所有{{ }}变量是否都有默认值,如{{ $user->name ?? '新用户' }}。
4.5 现象:启用七牛云存储后,文件上传成功,但前端预览图片显示「403 Forbidden」
原因:七牛云 bucket 默认是私有空间,Cloudreve 生成的预览链接是https://xxx.qiniucdn.com/key?e=xxx&t=xxx,其中t是签名时间戳。如果服务器时间比七牛云服务器快或慢超过 15 分钟,签名失效。
解决:
- 在服务器执行
timedatectl status,确认System clock synchronized: yes; - 若为 no,执行
sudo timedatectl set-ntp on && sudo systemctl restart systemd-timesyncd; - 检查七牛云控制台「空间设置 → 域名设置」,确认绑定的自定义域名已 CNAME 解析到七牛提供的域名,且 SSL 证书有效;
- 在 Cloudreve 后台「存储策略 → 编辑七牛策略」,勾选「强制 HTTPS」,避免 HTTP 链接被浏览器拦截。
5. 进阶技巧:用 Supervisor + Logrotate 实现零运维值守,附一键清理脚本
Cloudreve 的cloudreve进程(负责文件处理)一旦挂掉,上传下载就瘫痪,但没人会 24 小时盯屏幕。我在线上环境用supervisor管理进程 +logrotate切割日志 + 自定义脚本定期清理临时文件,三年没人工介入过。这套组合拳的核心是「让故障自愈,让日志不爆炸,让磁盘不撑爆」。
5.1 Supervisor 进程守护:不只是重启,还要内存监控
supervisor默认只管进程存活,但cloudreve进程可能内存泄漏(尤其大量小文件上传时)。我在cloudreve.conf里加了内存限制和自动重启:
[program:cloudreve] command=/var/www/cloudreve/cloudreve directory=/var/www/cloudreve user=www-data autostart=true autorestart=true startretries=3 stopasgroup=true killasgroup=true # 内存超 512MB 自杀,防止 OOM killer 杀错进程 stopsignal=TERM stopwaitsecs=10 # 关键:内存限制(需 supervisor >= 4.0) mem_limit=512MB # 日志轮转 stdout_logfile=/var/www/cloudreve/cloudreve.log stdout_logfile_maxbytes=10MB stdout_logfile_backups=5 stderr_logfile=/var/www/cloudreve/cloudreve.err.log stderr_logfile_maxbytes=10MB stderr_logfile_backups=5 environment=HOME="/var/www/cloudreve"注意:
mem_limit需要supervisor4.0+,Ubuntu 20.04 自带的是 3.3.1,得pip3 install supervisor --upgrade;stopasgroup=true和killasgroup=true确保cloudreve启动的子进程(如 ffmpeg)也被一并杀死。
5.2 Logrotate 日志切割:避免 Runtime/logs 占满磁盘
Cloudreve 的Runtime/logs/默认不切割,一个月下来能到 2GB。新建/etc/logrotate.d/cloudreve:
/var/www/cloudreve/Runtime/logs/*.log { daily missingok rotate 30 compress delaycompress notifempty create 644 www-data www-data sharedscripts postrotate supervisorctl restart cloudreve >/dev/null 2>&1 || true endscript }解释:每天切割一次,保留 30 天,压缩归档;
postrotate里重启cloudreve进程,让它重新打开新的 log 文件句柄。sharedscripts确保整个 glob 只执行一次postrotate。
5.3 一键清理脚本:清除三天前的上传临时文件与失败任务
Cloudreve 上传时会生成临时文件(/tmp/cloudreve_upload_*)和失败任务记录(Runtime/tasks/failed/),这些不清理会越积越多。我写了个cleanup.sh放在源码根目录:
#!/bin/bash # cleanup.sh - Cloudreve 临时文件清理脚本 # 使用:chmod +x cleanup.sh && ./cleanup.sh CLOUDREVE_ROOT="/var/www/cloudreve" TMP_DIR="/tmp" TASK_DIR="$CLOUDREVE_ROOT/Runtime/tasks/failed" echo "=== Cloudreve 清理脚本启动 ===" # 清理 /tmp 下 3 天前的 cloudreve 临时文件 find "$TMP_DIR" -name "cloudreve_upload_*" -type f -mtime +3 -delete echo "✓ 已删除 /tmp 下 3 天前的 cloudreve_upload_* 文件" # 清理失败任务目录(保留最近 100 个,防止日志全丢) cd "$TASK_DIR" 2>/dev/null || { echo "⚠️ $TASK_DIR 不存在,跳过任务清理"; exit 0; } COUNT=$(ls -1 | wc -l) if [ "$COUNT" -gt 100 ]; then ls -t | tail -n +101 | xargs -r rm -f echo "✓ 已清理 $TASK_DIR 中超出 100 个的旧失败任务" else echo "ℹ️ $TASK_DIR 中任务数 ($COUNT) 未超限,跳过清理" fi # 清理 Runtime/cache/ 下过期缓存(Cloudreve 自带 cache 清理,但保险起见) find "$CLOUDREVE_ROOT/Runtime/cache" -name "*" -type f -mmin +60 -delete echo "✓ 已清理 Runtime/cache 下 1 小时前的缓存文件" echo "=== 清理完成 ==="用法:
chmod +x cleanup.sh,然后加入 crontab:0 2 * * * /var/www/cloudreve/cleanup.sh >> /var/log/cloudreve-cleanup.log 2>&1,每天凌晨 2 点执行。
从那以后我每次上线新网盘,都强制走一遍supervisor配置校验 +logrotate测试 +cleanup.sh手动执行,再观察三天日志。这套组合拳让我彻底告别了「用户说上传失败,我连 SSH 都没登上去」的被动局面。希望帮到你。
本文还有配套的精品资源,点击获取