QuickShare 0.11.4 API 使用详解
QuickShare 0.11.4 REST API 全量实测:v1/v2 端点对照、登录认证、分块上传、分享公开访问、九类实测错误码与排障实录。
QuickShare 0.11.4 API 使用详解
QuickShare 0.11.4 提供两套并存的 REST API:v1(完整功能)与 v2(0.11.1 起新增,按 admin/my/public 三个权限域重新组织)。全部接口基于 HTTP + JSON,用浏览器 cookie 承载登录态,因此用 curl 就能完成从登录、上传、下载到分享的全部操作。本文在真实 Ubuntu 服务器上对每类接口逐个实测,请求与响应均为真实记录。
环境与版本信息如下:
| 项目 | 值 |
|---|---|
| 服务器系统 | Ubuntu 22.04 LTS(x86_64) |
| QuickShare 版本 | 0.11.4(当前最新稳定版) |
| 服务地址 | http://localhost:8130(默认端口 8686,本服务器改为 8130) |
| 管理员账号 | admin / q1w2e3r4 |
| 测试用户 | user1 / user1pwd1(role=user) |
配置细节见《QuickShare 0.11.4 配置参数详解》,安装部署见《QuickShare 0.11.4 Docker Compose 部署指南》,组件选型背景见《QuickShare 介绍与选型指南》。
一、前置条件
- 服务已启动并可访问(
curl -X OPTIONS http://localhost:8130/v1/settings/health返回 200,该探针无需登录)。 - 已安装
curl(示例均用它演示)。 - 已知一个可登录账号(管理员 admin,或按 5.1 节创建普通用户)。
- 理解基础 JSON 语法;注意部分字段是字符串化的数字(见第六节通用约定)。
二、API 总览
2.1 版本与权限域
| 版本 | 前缀 | 权限组织 | 说明 |
|---|---|---|---|
| v1 | /v1/ | 按接口逐一授权 | 全功能,保持兼容 |
| v2 | /v2/admin/ | 仅管理员 | 用户/配额/角色/工作队列管理 |
| v2 | /v2/my/ | 登录用户(个人域) | 改密码、个人偏好、个人文件操作 |
| v2 | /v2/public/ | 匿名可访问 | 登录、验证码、站点配置、分享浏览 |
角色权限矩阵(实测核对):admin 可调用全部接口;user 可操作个人文件、改自己密码/偏好、创建分享,不能列全站用户(403);visitor(未登录)只能登录、读验证码、读站点配置、浏览公开分享(403 其余)。
2.2 接口清单(v1 为主干,v2 为对应映射)
| 域 | 接口 | 方法 | 权限 |
|---|---|---|---|
| 认证 | /v1/users/login(/v2/public/login) | POST | 匿名 |
| 认证 | /v1/users/logout、/v1/users/isauthed | POST / GET | 登录用户 |
| 用户 | /v1/users/(建)、/v1/users/list(/v2/admin/users/list)、/v1/users/pwd/force-set、/v1/fs/used-space | POST/GET/PATCH/PUT | admin |
| 用户 | /v1/users/pwd、/v1/users/self、/v1/users/preferences | PATCH/GET/PATCH | 登录用户 |
| 目录 | /v1/fs/dirs?dp=、/v1/fs/dirs/home、POST /v1/fs/dirs | GET/POST | 登录用户 |
| 上传 | POST /v1/fs/files、PATCH /v1/fs/files/chunks、GET /v1/fs/files/chunks?fp=、GET /v1/fs/uploadings、DELETE /v1/fs/uploadings?fp= | — | 登录用户 |
| 文件 | GET /v1/fs/files?fp=(下载)、DELETE /v1/fs/files?fp=、PATCH /v1/fs/files/move、PATCH /v1/fs/files/copy | — | 登录用户 |
| 检索 | /v1/fs/search?k=、/v1/fs/metadata?fp=、PUT /v1/fs/reindex、POST /v1/fs/hashes/sha1 | — | 登录用户 |
| 分享 | POST/DELETE/GET /v1/fs/sharings、GET /v1/fs/sharings/ids?fp=、GET /v1/fs/sharings/exist?fp= | — | 登录用户 |
| 公开分享 | /v2/public/sharings/exist?fp=、/v2/public/sharings/dirs?shid= | GET | 匿名 |
| 系统 | GET/PATCH /v1/settings/client、POST /v1/settings/errors、GET /v1/settings/workers/queue-len、OPTIONS /v1/settings/health | — | 混合 |
2.3 响应格式与错误码
成功:{"msg":"OK"}(操作类)或直接返回数据 JSON;失败统一 {"error":"原因"}(个别 404 为 {"msg":"NotFound"})。实测错误码全谱:
| 状态码 | 实测触发场景 | 实测响应体 |
|---|---|---|
| 200 | 正常操作 | {"msg":"OK"} / 数据 |
| 400 | 参数非法(文件已存在、用户名过短等) | {"error":"file already exists"} |
| 401 | 令牌无效/过期 | {"error":"token is expired"} |
| 403 | 越权或匿名访问受保护接口 | {"error":"access denied"} |
| 404 | 公开分享不存在 | {"msg":"NotFound"} |
| 500 | 服务端状态冲突(重复用户、计数为负等) | {"error":"constraint failed: UNIQUE constraint failed: t_user.name (2067)"} |
| 501 | 功能未实现(copy) | {"msg":"Not Implemented"} |
三、认证与首次调用
3.1 登录获取令牌
curl -s -D headers.txt -c admin.cookie -X POST http://localhost:8130/v1/users/login \
-H 'Content-Type: application/json' \
-d '{"user":"admin","pwd":"q1w2e3r4"}'
实测响应头与响应体:
{"msg":"OK"}
Set-Cookie: tk=eyJUeXAiOiJKV1QiLCJBbGciOiJIUzI1NiIsIkN0eSI6IiJ9.eyJleHBpcmUiOiIxNzk0MTEwMDM4IiwiaWF0IjoxNzkzNTA1MjM4LCJyb2xlIjoiYWRtaW4iLCJ1aWQiOiIwIiwidXNlciI6ImFkbWluIn0.maeznCBSdzLOKH0Skd2RvfJwl0q8MVbZXFMZFlwdYTM; Path=/; Max-Age=604800; HttpOnly
令牌放在名为 tk 的 cookie 里,有效期 Max-Age=604800(7 天,来自配置 users.cookieTTL),带 HttpOnly。令牌载荷四个字段:uid/user/role/expire。
开启验证码时(默认开启),登录前先 GET /v1/captchas/ 拿验证码 ID 与图片,登录体加 captchaId 与 captchaInput 两个字段。
3.2 携带令牌调用与状态校验
后续请求带上 cookie(-b admin.cookie)即可。校验登录态:
curl -s -w ' [%{http_code}]' -b admin.cookie http://localhost:8130/v1/users/isauthed
# 输出:{"msg":"OK"} [200]
登调用 POST /v1/users/logout(服务端把 cookie 置空)。
无效令牌实测(伪造 tk=abc.def.ghi):
{"error":"unable to unmarshal payload json: invalid character 'u' looking for beginning of value"} [401]
令牌过期实测(篡改载荷 expire 为过去时间):
{"error":"token is expired"} [401]
匿名访问受保护接口实测(不带 cookie 列用户):
{"error":"access denied"} [403]
3.3 权限隔离实测
创建普通用户后验证越权(创建接口见 4.1):
curl -s -c user1.cookie -X POST http://localhost:8130/v1/users/login \
-H 'Content-Type: application/json' -d '{"user":"user1","pwd":"user1pwd1"}'
# 输出:{"msg":"OK"}
curl -s -w ' [%{http_code}]' -b user1.cookie http://localhost:8130/v1/users/list
# 输出:{"error":"access denied"} [403]
普通用户操作文件被限制在自己目录(user1/ 前缀)内,越界同样 403;读他人分享出去的目录则放行。
四、API 域详解
4.1 用户管理(admin)
创建用户 POST /v1/users/,请求体 {"name":"user1","pwd":"user1pwd1","role":"user"}:
{"id":"1793505238817338967"} [200]
返回的是用户 ID(字符串形式的雪花 ID)。两类实测错误:
# 用户名短于 minUserNameLen(默认 3)
{"error":"name is too short"} [400]
# 用户名重复
{"error":"constraint failed: UNIQUE constraint failed: t_user.name (2067)"} [500]
列出用户 GET /v1/users/list(v2 对应 /v2/admin/users/list),实测响应(节选 admin 段):
{"users":[{"id":"0","name":"admin","pwd":"","role":"admin","usedSpace":"0",
"quota":{"spaceLimit":"1073741824","uploadSpeedLimit":52428800,"downloadSpeedLimit":52428800},
"preferences":{"bg":{...},"lan":"en_US","theme":"light"}},
{"id":"1","name":"visitor","role":"visitor",
"quota":{"spaceLimit":"0","uploadSpeedLimit":10485760,"downloadSpeedLimit":10485760}}]}
内置三个账号:admin(id=0)、visitor(id=1,匿名身份的化身)、自建用户。注意 spaceLimit/usedSpace 是字符串数字。
修改用户配额 PATCH /v1/users/:请求体带用户 ID 与 quota 对象;spaceLimit 必须传字符串(如 "104857600"),传数字会得到 500 json: invalid use of ,string struct tag(实测踩坑,见第七节)。
强制改密 PATCH /v1/users/pwd/force-set(admin 对任意用户);删除用户 DELETE /v1/users/{name}——id=0/1 的内置账号不可删(实测返回 400)。
4.2 目录操作
建目录 POST /v1/fs/dirs,请求体 {"path":"admin/files/d5demo"}:
{"msg":"OK"} [200]
重复创建同一目录同样返回 200(幂等,无 4xx)。
列目录 GET /v1/fs/dirs?dp=admin/files/d5demo:
{"cwd":"admin/files/d5demo","metadatas":[{"name":"hello.txt","size":12,
"modTime":"2026-11-01T11:53:59.033557506+08:00","isDir":false,
"sha1":"4e49a8e71453999d6929ffb854dee5d6c2e204c9"}]} [200]
定位个人根目录 GET /v1/fs/dirs/home(admin 的家目录是 admin/files):
{"cwd":"admin/files","metadatas":[{"name":"d5demo","size":4096,"isDir":true,"sha1":""}, ...]} [200]
普通用户的家目录是 <用户名>/files。
4.3 文件上传(分块协议)
QuickShare 上传是「建会话 → 逐块传 → 查进度」三步,每块内容用 base64 编码放在 JSON 里。
第一步:创建上传会话 POST /v1/fs/files,{"path":"admin/files/d5demo/hello.txt","fileSize":12}:
{"msg":"OK"} [200]
fileSize 是预告的总字节数(用于配额预扣)。重复创建同名文件:
{"error":"file already exists"} [400]
第二步:查询进度 GET /v1/fs/files/chunks?fp=admin/files/d5demo/hello.txt:
{"path":"admin/files/d5demo/hello.txt","isDir":false,"fileSize":12,"uploaded":0} [200]
第三步:传块 PATCH /v1/fs/files/chunks,content 为 base64(hello d5 api → aGVsbG8gZDUgYXBp),offset 必须等于当前 uploaded:
curl -s -w ' [%{http_code}]' -b admin.cookie -X PATCH http://localhost:8130/v1/fs/files/chunks \
-H 'Content-Type: application/json' \
-d '{"path":"admin/files/d5demo/hello.txt","content":"aGVsbG8gZDUgYXBp","offset":0}'
# 输出:{"msg":"OK"} [200]
再查进度确认:
{"path":"admin/files/d5demo/hello.txt","isDir":false,"fileSize":12,"uploaded":12} [200]
uploaded == fileSize 即完成。上传中传错 offset(服务端要求 offset 严格等于已传字节数)会得到 500;会话已完成后再传块,会话已清理,实测返回:
{"error":"sql: no rows in result set"} [500]
💡 技术拓展:这种「声明总长 + base64 分块 + offset 对账」的设计让上传完全走 JSON over HTTP,不需要
multipart/form-data,天然适配前端 fetch 和反代环境;代价是 base64 有约 33% 体积膨胀,且大文件需自行切块循环。块大小默认 100 KB(下载同规格),由服务端内部约定,不随配置变化。
待传会话管理:GET /v1/fs/uploadings 列出所有未完成会话(实测可见历史残留的三个会话):
{"uploadInfos":[{"realFilePath":"admin/files/srch/neg30.bin","size":30,"uploaded":0},
{"realFilePath":"admin/files/d5demo/pending.bin","size":100,"uploaded":0}]} [200]
放弃某个上传用 DELETE /v1/fs/uploadings?fp=<路径>(实测返回 {"msg":"OK"} [200])。
4.4 下载、元数据与哈希
下载 GET /v1/fs/files?fp=admin/files/d5demo/hello.txt,实测回读内容与上传一致:
hello d5 api
元数据 GET /v1/fs/metadata?fp=...:
{"name":"hello.txt","size":12,"modTime":"2026-11-01T11:53:59.033557506+08:00","isDir":false,"sha1":""} [200]
注意 sha1 字段默认为空——哈希是异步任务,需要显式触发 POST /v1/fs/hashes/sha1(请求体 {"filePath":"..."},返回 {"msg":"OK"} [200]),完成后列目录接口中的 sha1 才有值。
4.5 移动与删除
移动 PATCH /v1/fs/files/move,{"oldPath":"admin/files/d5demo/hello.txt","newPath":"admin/files/d5demo/hello2.txt"}:
{"msg":"OK"} [200]
路径不存在时报 500 {"error":"path not found"}。目标已存在时移动失败(文件名唯一约束)。
复制 PATCH /v1/fs/files/copy——0.11.4 未实现:
{"msg":"Not Implemented"} [501]
删除 DELETE /v1/fs/files?fp=admin/files/d5demo/hello.txt:
{"msg":"OK"} [200]
若用户「已用空间」计数小于被删文件大小,删除会报 500 {"error":"used space can not be negative"}(计数漂移的修复见 4.6 与配置篇排障实录)。
4.6 配额与运维
重算已用空间 PUT /v1/fs/used-space,请求体是字符串化的用户 ID:
curl -s -w ' [%{http_code}]' -b admin.cookie -X PUT http://localhost:8130/v1/fs/used-space \
-H 'Content-Type: application/json' -d '{"userID":"0"}'
# 输出:{"msg":"OK"} [200]
这是异步任务(走后台 worker 扫描用户目录重建计数),片刻后 GET /v1/users/self 里 usedSpace 即恢复正确(实测重算后 "usedSpace":"0")。
后台队列水位 GET /v1/settings/workers/queue-len;重建搜索索引 PUT /v1/fs/reindex。
4.7 搜索
GET /v1/fs/search?k=<关键词>,按名称匹配、多关键词「与」语义(重复 k 参数):
{"results":["admin/files/d5demo/hello.txt"]} [200] # k=hello
{"results":["admin/files/srch"]} # k=srch(目录名匹配)
{"results":[]} [200] # k=f1&k=f2(无同时含两词的路径)
结果上限由配置 fs.searchResultLimit(默认 16)控制。admin 全库可搜,普通用户只搜自己目录。
4.8 分享与公开访问
分享只能针对目录(分享文件会报 400 can not sharing a file)。
创建分享 POST /v1/fs/sharings,{"sharingPath":"admin/files/d5demo"}:
{"msg":"OK"} [200]
列出我的分享 GET /v1/fs/sharings:
{"sharingDirs":["admin/files/d5demo"]} [200]
取分享 ID GET /v1/fs/sharings/ids?fp=admin/files/d5demo:
{"IDs":{"admin/files/d5demo":"e04d51a"}} [200]
这个 e04d51a 就是 shid。匿名侧确认(不带 cookie):
curl -s -w ' [%{http_code}]' 'http://localhost:8130/v2/public/sharings/exist?fp=admin/files/d5demo'
# 输出:{"msg":"OK"} [200]
通过 shid 浏览 GET /v2/public/sharings/dirs?shid=e04d51a:
{"sharingDir":"admin/files/d5demo"} [200]
匿名下载分享目录里的文件:
curl -s -w ' [%{http_code}]' 'http://localhost:8130/v1/fs/files?fp=admin/files/d5demo/hello2.txt'
# 输出:hello d5 api [200]
取消分享 DELETE /v1/fs/sharings?fp=admin/files/d5demo 后,公开侧立即失效:
{"msg":"NotFound"} [404]
五、v2 接口对照速查
v2 与 v1 能力一一对应,只是按权限域归位(实测验证过映射):
| 需求 | v1 | v2 |
|---|---|---|
| 登录 | POST /v1/users/login | POST /v2/public/login |
| 站点配置读取 | GET /v1/settings/client | GET /v2/public/settings/client |
| 列用户 | GET /v1/users/list | GET /v2/admin/users/list |
| 改配额/强制改密 | PATCH /v1/users/ | PATCH /v2/admin/users/ |
| 查自己信息 | GET /v1/users/self | GET /v2/my/self |
| 个人文件操作 | /v1/fs/* | /v2/my/fs/* |
| 公开分享浏览 | /v1/fs/sharings/exist(匿名受限) | GET /v2/public/sharings/exist?fp=、/v2/public/sharings/dirs?shid= |
实测普通用户调 /v2/my/self 返回自身信息(含配额),新用户配额来自配置默认值(spaceLimit 104857600 = 100 MB)。
六、通用约定与易错点
路径一律相对家目录:所有 fp/dp/path 参数以 admin/files/...、user1/files/... 这样的「用户名/」前缀开头(即家目录相对路径)。直接传系统绝对路径或他人目录,普通用户会 403。
查询参数名是单字母:fp(文件路径)、dp(目录路径)、shid(分享 ID)、k(搜索关键词)。实测搜索参数不是 q——传 ?q=big 会得到 {"error":"empty keyword"}。
字符串化数字:spaceLimit、usedSpace、userID 等字段在 JSON 里必须是字符串(Go 的 ,string tag),传数字报 500 json: invalid use of ,string struct tag。
幂等性差异:建目录重复调用 200;建文件重复调用 400;重复登录直接重发 cookie。
限流:每用户默认 1 秒 1000 次请求(users.limiterCapacity/limiterCyc),超限 429,脚本批量调用时注意加间隔。
七、常见问题与排障实录
Q1:上传块报 500 sql: no rows in result set? 会话已完成(uploaded==fileSize)或已被 DELETE /v1/fs/uploadings 清理后,再 PATCH 就查不到会话记录。先 GET /v1/fs/files/chunks?fp= 确认会话存在,再对账 offset 传块。
Q2:建用户报 500 UNIQUE constraint failed: t_user.name? 用户名已存在。用户名冲突服务端返回 500 而非 409,脚本需按错误串判重。
Q3:改配额报 500 json: invalid use of ,string struct tag? 配额字段要传字符串。"spaceLimit":"104857600" 而不是 "spaceLimit":104857600。
Q4:建用户报 400 name is too short? 用户名长度低于 minUserNameLen(默认 3)或密码低于 minPwdLen(默认 8),按配置调整命名或放宽配置。
Q5:分享文件报 400? 分享目标必须是目录;要分享单文件,先为它建一个目录放进去(或分享其所在目录)。
Q6:复制文件报 501? 0.11.4 的 copy 接口是占位(Not Implemented),用「下载再上传」或「move」替代。
Q7:搜索报 empty keyword? 关键词参数名是 k 且不能为空;多关键词用重复 k 参数表达「与」关系。
Q8:删除文件报 500 used space can not be negative? 已用空间计数漂移(小于文件实际大小)。管理员调 PUT /v1/fs/used-space({"userID":"<id>"})触发异步重算修复;详见配置篇排障实录。
Q9:匿名侧看到 {"msg":"NotFound"} 而非 error? 公开分享接口(/v2/public/)的 404 返回体是 {"msg":"NotFound"},与常规 {"error":...} 不同,脚本解析时兼容两种格式。