Sharry 1.17.0 API 使用详解
Sharry 1.17.0 REST API 实战:/api/v2 挂载前缀、Sharry-Auth 认证头、四大权限域 44 端点全景、分享全生命周期、tus 断点续传、别名收件与管理域,附九条排障实录。
Sharry 1.17.0 API 使用详解
目标:面向需要脚本化/自动化对接 Sharry 的读者,讲清 Sharry 1.17.0 REST API 的真实调用方式——服务地址与挂载前缀、认证头约定、四大权限域端点全景、分享全生命周期、tus 断点续传、别名收件与管理域,并把实测中踩到的坑(路径前缀、认证头名称、必填字段、单位毫秒等)沉淀为排障实录。全部请求与响应均在 Ubuntu 22.04 + Docker 部署环境实测通过。
一、环境信息
本文 API 实测在以下环境进行:
- 操作系统:Ubuntu 22.04(x86_64)
- Sharry 版本:1.17.0(官方镜像
eikek0/sharry:v1.17.0,构建时间 2026-10-05,/open/info/version实查) - 部署形态:Docker Compose 双服务(Sharry + PostgreSQL 18.6),部署细节见 《Sharry 1.17.0 Docker Compose 部署指南》
- 宿主机端口:8131(映射容器 9090)
- API 挂载前缀:
/api/v2(实测发现,官方 OpenAPI 文档中的路径不含此前缀,详见第十一章排障实录) - 演示账号:
admin(fixed 模块内置管理员,密码q1w2e3r4) - 客户端工具:curl 7.81.0
二、前置条件
- 一套可用的 Sharry 服务:本文所有示例基于 Docker Compose 部署的 1.17.0 实例;如尚未部署,先按本专栏部署篇完成安装。
- 一个可登录的账号:内置
admin,或通过开放注册自建(见 9.1 节)。 - 确认服务可达:
curl -s http://localhost:8131/api/v2/open/info/version
{"version":"1.17.0","builtAtMillis":1791184276879,"builtAtString":"2026-10-05 09:11:16.879+0200","gitCommit":"31154b4ebab6e0c623a9ccb7b54691d7b8d6eb80","gitVersion":"1.17.0"}
- 三个自定义请求头先认识(Sharry 与常见 Bearer Token 风格差异很大,全部实测确认):
| 请求头 | 用途 | 对应端点域 |
|---|---|---|
Sharry-Auth | 用户认证令牌(登录接口返回的 token 原文,不加 Bearer 前缀) | /sec/*、/admin/* |
Sharry-Alias | 别名页上传凭证(别名 ID) | /alias/* |
Sharry-Password | 分享密码(需 percent-encode) | 密码保护的公开分享 |
三、API 总览
3.1 服务地址与挂载前缀
REST API 全部挂在 /api/v2 前缀之下。官方 OpenAPI 3.0 规范文件中写的路径(如 /open/auth/login)是不含前缀的规范路径,实际调用要补上前缀:
实际请求地址 = http://<主机>:<端口>/api/v2 + <规范路径>
两个可用的文档入口(实测):
- 在线交互文档(Swagger UI):
http://<主机>:8131/api/doc——服务器内置 Swagger UI 5.20.0,可直接在浏览器里试调(注意其 "Try it out" 生成的请求同样带/api/v2前缀); - 静态规范文件:https://eikek.github.io/sharry/openapi/sharry-openapi.yml(openapi 3.0,license GPLv3,
info.version1.17.0)。
3.2 四大权限域与端点全景
官方把路由按权限分为四类域(OpenAPI 规范与实测归纳)。1.17.0 共 44 条路径、约 61 个操作,全景如下({id} 为分享 ID、{pid} 为发布 ID、{fid} 为文件 ID):
开放域 /open(无需认证,10 条路径)
| 方法 | 路径 | 用途 |
|---|---|---|
| GET | /open/info/version | 版本与构建信息 |
| GET | /open/info/appconfig | 前端配置(注册模式、大小上限等) |
| POST | /open/auth/login | 账号密码登录换取 token |
| GET | /open/auth/oauth/{id} | OAuth2 提供方跳转 |
| POST | /open/auth/oauth/{id}/resume | OAuth2 回调续接 |
| POST | /open/auth/proxy | 反向代理头认证 |
| POST | /open/signup/register | 开放注册新账号 |
| GET | /open/share/{pid} | 匿名读取已发布分享详情 |
| GET | /open/share/{pid}/file/{fid} | 匿名下载单个文件 |
| GET | /open/share/{pid}/zip | 匿名下载整包 ZIP |
认证域 /sec(需 Sharry-Auth 头,25 条路径)
| 方法 | 路径 | 用途 |
|---|---|---|
| POST | /sec/auth/session | 续期会话(换新 token) |
| POST | /sec/auth/logout | 登出(见 11.6 陷阱) |
| GET, POST, DELETE | /sec/settings/email | 当前账号邮箱查询/设置/删除 |
| GET, POST | /sec/settings/password | 当前账号改密 |
| GET, POST | /sec/alias | 别名列表 / 创建别名 |
| GET, POST, DELETE | /sec/alias/{id} | 别名详情 / 修改 / 删除 |
| GET | /sec/alias-member | 可加为别名成员的账号搜索 |
| POST | /sec/upload | multipart 一步建分享并传文件 |
| POST | /sec/upload/new | 建空分享(JSON 元数据) |
| POST | /sec/upload/{id}/files/add | 向已有分享追加文件 |
| OPTIONS, POST | /sec/upload/{id}/files/tus | tus 协议探测 / 创建上传会话 |
| PATCH, HEAD | /sec/upload/{id}/files/tus/{fid} | tus 分片写入 / 进度查询 |
| GET | /sec/share/search | 搜索自己的分享(分页) |
| GET, DELETE | /sec/share/{id} | 分享详情 / 删除分享 |
| POST, DELETE | /sec/share/{id}/name | 设置 / 清除分享名 |
| POST | /sec/share/{id}/validity | 修改有效期 |
| POST | /sec/share/{id}/description | 修改描述 |
| POST | /sec/share/{id}/maxviews | 修改最大查看次数 |
| POST, DELETE | /sec/share/{id}/password | 设置 / 移除密码 |
| POST, DELETE | /sec/share/{id}/publish | 发布 / 下线 |
| GET, DELETE | /sec/share/{id}/file/{fid} | 下载 / 删除单个文件 |
| GET | /sec/share/{id}/zip | 所有者打包下载 |
| GET | /sec/mail/template/share/{id} | 预览「分享就绪」邮件模板 |
| GET | /sec/mail/template/alias/{aid} | 预览别名页链接邮件模板 |
| POST | /sec/mail/send | 发送通知邮件 |
别名域 /alias(需 Sharry-Alias 头,6 条路径)
/alias/upload、/alias/upload/new、/alias/upload/{id}/files/add、/alias/upload/{id}/files/tus、/alias/upload/{id}/files/tus/{fid} 五条路径在规范中以 $ref 方式完全镜像同名 /sec/upload* 端点(方法一致),区别仅是认证方式从 Sharry-Auth 换成 Sharry-Alias;另有 POST /alias/mail/notify/{id} 供匿名上传者请求邮件通知。
管理域 /admin(需 Sharry-Auth 头且账号为管理员,3 条路径)
| 方法 | 路径 | 用途 |
|---|---|---|
| POST | /admin/signup/newinvite | 生成注册邀请码(invite 模式) |
| GET, POST | /admin/account | 账号列表 / 创建账号 |
| GET, POST, DELETE | /admin/account/{accountId} | 账号详情 / 修改 / 删除 |
3.3 通用请求与响应约定
- 请求体:一律
application/json(文件上传用multipart/form-data);JSON 字段缺失或拼错返回 400,报文为Internal Error: Invalid message body: Could not decode JSON: ...(原文照录,见 11.3); - 操作结果:多数写操作返回
BasicResult/IdResult结构——{"success":布尔,"message":"...","id":"..."},id仅在 success 为 true 时有效; - 时间字段:全部为 Unix 毫秒时间戳(如
created:1793502793912); - 时长字段:
validity、maxValidity等一律为毫秒整数(如 7 天 =604800000),见 11.5; - 大小字段:字节整数(
maxSize:1610612736即 1.5 GiB)。
💡 技术拓展:
/sec/share/{id}返回里的publishInfo.id与路径参数{pid}是同一个东西——分享创建后的 ID({id})与发布后的公开 ID({pid})是两套标识。公开访问域只认{pid},管理域只认{id};reuseId参数传true时重复发布会沿用原{pid}(下线再上线链接不变),传false则换新链接。
四、认证与首次调用
4.1 登录获取令牌
POST /api/v2/open/auth/login,请求体字段为 account 与 password:
curl -s -X POST http://localhost:8131/api/v2/open/auth/login \
-H 'Content-Type: application/json' \
-d '{"account":"admin","password":"q1w2e3r4"}'
{"id":"G3xsRFTpFqK-pDbvZ8HdWsP-cdR4sooakgi-RN2CRvjCaHq","user":"admin","admin":true,"success":true,"message":"Login successful","token":"1793502747122-RzN4c1JGVHBGcUstcERidlo4SGRXc1AtY2RSNHNvb2FrZ2ktUk4yQ1J2akNhSHEvYWRtaW4vdHJ1ZQ==-$2a$10$CWLjrfvTL9xEfxhv1pAE6.-ygfRuUZQBCNU/Vj2lQN32hWpFfc=","validMs":480000}
关键字段:token 是后续所有认证请求的凭证原文;validMs:480000 表示令牌 480 秒(8 分钟)后过期——对应配置 backend.auth.session-valid = "8 minutes";admin:true 标识管理员。
💡 技术拓展:token 是「时间戳-负载-签名」三段式,中段是 Base64:把上面 token 的中段解码得到
G3xsRFTpFqK-pDbvZ8HdWsP-cdR4sooakgi-RN2CRvjCaHq/admin/true——即「账号 ID/用户名/是否管理员」。它不是 JWT(无 header.payload.sig 三段 JSON 结构),而是服务端用server-secretHMAC 签名的自定义格式,因此改密码不会使已签发 token 失效,只能等过期。
4.2 会话续期与登出
续期(拿旧 token 换新 token,重新计时 8 分钟):
curl -s -X POST http://localhost:8131/api/v2/sec/auth/session \
-H "Sharry-Auth: $TOKEN"
{"id":"G3xsRFTpFqK-pDbvZ8HdWsP-cdR4sooakgi-RN2CRvjCaHq","user":"admin","admin":true,"success":true,"message":"Login successful","token":"1793502771807-RzN4c1JGVHBGcUstcERidlo4SGRXc1AtY2RSNHNvb2FrZ2ktUk4yQ1J2akNhSHEvYWRtaW4vdHJ1ZQ==-$2a$10$t7gW0.cDh4NF76EDXxRRcO-9cYvHuhXzrDewElA5/rsCWcXMiM=","validMs":480000}
登出:
curl -s -o /dev/null -w '%{http_code}\n' -X POST http://localhost:8131/api/v2/sec/auth/logout -H "Sharry-Auth: $TOKEN2"
200
脚本轮询场景的正确姿势:每收到 401 就重新登录,或在每次调用前用 /sec/auth/session 换新 token(Web 客户端按 auth-renewal = 4 minutes 周期自动续期)。
4.3 认证头实战:Bearer 为什么不行
Sharry 的认证头叫 Sharry-Auth,值就是 token 原文。若按 REST 惯例用 Authorization: Bearer,服务端会当未认证处理:
curl -s -X POST http://localhost:8131/api/v2/sec/auth/session \
-H "Authorization: Bearer $TOKEN"
Authentication failed.
(HTTP 状态码 401,响应体原文如上。)换成正确的头即成功:
curl -s -X POST http://localhost:8131/api/v2/sec/auth/session \
-H "Sharry-Auth: $TOKEN"
{"id":"G3xsRFTpFqK-pDbvZ8HdWsP-cdR4sooakgi-RN2CRvjCaHq","user":"admin","admin":true,"success":true,"message":"Login successful","token":"...","validMs":480000}
4.4 服务信息端点
GET /api/v2/open/info/appconfig 返回服务器生效配置的只读视图(写脚本前先读它,能省去硬编码):
curl -s http://localhost:8131/api/v2/open/info/appconfig | head -c 800
{"appName":"Sharry","baseUrl":"","logoUrl":"/app/assets/sharry-webapp/1.17.0/img/logo.png","logoUrlDark":"/app/assets/sharry-webapp/1.17.0/img/logo-dark.png","iconUrl":"/app/assets/sharry-webapp/1.17.0/img/icon.svg","iconUrlDark":"/app/assets/sharry-webapp/1.17.0/img/icon-dark.svg","footerText":"","footerVisible":true,"signupMode":"open","oauthConfig":[],"chunkSize":10485760,"retryDelays":[0,3000,6000,12000,24000,48000],"maxValidity":2592000000,"maxSize":1610612736,"mailEnabled":false,"welcomeMessage":"","defaultLanguage":"gb","authRefreshTime":240000,"initialPage":"home","oauthOnly":false,"aliasMemberEnabled":true,"defaultValidity":604800000,"initialTheme":"light","oauthAutoRedirect":true,"proxyAuthEnabled":false,"proxyOnly":false,"hideLoginForm":false,"zipMaxSize":1610612736,"defaultTime
(超过 800 字节部分截断,完整响应含 maxPageSize:100、defaultTimezone 等字段。)脚本常取的字段:signupMode(注册模式)、maxSize(单分享大小上限 1.5 GiB)、maxValidity(有效期上限 30 天 = 2592000000 毫秒)、chunkSize(tus 分块建议 10 MiB)。
五、分享管理域(/sec/share 与 /sec/upload)
5.1 创建分享与必填陷阱
POST /api/v2/sec/upload/new 创建一个空分享,请求体是 ShareProperties 结构。validity 与 maxViews 是必填字段,只传其一会被拒绝(实测):
curl -s -X POST http://localhost:8131/api/v2/sec/upload/new \
-H "Sharry-Auth: $TOKEN" -H 'Content-Type: application/json' \
-d '{"validity": 604800000}'
Internal Error: Invalid message body: Could not decode JSON: {
"validity" : 604800000
}
补全 maxViews 后成功:
curl -s -X POST http://localhost:8131/api/v2/sec/upload/new \
-H "Sharry-Auth: $TOKEN" -H 'Content-Type: application/json' \
-d '{"name":"api-demo","validity":604800000,"maxViews":10}'
{"success":true,"message":"Share created.","id":"AnKj8P4WYPJ-bsxwxmYQMAW-vpv7HTknBpr-P4WgUg7k71c"}
ShareProperties 全部字段:name(可选,分享名)、validity(必填,毫秒)、maxViews(必填,最大查看次数)、description(可选,Markdown 文本)、password(可选,创建时直接带密码)。
5.2 multipart 上传文件
向已有分享追加文件用 POST /api/v2/sec/upload/{id}/files/add,multipart 部件名任意(规范声明全部部件按文件处理):
echo 'sharry api test file content' > api-note.txt
curl -s -X POST "http://localhost:8131/api/v2/sec/upload/$SID/files/add" \
-H "Sharry-Auth: $TOKEN" -F "file=@api-note.txt"
{"success":true,"message":"File(s) added"}
另有一步到位的 POST /api/v2/sec/upload(multipart):除名为 meta 的部件按 JSON 元数据解析外,其余部件全部作为文件入库,适合「建分享 + 传文件」一次完成。
5.3 发布、下线与删除(完整生命周期)
发布(reuseId:true 表示下线后重新发布沿用同一公开链接):
curl -s -X POST "http://localhost:8131/api/v2/sec/share/$SID/publish" \
-H "Sharry-Auth: $TOKEN" -H 'Content-Type: application/json' \
-d '{"reuseId": true}'
{"success":true,"message":"Share published."}
查看详情(注意 publishInfo.id 就是公开访问用的 {pid}):
curl -s "http://localhost:8131/api/v2/sec/share/$SID" -H "Sharry-Auth: $TOKEN"
{"id":"AnKj8P4WYPJ-bsxwxmYQMAW-vpv7HTknBpr-P4WgUg7k71c","name":"api-demo","aliasId":null,"aliasName":null,"validity":604800000,"maxViews":10,"password":false,"descriptionRaw":null,"description":null,"created":1793502793912,"publishInfo":{"id":"Awzs1vkjsm2-rqiykNG4dGm-CmTorBc4qG1-D5UsynFCY16","enabled":true,"views":0,"publishDate":1793502800841,"publishUntil":1794107600841,"expired":false,"lastAccess":null},"files":[{"id":"8BmFHJPU9Tj-JBRoqjy5oEB-XTXVyATSVp6-Y4skL3ZCRP","filename":"api-note.txt","size":29,"mimetype":"text/plain","checksum":"","storedSize":29,"created":1793502800812}]}
下线(DELETE 方法,不是 POST):
curl -s -X DELETE "http://localhost:8131/api/v2/sec/share/$SID/publish" \
-H "Sharry-Auth: $TOKEN"
{"success":true,"message":"Share unpublished."}
下线后详情里 publishInfo.enabled 变为 false,views 保留(实测 {"id":"Awzs1vkjsm2-...","enabled":false,"views":2,...}),公开链接立即 404,重新 publish(reuseId:true)可复活原链接。
删除分享(不可恢复):
curl -s -X DELETE "http://localhost:8131/api/v2/sec/share/$SID" -H "Sharry-Auth: $TOKEN"
{"success":true,"message":"Share deleted."}
删除后再查详情返回 404:
curl -s -o /dev/null -w '%{http_code}\n' \
"http://localhost:8131/api/v2/sec/share/$SID" -H "Sharry-Auth: $TOKEN"
404
5.4 查询与搜索
GET /api/v2/sec/share/search?q=<关键词>&p=<页码>:
curl -s "http://localhost:8131/api/v2/sec/share/search?q=api&p=1" -H "Sharry-Auth: $TOKEN"
{"items":[{"id":"AnKj8P4WYPJ-bsxwxmYQMAW-vpv7HTknBpr-P4WgUg7k71c","name":"api-demo","aliasInfo":null,"validity":604800000,"maxViews":10,"password":false,"created":1793502793912,"files":1,"size":29,"published":true}]}
列表项是精简版分享元数据(files 为文件数、size 为总字节、published 为发布状态)。分页参数见第十章。
5.5 属性更新(名称/有效期/次数/描述)
四个属性端点都是 POST(不是 PUT,PUT 会 404,见 11.4),请求体统一为 {"value": ...}:
curl -s -X POST "http://localhost:8131/api/v2/sec/share/$SID/name" \
-H "Sharry-Auth: $TOKEN" -H 'Content-Type: application/json' \
-d '{"value":"api-demo-renamed"}'
{"success":true,"message":"Name updated."}
curl -s -X POST "http://localhost:8131/api/v2/sec/share/$SID/maxviews" \
-H "Sharry-Auth: $TOKEN" -H 'Content-Type: application/json' \
-d '{"value":25}'
{"success":true,"message":"Max. views updated."}
curl -s -X POST "http://localhost:8131/api/v2/sec/share/$SID/validity" \
-H "Sharry-Auth: $TOKEN" -H 'Content-Type: application/json' \
-d '{"value":1209600000}'
{"success":true,"message":"Validity updated."}
curl -s -X POST "http://localhost:8131/api/v2/sec/share/$SID/description" \
-H "Sharry-Auth: $TOKEN" -H 'Content-Type: application/json' \
-d '{"value":"# 更新后的描述\n\n支持 **Markdown**。"}'
{"success":true,"message":"Description updated."}
其中 name 与 password 另有 DELETE 方法用于清除(validity/maxviews/description 没有清除语义,只能改值)。
5.6 分享密码的设置与移除
设置密码(POST /api/v2/sec/share/{id}/password,体为 SingleString):
curl -s -X POST "http://localhost:8131/api/v2/sec/share/$SID/password" \
-H "Sharry-Auth: $TOKEN" -H 'Content-Type: application/json' \
-d '{"value":"sharepass123"}'
{"success":true,"message":"Password updated."}
移除密码用 DELETE /api/v2/sec/share/{id}/password。注意创建分享时若在 ShareProperties 里传 "password":""(空字符串),会把分享标记为「有密码」但密码内容为空,匿名访问会一直要密码——不想设密码就省略该字段。
5.7 ZIP 打包下载
所有者视角 GET /api/v2/sec/share/{id}/zip,或公开视角(见 6.2 节后实测)。实测公开视角带密码头下载:
curl -s -o api.zip -w '%{http_code} %{size_download}\n' \
"http://localhost:8131/api/v2/open/share/$PID/zip" \
-H 'Sharry-Password: sharepass123'
200 843
file api.zip
api.zip: Zip archive data, at least v2.0 to extract, compression method=deflate
ZIP 打包走流式压缩,受 zip-max-size 配置限制(实测实例为 1.5 GiB)。
六、公开访问域(/open/share)
6.1 匿名访问详情与文件
已发布分享无需任何认证即可读取(密码分享除外):
curl -s "http://localhost:8131/api/v2/open/share/$PID"
{"id":"AnKj8P4WYPJ-bsxwxmYQMAW-vpv7HTknBpr-P4WgUg7k71c","name":"api-demo","aliasId":null,"aliasName":null,"validity":604800000,"maxViews":10,"password":false,"descriptionRaw":null,"description":null,"created":1793502793912,"publishInfo":{"id":"Awzs1vkjsm2-rqiykNG4dGm-CmTorBc4qG1-D5UsynFCY16","enabled":true,"views":0,"publishDate":1793502800841,"publishUntil":1794107600841,"expired":false,"lastAccess":null},"files":[{"id":"8BmFHJPU9Tj-JBRoqjy5oEB-XTXVyATSVp6-Y4skL3ZCRP","filename":"api-note.txt","size":29,"mimetype":"text/plain","checksum":"","storedSize":29,"created":1793502800812}]}
匿名下载单个文件(内容直接回显):
curl -s "http://localhost:8131/api/v2/open/share/$PID/file/8BmFHJPU9Tj-JBRoqjy5oEB-XTXVyATSVp6-Y4skL3ZCRP"
sharry api test file content
6.2 密码三级控制实测
对设置了密码的分享(上文 sharepass123),匿名访问实测三级响应:
curl -s -o /dev/null -w '%{http_code}\n' "http://localhost:8131/api/v2/open/share/$PID"
401
curl -s -o /dev/null -w '%{http_code}\n' \
"http://localhost:8131/api/v2/open/share/$PID" -H 'Sharry-Password: wrongpass'
403
curl -s -o /dev/null -w '%{http_code}\n' \
"http://localhost:8131/api/v2/open/share/$PID" -H 'Sharry-Password: sharepass123'
200
即:不带密码头 401、密码错误 403、密码正确 200。Sharry-Password 头的值需 percent-encode(含中文/空格/特殊字符的密码先编码再放头里)。同一密码头同样适用于 /open/share/{pid}/file/{fid} 与 /open/share/{pid}/zip。
七、断点续传域(tus 协议)
Sharry 的 Web 界面上传默认走 tus 协议(开放断点续传标准),脚本同样可以使用。
7.1 探测与创建上传会话
先 OPTIONS 探测(可选):OPTIONS /api/v2/sec/upload/{id}/files/tus 返回 Tus-Version: 1.0.0 与 Tus-Extension 支持列表。创建会话用 POST,带 Upload-Length(文件总字节数)与 Upload-Metadata(自定义元数据,键值对用 Base64 编码、空格分隔):
curl -s -i -X POST "http://localhost:8131/api/v2/sec/upload/$SID/files/tus" \
-H "Sharry-Auth: $TOKEN" -H 'Tus-Resumable: 1.0.0' \
-H 'Upload-Length: 512' \
-H 'Upload-Metadata: filename YXBpLWJpZy5iaW4=' | head -8
HTTP/1.1 201 Created
Date: Sun, 01 Nov 2026 03:14:32 GMT
Connection: keep-alive
Content-Length: 0
Tus-Resumable: 1.0.0
Location: http://localhost:9090/api/v2/sec/upload/AnKj8P4WYPJ-bsxwxmYQMAW-vpv7HTknBpr-P4WgUg7k71c/files/tus/HBbGLq4WpUY-46aNBZ1uViQ-equLaAj5mTF-Afyn3UxY8Ci
Location 指向新文件资源(末段即 {fid})。注意:Location 的主机端口来自服务器 base-url 配置(本实例为 http://localhost:9090),从外部调用时应只取其路径部分拼接自己的可达地址(见 11.7)。
7.2 HEAD 查询进度
curl -s -I -X HEAD "$TUS" -H "Sharry-Auth: $TOKEN" -H 'Tus-Resumable: 1.0.0' | grep -Ei 'HTTP|Upload-Offset|Tus'
HTTP/1.1 200 OK
Tus-Resumable: 1.0.0
Upload-Offset: 0
Tus-Max-Size: 1610612736
Upload-Offset 是已接收字节数(断点续传的续传点),Tus-Max-Size 即实例的 max-size(1610612736 字节 = 1.5 GiB)。
7.3 PATCH 分片写入与断点续传
先传前 29 字节:
curl -s -i -X PATCH "$TUS" \
-H "Sharry-Auth: $TOKEN" -H 'Tus-Resumable: 1.0.0' \
-H 'Upload-Offset: 0' -H 'Content-Type: application/offset+octet-stream' \
--data-binary @part1.bin | grep -Ei 'HTTP|Upload-Offset'
HTTP/1.1 204 No Content
Upload-Offset: 29
再次 HEAD 确认进度停在 29:
curl -s -I -X HEAD "$TUS" -H "Sharry-Auth: $TOKEN" -H 'Tus-Resumable: 1.0.0' | grep -Ei 'Upload-Offset'
Upload-Offset: 29
模拟断点恢复:以 Upload-Offset: 29 续传剩余 483 字节:
curl -s -i -X PATCH "$TUS" \
-H "Sharry-Auth: $TOKEN" -H 'Tus-Resumable: 1.0.0' \
-H 'Upload-Offset: 29' -H 'Content-Type: application/offset+octet-stream' \
--data-binary @part2.bin | grep -Ei 'HTTP|Upload-Offset'
HTTP/1.1 204 No Content
Upload-Offset: 512
Upload-Offset 到达 Upload-Length 即传输完成,文件自动落入分享。若客户端传的 Upload-Offset 与服务器进度不符,tus 规范要求返回 409,客户端应先 HEAD 校准。
7.4 容量上限(413)
创建会话时 Upload-Length 超过 Tus-Max-Size 直接拒绝(实测):
curl -s -o /dev/null -w '%{http_code}\n' -X POST \
"http://localhost:8131/api/v2/sec/upload/$SID/files/tus" \
-H "Sharry-Auth: $TOKEN" -H 'Tus-Resumable: 1.0.0' \
-H 'Upload-Length: 2147483648'
413
⚠️ 注意(实测差异):tus 路径在会话创建时就强制
max-size;而 multipart 的/files/add端点不做预检——超限文件在 multipart 路径下可能以非 413 的方式失败或落库异常。脚本化大文件传输务必走 tus 路径,让 413 在第一时间暴露。
八、别名收件域(/alias)
别名(alias)是 Sharry 的「对外收件入口」:把别名 ID 给对方,对方无需账号即可向你上传文件。
8.1 创建别名
POST /api/v2/sec/alias,AliasChange 结构四个必填字段:name、validity(经此别名上传的分享默认有效期,毫秒)、enabled、members(成员账号 ID 数组,可为空):
curl -s -X POST http://localhost:8131/api/v2/sec/alias \
-H "Sharry-Auth: $TOKEN" -H 'Content-Type: application/json' \
-d '{"name":"apialias","validity":604800000,"enabled":true,"members":[]}'
{"success":true,"message":"Alias successfully created.","id":"9YmUsu5agsy-YF9S3injsrm-fvkRaSkPFSX-vhFCwPkzTHw"}
只传 name 会报 decode 错误(实测回显同 11.3 模式)。别名列表:
curl -s http://localhost:8131/api/v2/sec/alias -H "Sharry-Auth: $TOKEN"
{"items":[{"id":"FY31msiqnpa-WaFz96zhNT2-tVY1vLh6BMy-jnw7cX8h5qX","name":"客户投递入口","validity":604800000,"owner":"admin","enabled":true,"members":{"items":[]},"created":1793501785272}]}
(返回的是调用前已有别名;新建别名同结构追加在 items 中。)
8.2 别名收件全链路
以 Sharry-Alias 头替代 Sharry-Auth,即可在 /alias 域完成「建分享 → 传文件」:
curl -s -X POST http://localhost:8131/api/v2/alias/upload/new \
-H "Sharry-Alias: $AID" -H 'Content-Type: application/json' \
-d '{"name":"via-alias","validity":604800000,"maxViews":5}'
{"success":true,"message":"Share created.","id":"xqGXHhxLX1H-cMgYFuHqEc4-ytd8WjWLdhU-1KQphbFsTX"}
curl -s -X POST "http://localhost:8131/api/v2/alias/upload/$ASID/files/add" \
-H "Sharry-Alias: $AID" -F "file=@demo-note.txt"
{"success":true,"message":"File(s) added"}
分享归属别名所有者(admin),所有者搜索即可看到,aliasInfo 标注来源别名:
curl -s "http://localhost:8131/api/v2/sec/share/search?q=via-alias&p=1" -H "Sharry-Auth: $TOKEN"
{"items":[{"id":"xqGXHhxLX1H-cMgYFuHqEc4-ytd8WjWLdhU-1KQphbFsTX","name":"via-alias","aliasInfo":{"id":"9YmUsu5agsy-YF9S3injsrm-fvkRaSkPFSX-vhFCwPkzTHw","name":"apialias"},"validity":604800000,"maxViews":5,"password":false,"created":1793502928966,"files":1,"size":41,"published":null}]}
之后所有者按 5.3 节正常发布即可把匿名投递的文件转成对外分享。匿名上传者还可调 POST /api/v2/alias/mail/notify/{id} 请求邮件通知(需服务器已配置 SMTP,本实例 mailEnabled:false 时该链路不生效)。
九、账号与管理域
9.1 注册与邀请
开放注册(signupMode:"open" 时可用),请求体字段是 login 与 password(不是 name/account,实测确认):
curl -s -X POST http://localhost:8131/api/v2/open/signup/register \
-H 'Content-Type: application/json' \
-d '{"login":"apitester","password":"q1w2e3r4"}'
{"success":true,"message":"Signup successful"}
invite 模式下管理员生成邀请码(POST /api/v2/admin/signup/newinvite,体为 {"password":"<配置文件中的邀请口令>"})。本实例为 open 模式,实测返回:
curl -s -X POST http://localhost:8131/api/v2/admin/signup/newinvite \
-H "Sharry-Auth: $TOKEN" -H 'Content-Type: application/json' \
-d '{"password":"q1w2e3r4"}'
{"success":false,"message":"Signing up is not enabled for invitations.","key":null}
(不传 body 会得到 Internal Error: Malformed message body: Invalid JSON: empty body——空体也算 decode 失败。)
9.2 管理员账号管理
GET /api/v2/admin/account?q=<关键词> 检索账号(支持分页):
curl -s "http://localhost:8131/api/v2/admin/account?q=apitester" -H "Sharry-Auth: $TOKEN"
{"items":[{"id":"AaQ3tPUr73Y-AdaoWx5ramf-Dn6iXnDPdnR-mbDcu21cN6H","login":"apitester","source":"intern","state":"Active","admin":false,"email":null,"loginCount":0,"shares":0,"lastLogin":null,"created":1793502947917}]}
source:"intern" 表示 internal 模块创建(相对于 fixed/oauth 等外部来源)。配套操作:POST /admin/account 建号、POST /admin/account/{accountId} 改状态(Active/Disabled)与权限、DELETE /admin/account/{accountId} 删号。非管理员 token 调用返回 403。
9.3 邮件模板与通知
即便 SMTP 未启用,也可预览模板渲染结果(占位符已替换,实测):
curl -s "http://localhost:8131/api/v2/sec/mail/template/share/$SID" -H "Sharry-Auth: $TOKEN"
{"subject":"Download ready.","body":"Hello,\n\nthere are some files for you to download. Visit this link:\n\nhttp://localhost:9090/app/open/Awzs1vkjsm2-rqiykNG4dGm-CmTorBc4qG1-D5UsynFCY16\n\nThe required password will be sent by other means.\n\nGreetings,\nadmin via Sharry\n"}
邮件正文里的链接同样由 base-url 拼出(本实例为 localhost:9090,生产环境必须把 base-url 配成外部真实地址,详见配置篇)。POST /api/v2/sec/mail/send 按 recipients 数组实际发送,mailEnabled:false 时不可用。
十、分页、过滤与通用约定
- 分页参数:列表类端点统一支持
page(页码,从 1 起)与pageSize(页大小)查询参数(如/sec/share/search、/admin/account);服务器硬上限max-page-size = 100(配置项,超出按 100 截断); - 过滤参数:
q为关键词过滤(分享按名称/描述匹配、账号按登录名匹配、别名按名称匹配); - 响应结构:列表统一为
{"items":[...]}; - 时间戳:
created/publishDate/lastLogin等全部为 Unix 毫秒; - 时长:
validity/maxValidity/session-valid相关回显一律毫秒整数; - ID 体系:账号 ID(
account/{accountId})、分享 ID({id})、发布 ID({pid})、文件 ID({fid})、别名 ID(Sharry-Alias头的值)五套相互独立,均由服务端生成 43 字符左右的随机串,不可自定义; - 编码:
Sharry-Password头需 percent-encode;Upload-Metadata的值需 Base64;其余 JSON 体一律 UTF-8(中文可直接写)。
十一、常见问题(排障实录)
以下全部为本机实测复现的问题与结论。
11.1 调官方文档路径返回 404 "Not found"
现象:按 OpenAPI 规范路径直调,返回 HTTP 404 与纯文本 Not found:
curl -si -X POST http://localhost:8131/open/auth/login \
-H 'Content-Type: application/json' \
-d '{"account":"admin","password":"q1w2e3r4"}' | head -3
HTTP/1.1 404 Not Found
Date: Sun, 01 Nov 2026 03:12:06 GMT
Connection: keep-alive
原因:1.x 的 REST 服务实际挂载在 /api/v2 前缀下(容器日志可见成功请求形如 GET /api/v2/open/info/version),规范文件与 Swagger 页面展示的路径都不含该前缀。解决:所有规范路径前补 /api/v2。
11.2 用 Authorization: Bearer 返回 401 Authentication failed
现象与解决:见 4.3 节实测。认证头名为 Sharry-Auth,值直接放 token,不加 Bearer 前缀;同理别名上传用 Sharry-Alias、密码访问用 Sharry-Password。
11.3 请求体报 Invalid message body: Could not decode JSON
现象(原文照录,缺 maxViews 时):
Internal Error: Invalid message body: Could not decode JSON: {
"validity" : 604800000
}
原因:Circe 解码要求必填字段齐全。高频必填清单:建分享 ShareProperties 需 validity+maxViews;建别名 AliasChange 需 name+validity+enabled+members;发布 {"reuseId":布尔};注册 {"login","password"};邀请码 {"password":...};属性更新统一 {"value":...}。解决:对照本文各节请求体逐字段补全;空 body 也会报 Malformed message body: Invalid JSON: empty body。
11.4 属性更新用 PUT 返回 404
现象:PUT /api/v2/sec/share/{id}/name 等 → Not found。原因:这些端点只注册了 POST(清除用 DELETE),没有 PUT 路由。解决:一律改 POST。
11.5 validity 传「7 days」或秒数导致行为异常
现象:ShareProperties.validity 是 integer format duration——传字符串直接 decode 错;传秒数(如 604800)会被解释为 604800 毫秒(约 10 分钟),实测创建后详情回显 validity:604800。解决:统一传毫秒,7 天 = 604800000,且不得超过 appconfig 里的 maxValidity(本实例 2592000000)。
11.6 登出后 token 仍然有效
现象:POST /sec/auth/logout 返回 200(空响应体),但同一 token 再调 /sec/auth/session 依旧 200。原因:token 是无状态签名串,登出只对 Web 客户端语义有效(前端丢弃),服务端没有黑名单。解决:脚本侧把「登出」视为清理动作而非权限回收;确需权限回收时改 server-secret 重启(全部 token 立即失效)或禁用账号。
11.7 tus 的 Location 指向 localhost:9090 不可达
现象:创建 tus 会话返回 Location: http://localhost:9090/api/v2/...,外部机器直接 PUT/PATCH 该地址失败。原因:Location 由 base-url 配置拼接(本实例容器内默认 http://localhost:9090)。解决:外部调用取 Location 的路径部分(/api/v2/sec/upload/.../tus/{fid})拼自己的可达地址;根治则把 base-url 配成外部真实地址(Docker Compose 部署篇已演示 base-url = "http://localhost:8131")。
11.8 multipart 传大文件不报 413
现象:/files/add 传超过 max-size 的文件没有 413;tus 路径创建会话则立刻 413。结论:容量预检只在 tus 路径实现。解决:脚本大文件传输一律走 tus(7.4 节),并以 Tus-Max-Size 响应头为上限依据。
11.9 表名与数据库直查的坑
现象:PostgreSQL 直查 pg_dump -t account 报 no matching tables。原因:Sharry 数据库表名带尾下划线(account_ 等 10 张表)。解决:先 \dt 确认表名再操作;日常备份用 pg_dump 整库导出(部署篇持久化章有完整命令)。
十二、参考链接
- 本专栏部署篇:《Sharry 1.17.0 Docker Compose 部署指南》(compose 编排、持久化、tus 上传与 413 实测)
- 本专栏配置篇:《Sharry 1.17.0 配置参数详解》(HOCON 参数、环境变量覆盖规则、
base-url/max-size/认证模块详解) - 官方 REST API 文档(含 Swagger UI):https://eikek.github.io/sharry/restapi.html
- OpenAPI 3.0 静态规范:https://eikek.github.io/sharry/openapi/sharry-openapi.yml
- 服务器内置 Swagger UI(部署后可达):
http://<主机>:<端口>/api/doc - tus 协议规范:https://tus.io/protocols/resumable-upload
- 源码仓库:https://github.com/eikek/sharry