QuickShare 0.11.4 API 使用详解

QuickShare 0.11.4 REST API 全量实测:v1/v2 端点对照、登录认证、分块上传、分享公开访问、九类实测错误码与排障实录。

QuickShareQuickShare文件传输文件分享自托管2026/11/01

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/isauthedPOST / GET登录用户
用户/v1/users/(建)、/v1/users/list(/v2/admin/users/list)、/v1/users/pwd/force-set、/v1/fs/used-spacePOST/GET/PATCH/PUTadmin
用户/v1/users/pwd、/v1/users/self、/v1/users/preferencesPATCH/GET/PATCH登录用户
目录/v1/fs/dirs?dp=、/v1/fs/dirs/home、POST /v1/fs/dirsGET/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 能力一一对应,只是按权限域归位(实测验证过映射):

需求v1v2
登录POST /v1/users/loginPOST /v2/public/login
站点配置读取GET /v1/settings/clientGET /v2/public/settings/client
列用户GET /v1/users/listGET /v2/admin/users/list
改配额/强制改密PATCH /v1/users/PATCH /v2/admin/users/
查自己信息GET /v1/users/selfGET /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":...} 不同,脚本解析时兼容两种格式。

八、参考链接