简介:Cloudreve 个人网盘系统源码基于 Go 框架开发,面向需要自建私有云盘的个人站长、小型团队或开发者,解决主流网盘限速、涨价和数据自主管控等痛点。系统支持本地存储及七牛、阿里云 OSS、腾讯云 COS、又拍云、OneDrive 等云存储驱动,后端采用 Go 与 Gin,前端采用 React、Redux 和 Material-UI,具备文件管理、分享链接、访问密码、有效期、下载次数限制、多用户管理和权限控制等功能,部署简单,适合二次开发。资源包为 zip 格式,共 341 个文件,大小约 594KB,以 314 个 Go 源码文件为主,另含 Dockerfile、YAML 配置、Markdown 说明文档和前端相关文件,目录覆盖 API 测试、WebDAV 客户端、管理模块等,结构清晰便于按模块学习。目前已有 656 人学习/下载,适合具备一定 Go 基础、对云存储对接与网盘权限设计感兴趣的开发者;通过阅读源码可以掌握多存储驱动接入方式、分享链接权限控制机制、用户体系与文件管理模块的实现思路,也能参考其前后端组织方式,用于个人网盘项目的扩展与改造。
1. Cloudreve 是什么:一台小机器就能撑起的个人网盘,源码背后是一套云存储抽象层
如果你手里有一台 1 核 2G 的小服务器,想把它变成能分享文件、能离线下载的个人网盘,Cloudreve 大概是目前最省事的方案之一。它是一个基于 Go 框架开发的开源项目,官方发布的是编译好的单文件二进制,而标题里的“源码”意味着你还可以直接拉仓库自行构建,改逻辑、加存储后端、做成私有化版本都不受限制。
Cloudreve 最核心的思路不是把磁盘塞进自己的程序里,而是把存储这件事抽象了一层:本机磁盘、七牛、阿里云 OSS、腾讯云 COS、又拍云、OneDrive 都算“存储策略”,你只需在网盘里建策略,上传文件时选策略,后端会自动把数据写到对应的对象存储或云盘上。也就是说,你的服务器只是控制面和流量中转,数据本体不占本地磁盘,扩容也变成“给 Bucket 充值”而不是“给服务器加盘”。
这套设计对我来说最大的价值是:小机器可以把内存省给 PHP、OCR 这类其他服务,网盘本身的内存占用长期维持在 80MB 上下。接下来我把从源码编译、Docker 部署到对接各种云存储后端的完整路径拆开讲,包括那些文档里不写但一定会翻车的细节。
2. 选型理由:Go 单二进制和存储策略抽象,为什么它适合个人网盘场景
2.1 从源码构建:为什么单二进制能这么小,以及编译时需要关注什么
Cloudreve 的前端是 Vue 写的,构建后被打包进 Go 的 embed 文件系统里。最终产物只有一个可执行文件,连静态资源都不需要单独放在磁盘上。这对服务器运维的意义很大:没有 Node 运行时依赖、没有 PHP-FPM、没有一堆配置文件兜底,拷过去就能跑,哪怕跑在 ARM 板子上也只是交叉编译的事。
从源码构建的常见做法是先拉仓库,再按版本打 tag。Go 的构建命令本身不复杂:
git clone --recurse-submodules https://github.com/cloudreve/Cloudreve.git cd Cloudreve # 编译前端资源,Cloudreve 需要先构建静态页面再嵌入二进制 cd frontend yarn install yarn build cd .. # 回到主目录,打包静态资源和后端代码 go build -o cloudreve -ldflags "-s -w" -tags "embed"参数说明:-tags "embed"是关键,它告诉 Go 编译器把frontend/dist目录内的内容嵌进二进制的embed.FS;如果不加这个 tag,程序能编译过,但跑起来首页会白屏,因为静态资源没进去。-ldflags "-s -w"是去掉符号表和调试信息的常用做法,二进制体积能缩减约 20%。另外建议编译前先跑一遍go mod tidy确认依赖完整,Cloudreve 的依赖里包含golang.org/x/crypto这类基础库,网络环境不干净时下载会卡住,但我不展开讲这个。
编译好之后,直接运行:
./cloudreve首次启动会在当前目录生成conf.ini和cloudreve.db。需要注意:Cloudreve 默认使用 SQLite,数据库文件就一个,备份非常方便,但并发写入能力弱。如果你要跑公共分享站或小团队使用,建议一开始就切到 MySQL。
2.2 存储策略机制:从“文件夹”到“存储后端”的一层映射
Cloudreve 里的存储策略(Storage Policy)是整个网盘的地基。理解它之后,多后端对接就不难了。策略的本质是一个 JSON 配置,里面写着后端类型、是否生成直链、URL 规则、上传方式等。用户创建目录时,可以给目录指定某一个策略;上传文件时,文件会被写入该策略对应的存储后端。同一个网盘里可以同时存在多个策略,比如把个人文件放 OneDrive,把分享出去的视频放 OSS,借此分摊流量成本。
策略有点像一个“转账接口”:你决定数据落在哪个后端,而不是数据怎么组织。Cloudreve 的驱动器层会按照策略配置把本地文件、七牛/OSS/COS/又拍这类 S3 兼容对象存储、OneDrive 这种网盘统一成一个接口。上层的文件表、分享表、用户表都只记录文件元数据和策略 ID,不关心文件真实存储位置。这带来的好处是,迁移存储后端时不需要动任何用户数据——把策略改一下,新上传的文件就去了新地方,老文件仍然留在原桶里,通过调整策略的 URL 规则继续访问。
权限模型上,Cloudreve 将用户分组,每个组能看到的存储策略列表是可配置的。比如游客组只能用“下载”策略,上传组才能用“雨季大文件”策略。这块配置在后台管理面板里完成,与源码层面的策略 JSON 是两个层次,别搞混:JSON 决定文件写到哪、用什么方式写;后台配置决定谁能用哪个策略。
从源码角度去看策略实现,会发现 Cloudreve 把每个后端都实现了一套 driver 接口,接口里定义了Put、Delete、Get、Thumb等方法。如果你要接一个新的私有存储,理论上只需新增一个 driver 文件并注册到drivers包里。这就是它作为可二次开发项目的核心扩展点。
3. 跑通最小可用系统:源码构建 + Docker 编排,把阿里云 OSS 挂成本地目录
3.1 两种部署方式怎么选:Docker Compose 更适合带 MySQL 的正式环境
Cloudreve 官方提供了 Docker 镜像,适合不想折腾编译、想把网盘当黑盒用的场景。我个人推荐用 Docker Compose 同时起cloudreve和mysql两个容器,因为 SQLite 在网盘使用量上来以后会频繁地出现“database is locked”。下面是我常用的编排文件骨架:
version: "3.7" services: cloudreve: image: cloudreve/cloudreve:latest container_name: cloudreve ports: - "5212:5212" volumes: - ./data:/data - ./conf.ini:/data/conf.ini environment: - TZ=Asia/Shanghai depends_on: - db restart: unless-stopped db: image: mysql:8.0 container_name: cloudreve-db environment: MYSQL_ROOT_PASSWORD: rootpw MYSQL_DATABASE: cloudreve MYSQL_USER: cloudreve MYSQL_PASSWORD: cloudrevepw volumes: - ./mysql:/var/lib/mysql restart: unless-stopped这里的conf.ini是 Cloudreve 的主配置,首次启动后容器会把默认配置拷贝到挂载目录。注意:如果你把conf.ini也放进./data下,路径要写成/data/conf.ini,否则 Docker 不会自动生成,Cloudreve 启动会直接报错退出。而 MySQL 容器先于 Cloudreve 启动,是因为 Cloudreve 启动时会检查数据库连通性,连接失败会直接退出,不会自动等待。
3.2 conf.ini 里的关键参数:session_secret、数据库连接和监听地址
Cloudreve 的conf.ini是 INI 格式,按[System]、[Database]、[Redis]分节。第一次启动后会自动生成默认值,但有两个参数必须手动改:一个是[System]下的session_secret,默认是随机字符串,容器重启不会变,但不改的话副作用是登录态在进程重启后被重置,所有人被迫重新登录;另一个是数据库连接串,如果你用 SQLite 就保持空着,在 Docker 里换成 MySQL 时这样写:
[System] ; 监听地址和端口 Listen = :5212 ; 登录会话密钥,必须手动改成随机长字符串 session_secret = a-random-string-at-least-32-bytes-long ; 首次运行后是否初始化管理员账号 ; 0 会要求命令行初始化,建议保持 0 Initialized = 0 [Database] ; 留空使用 SQLite Type = mysql ; MySQL 连接参数 Host = 127.0.0.1 Port = 3306 User = cloudreve Password = cloudrevepw Name = cloudreve Charset = utf8mb4参数说明:Listen端口默认 5212,如果前面有 Nginx,务必让 Nginx 把proxy_pass指向http://127.0.0.1:5212,否则跨域和 WebSocket 都会出问题。Initialized = 0时,进程启动后会在控制台输出管理员账号一次性密码,这个信息只会出现一次,建议立即登录后台改掉。Docker 部署时容器日志同样可以看到,注意别把日志输出到公网日志平台。
数据库这部分我吃过亏:Charset写成utf8而不是utf8mb4,用户上传带 emoji 的文件名时数据库写入直接报错,文件存进去了但列表里看不到。后来统一用utf8mb4,问题就消失了。如果你接手一个已经建好的表,改完字符集后要手动执行ALTER TABLE转换,光改配置不生效。
3.3 绑定阿里云 OSS:创建存储策略,存下来直接可用
接下来是真正把 OSS 挂进来的步骤。后台登录后,进入“管理面板 → 存储策略 → 创建新策略”。你需要准备 OSS 的AccessKeyId和AccessKeySecret,Bucket 名、Region 和 Endpoint 是另一个概念,别选错。
{ "base_url": "https://your-custom-domain.example.com", "UploadURL": "https://oss-cn-hangzhou.aliyuncs.com", "DownloadURL": "https://your-custom-domain.example.com", "AccessKeyId": "xxxxxxxxxxxxxxxx", "AccessKeySecret": "yyyyyyyyyyyyyyyy", "BucketName": "my-cloudreve-files", "Region": "oss-cn-hangzhou", "Endpoint": "oss-cn-hangzhou.aliyuncs.com", "IsPrivate": false, "IsCNAME": true }这段 JSON 在 Cloudreve 的 OSS 策略配置里对应字段。几个容易出错的点:BaseURL是文件对外服务的域名,如果走自定义 CNAME 回源加速,必须写https://your-domain.com,并且该域名要在 OSS 控制台绑定 Bucket;UploadURL和Endpoint很多人看成同一个东西,但它们不一定相等——如果你的服务器和 OSS 在同地域,可以用内网 Endpoint 走内网上传,流量费为零。IsCNAME表示是否使用自定义域名访问,开启后 DownloadURL 里的域名才会生效,否则文件直链会生成到 OSS 默认域名,隐私空间下会有防盗链问题。配置完后,回到云存储根目录新建一个文件夹,给文件夹指定这个策略,测试上传。
上传验证的最快方法不是点网页,而是直接构造一个 PUT 请求:
curl -X PUT "https://your-domain.com/upload/test.txt" \ -H "Authorization: Bearer <登录后的 Cookie 或 Token>" \ -d "hello cloudreve"如果返回201或200,且文件出现在 OSS 桶里,策略就通了。如果返回 403,优先检查IsPrivate字段:私有 Bucket 需要 Cloudreve 每次生成带签名的 URL,IsPrivate必须为true,否则前端拿不到文件内容。
4. 存储后端差异化接入:七牛、腾讯云 COS、又拍云、OneDrive 的配置边界
4.1 各后端的选型对比:哪些适合直传,哪些必须走回调
Cloudreve 的存储策略在底层对每个后端都做了适配,但它们的行为差异很大。我整理了一张表,按我自己的使用体验排了优先级:
| 后端 | 上传模式 | 外链与自定义域名 | 典型问题 |
|---|---|---|---|
| 阿里云 OSS | 直传(客户端直传 Bucket) | 支持 CNAME 自定义域名,签名 URL 支持 | 跨域配置复杂,私有读下载要走签名 |
| 腾讯云 COS | 直传 | 支持自定义域名,签名 URL 支持 | Endpoint 多了个cos.前缀,容易写错 |
| 七牛云 Kodo | 直传或回调上传 | 强制使用测试域名或绑定 CDN 域名 | 测试域名有 30 天有效期,正式用必须绑 CDN |
| 又拍云 | 表单回调 | 必须配置服务名和操作员 | 依赖回调地址公网可达,否则上传后不落盘 |
| OneDrive | 授权后服务端中转 | 不支持自定义域名直链 | 需要 Application 权限和定时刷新 Token |
选型建议:如果想让用户上传大文件时服务器内存不炸,优先选支持“直传”的 OSS、COS 和 Kodo。直传模式下,浏览器或客户端直接把文件传到 Bucket,Cloudreve 只负责生成临时凭证和记录元数据。相反,又拍云默认是表单异步回调,文件先到又拍服务器,又拍再回调你的 Cloudreve 地址,如果你的服务器没有公网 IP 或回调地址被防火墙挡了,文件就永远不回记录。
4.2 OneDrive 授权流程:从 Azure 注册应用到 Token 刷新,难点全在“回调”
OneDrive 是这套方案里特殊的一环。它不提供对象存储式的 Bucket,而是直接操作微软网盘。Cloudreve 需要拿到你 Azure AD(现在叫 Entra ID)应用的 client_id、client_secret,然后通过 OAuth 授权获取 access_token 和 refresh_token。授权完成后,Cloudreve 会把文件写到这个 OneDrive 账号的固定目录下。
操作上需要先到 Azure Portal 注册一个应用,设置 Redirect URI 为你的 Cloudreve 回调地址,格式一般是https://你的域名/api/v3/oauth/onedrive。然后给应用添加Files.ReadWrite和offline_access权限,先拿到授权码,再换取 token。这里有个坑:token 有效期短,Cloudreve 会用 refresh_token 自动续期,但refresh_token在微软侧有最长生命周期,一旦超过有效期,网盘会进入“只读”状态,上传直接报 401。
我的经验是把 OneDrive 策略的“自动刷新 Token”开关打开,并且在服务器上用 cron 每 10 分钟请求一次 Cloudreve 的/api/v3/oauth/onedrive状态接口,检查 token 是否还有效。无效时的处理不是手工重新授权,而是用 refresh_token 重新拉一次 token,然后写回数据库的存储策略表。更省心的方案是:如果 OneDrive 只是个人备份用途,干脆把它的优先级放低,不要让它承担大流量分享,因为微软 API 限流很快,下载量大时接口会间歇性返回 429。
4.3 自定义域名/CORS 配置:直传模式下最容易忽略的跨域和 Referer 防盗链
直传模式下,浏览器从your-domain.com发起请求,目标却是oss-cn-hangzhou.aliyuncs.com,这一看就是跨域。OSS 控制台里要给 Bucket 配置 CORS 规则,允许的来源写上你的后台域名,允许的方法至少包含GET, PUT, POST, DELETE, HEAD,ExposeHeaders里要加上ETag,否则 Cloudreve 拿不到文件校验值,上传后可能显示“校验失败”。COS 和 Kodo 的 CORS 配置同理,但它们默认允许的规则比 OSS 宽松,很多场景不配也能跑,等到浏览器报 Red Block 错误时再回头补。
防盗链上,OSS、COS 和七牛都支持“Referer 黑白名单”。如果你希望文件只能被自己的站点页面引用,可以把白名单填成*.your-domain.com。但要注意:这个规则会误伤 OneDrive 这种服务端下载模式,因为服务端下载时 Referer 可能是空的,容易被规则拦截。所以防盗链建议只加在纯直链的 Bucket 策略上,别全局套用。
5. 部署常见坑:上传卡 99%、离线下载失败、回调验签不过这几件事
5.1 上传 99% 一直转圈:Nginx 默认限制和表单大小上限
现象:小文件秒传成功,超过 100MB 的文件进度条走到 99% 就停住,过一会儿提示上传失败。
原因:Cloudreve 本身没有限制上传体积,但 Nginx 默认client_max_body_size只有 1MB,超过后直接返回 413。另一种情况是反向代理没有配置超时,大文件上传时间超过proxy_read_timeout的默认 60 秒,代理主动断开连接。
解决:在 Nginx 的 server 块中显式调整:
server { listen 80; server_name pan.example.com; client_max_body_size 20g; proxy_read_timeout 600s; proxy_send_timeout 600s; location / { proxy_pass http://127.0.0.1:5212; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } }client_max_body_size根据你的实际需求调整,20g 是常见上限。同时,如果是直传 OSS,Nginx 这个值其实只影响 Cloudreve 的 API 转发,浏览器到 OSS 的直传流量并不走你的 Nginx,所以别以为调了 Nginx 就能解决所有上传慢的问题,真正瓶颈可能在 OSS 公网带宽或跨地域链路。
5.2 文件传上去了,列表里看不到:SQLite 锁冲突与分库建议
现象:上传完成但刷新后文件不在目录里,管理后台能看到记录,用户端列表为空。
原因:最常见的是 SQLite 在并发写入时锁冲突。Cloudreve 的文件记录写入和其他查询业务在同一数据库文件上,上传高峰时多个写事务同时发生,SQLite 会随机拒掉一部分写入,程序没有重试机制,最终导致文件未入库。其次是列表中勾选了“只显示已上传完成”的状态过滤,上传中断剩下的临时记录被过滤掉。
解决:长期方案是换 MySQL,我前面给的 Compose 编排里直接带 MySQL 就是为了避免这个坑。临时方案是手动去 SQLite 里查一下记录状态:
sqlite3 cloudreve.db "select id, file_name, status, size from files;"如果有status = 0或upload_session_id存在但文件不完整,把对应记录删掉,让用户重新上传。记一条血泪经验:个人网盘前期用 SQLite 完全够,但一旦挂到公网并有几十个人同时传文件,就必须提前换 MySQL,否则某个周末流量高峰时你会收到一堆“传不上去”的反馈。
5.3 离线下载总是失败,提示超时或找不到下载器
现象:粘贴一个磁力链或 HTTP 下载链接,任务在后台停留几分钟后提示失败。
原因:Cloudreve 本身不实现下载协议,它依赖外部命令aria2或rclone。Docker 镜像默认不含这两个组件,所以你从镜像直接启动,离线下载面板就像个摆设。另外容器内的aria2需要配置 RPC 地址,默认是http://localhost:6800/jsonrpc,如果容器里没起 aria2 服务,Cloudreve 连不上。
解决:使用支持aria2的镜像版本,或者在宿主机装好aria2后,在 Cloudreve 管理后台把 RPC 地址指向宿主机的 IP 和端口:
[aria2] rpc_server = http://127.0.0.1:6800/jsonrpc rpc_secret = your-secret同时确认 aria2 启动时开启 RPC 模式:
aria2c --enable-rpc=true --rpc-listen-all=true --rpc-secret=your-secret --dir=/downloads注意rpc-secret要和 Cloudreve 后台填的一致,否则任务提交后会被 aria2 拒绝。反过来说,如果你根本不需要离线下载,可以无视这个报错,因为网盘核心功能不受影响。
5.4 用自定义域名访问后台,登录后自动退出:Session 域校验问题
现象:用 IP 访问一切正常,换成域名登录后,一刷新就回到登录页。
原因:Cloudreve 的 Session Cookie 默认绑定在 IP 或初始域名上。首次用 IP 启动后,后来换成域名,Session Cookie 的 Domain 不匹配,浏览器不发送 Cookie,后端自然认为未登录。
解决:修改conf.ini里的session_domain为你的域名,并保证前后访问方式一致。如果已经用 IP 登录过,先清除浏览器 Cookie 再重新登录。这个坑在本地调试时最容易踩:本地用localhost:5212,部署后改用pan.example.com,不刷新 Cookie 就始终卡在登录页。
6. 进阶技巧:用定时备份与策略隔离,把 Cloudreve 变成一台可靠的家庭数据枢纽
到这里,部署和存储对接基本收尾,最后说两个让这套系统长期稳定跑下去的维护手段,都是我踩过坑之后才加上的。
第一是数据库备份。Cloudreve 的元数据都集中在cloudreve.db(SQLite)或 MySQL 里,文件本体分散在各存储桶。备份时不要想着把文件也拷一份,那是存储后端的活。我习惯每天凌晨 3 点用sqlite3做在线备份,而不是直接cp数据库文件,因为 SQLite 在写入时直接复制文件会得到损坏的备份:
sqlite3 /path/to/cloudreve.db ".backup '/backup/cloudreve_$(date +\%F).db'"SQLite 的.backup命令利用在线备份 API,能保证一致性。如果跑 MySQL,就用mysqldump加上单事务参数。备份文件保留 14 天,配合云存储的跨区域复制,基本能做到任意一天故障可恢复。
第二是离线下载目录的定期清理。aria2 下载的文件会堆在/downloads,不清理的话磁盘迟早被填满。我一般在宿主机挂一个定时任务,把超过 7 天没被 Cloudreve 记录引用的临时文件删掉:
find /downloads -type f -mtime +7 -delete这个做法的前提是:Cloudreve 会把下载完成的文件转移进存储策略,转移完成后/downloads里只剩临时文件。如果你的策略没有启用“下载后自动转移”,这个命令会把已下载文件一起删掉,所以务必先确认策略配置里勾了“离线下载完成后自动转存”。
我用 Cloudreve 已经三年,最大的感受是它把“网盘”拆成了清晰的两层:上层是文件和分享逻辑,下层是可替换的云存储驱动。源码级扩展能力的价值在于,当你不满足于内置后端时,可以照着 driver 接口写自己的私有实现。希望这些配置和踩坑记录能帮到你——至少让你在一开始就避开我当年夜里爬起来换配置的那些弯路。
本文还有配套的精品资源,点击获取