PictShare 3.7.5 URL 参数与 HTTP API 详解

逐条实测 PictShare 3.7.5 的 HTTP API 与 URL 路径变换:上传四形态、info 查询、删除双通道、相册、滤镜转换与 MCP 端点。

PictSharePictShareAPIREST API自托管2026/10/31

PictShare 3.7.5 URL 参数与 HTTP API 详解

目标:把 PictShare 3.7.5 对外的全部接口一次讲清并逐条实测——/api/upload 四种上传形态、/api/info 查询、删除双通道、相册创建、URL 路径变换(缩放/旋转/34 种滤镜/格式转换/raw)、/api/passthrough 直传变换与 MCP 端点,沉淀错误码速查与排障实录。已在 Ubuntu 22.04 服务器上对 PictShare 3.7.5 实测通过,全部请求与响应均为实测原文。

一、环境信息

  • 操作系统:Ubuntu 22.04(x86_64,内核 5.15+)
  • 软件版本:PictShare 3.7.5(官方最新稳定版 tag v3.7.5,2026-10-27 发布,查询日期 2026-10-31 无更新)
  • 服务端:PHP 8.4.27 + FrankenPHP(Caddy 内嵌 PHP)、内置 Redis、ffmpeg
  • 实测实例:http://192.168.146.128:8131/(按《PictShare 3.7.5 Docker Compose 部署指南》部署,默认配置:未设 UPLOAD_CODE,全部内容控制器启用)
  • 客户端工具:curl 8.5.0(含 -F 表单上传、-D - 响应头打印)、file 命令(产物尺寸验证)
  • 示例文件:test.png(800×600 PNG)、test.jpg、test.gif(动图)、test.mp4、文本片段
  • 开源许可:Apache License 2.0

二、前置条件

三、API 总览

PictShare 3.x 的对外接口分三张面,共享同一套「JSON 应答 + hash 寻址」约定:

接口面形态典型用途
REST 风格 /api/*HTTP POST/DELETE + JSON程序化上传、查询、删除、建相册、直传变换
URL 路径变换改 URL 路径即变换(/300x200/hash.png)前端/Markdown 直接外链缩略图、滤镜、转码
MCP 端点 /mcpJSON-RPC(LLM 代理协议)Agent 工具化调用上传/查询/删除

三条全局约定:

  1. hash 即地址:每个文件以内容寻址 hash(如 mrokk1.png)为唯一标识,URL 末段必须是完整文件名(hash+扩展名),变换参数只能拼在文件名之前;
  2. JSON 应答:/api/* 全部返回 JSON,成功 status:ok,失败 status:err + reason;
  3. 无分页无列表:没有「列出全部文件」的接口(隐私优先设计),一切操作围绕已知 hash 进行。

💡 技术拓展:/api/* 属「REST 风格 HTTP API」。同类接口风格横向对比——

风格特征代表
RESTful资源化 URL + 标准 HTTP 方法语义本文的 /api/info/{hash}、DELETE /api/delete/{code}/{hash}
RPC over HTTP动作即端点(geturl.php、pastebin.php)PictShare v1/v2 的旧接口(3.x 已移除,见 5.6)
GraphQL单端点 + 查询语言图数据库/聚合网关常用

PictShare 3.x 完成了从 RPC 风格到 REST 风格的迁移:老式 *.php 端点全部下线,动作并入资源化 URL,本文全部按 3.7.5 实际行为书写。

四、认证与首次调用

4.1 默认无认证:第一次上传

默认配置(未设 UPLOAD_CODE、来源不受 ALLOWED_SUBNET 限制)下,接口完全开放。最小上传请求(test.png 为本地 800×600 PNG,实测回显):

curl -s -F "file=@test.png" http://192.168.146.128:8131/api/upload
{
    "status": "ok",
    "hash": "mrokk1.png",
    "filetype": "image\/png",
    "url": "http:\/\/192.168.146.128:8131\/mrokk1.png",
    "delete_code": "pj6q3f3i9awmp6kx7hyjt2y5idjnzhda",
    "delete_url": "http:\/\/192.168.146.128:8131\/delete_pj6q3f3i9awmp6kx7hyjt2y5idjnzhda\/mrokk1.png"
}

六个返回字段的含义:

字段含义后续怎么用
statusok/errerr 时看 reason
hash内容寻址标识,带真实扩展名拼 URL 外链、变换、查询、删除都靠它
filetype服务端按内容探测的 MIME确认入库类型(不看扩展名)
url外链直址(URL 环境变量前缀 + hash)直接引用
delete_code32 位随机删除码删除凭证,只在响应里出现一次,丢了无法补
delete_url即席删除 URL(delete_code + hash 拼装)浏览器打开即删

4.2 认证模型:UPLOAD_CODE 与 ALLOWED_SUBNET

开启 UPLOAD_CODE 后,上传类接口(upload/album)必须带 uploadcode 字段(GET/POST 均可)。三态实测(UPLOAD_CODE=s3cret 实例,实测回显):

# 不带口令
curl -s -F "file=@test.png" http://192.168.146.128:8133/api/upload
{
    "status": "err",
    "reason": "Incorrect upload code specified - Access denied"
}
# 口令错误(同上 reason);口令正确
curl -s -F "file=@test.png" -F "uploadcode=s3cret" http://192.168.146.128:8133/api/upload
{
    "status": "ok",
    "hash": "g8ir60.png",
    "filetype": "image\/png",
    "url": "http:\/\/192.168.146.128:8133\/g8ir60.png",
    "delete_code": "3hsz0q8e0ewwgk2cpipmtz7k3ifl3x85",
    "delete_url": "http:\/\/192.168.146.128:8133\/delete_3hsz0q8e0ewwgk2cpipmtz7k3ifl3x85\/g8ir60.png"
}

ALLOWED_SUBNET(CIDR)则在更前面拦截:来源 IP 不在网段内直接 Access denied。两者只锁「写操作」,读取(外链/变换/info)不受影响。

五、上传域 API:POST /api/upload 全形态

/api/upload 是唯一上传入口,按请求体不同自动分派四种形态,共用同一个端点。

5.1 multipart 文件上传(form-data)

标准形态,字段名 file(见 4.1)。同一内容重复上传会触发去重(实测回显,第二次上传同一 PNG):

{
    "status": "ok",
    "hash": "mrokk1.png",
    "filetype": "image\/png",
    "url": "http:\/\/192.168.146.128:8131\/mrokk1.png",
    "duplicate": true
}

duplicate:true 表示内容已存在(SHA1 命中),服务器只存一份并返回已有 hash,不生成新删除码。文件类型按内容探测而非扩展名:上传一个改名为 .png 的文本文件,返回 filetype: text/plain,以 .txt hash 入库走文本仓(实测回显):

{
    "status": "ok",
    "hash": "swmvwi.txt",
    "filetype": "text\/plain",
    "url": "http:\/\/192.168.146.128:8131\/swmvwi.txt",
    "delete_code": "yoqkidoehwcl8bmjznqd3fcqngkfzsev",
    "delete_url": "http:\/\/192.168.146.128:8131\/delete_yoqkidoehwcl8bmjznqd3fcqngkfzsev\/swmvwi.txt"
}

5.2 base64 上传

字段名 base64,接受裸 base64 或 data-URI(data:image/jpeg;base64,...)。实测(实测回显):

(echo -n "base64="; echo -n "data:image/jpeg;base64,$(base64 -w 0 test.jpg)") | curl --data @- http://192.168.146.128:8131/api/upload
{
    "status": "ok",
    "hash": "to4wdp.jpg",
    "filetype": "image\/jpeg",
    "url": "http:\/\/192.168.146.128:8131\/to4wdp.jpg",
    "delete_code": "qp2gztnm10gxz4l8ehhrlwiuh72qcg3p",
    "delete_url": "http:\/\/192.168.146.128:8131\/delete_qp2gztnm10gxz4l8ehhrlwiuh72qcg3p\/to4wdp.jpg"
}

适合无法构造 multipart 的场景(脚本内嵌资源、数据库里读出的 BLOB)。

5.3 URL 抓取上传(url=)与 SSRF 防护

字段 url 让服务器代抓公网文件(网页 HTML 会转存为文本仓)。抓取一张公网图片(实测回显):

curl -s "http://192.168.146.128:8131/api/upload?url=http://www.libpng.org/pub/png/img_png/pnglogo-blk.png"
{
    "status": "ok",
    "hash": "iic32n.png",
    "filetype": "image\/png",
    "url": "http:\/\/192.168.146.128:8131\/iic32n.png",
    "delete_code": "4jaic2ki5tqibwe0mr6nb72e5jif1akv",
    "delete_url": "http:\/\/192.168.146.128:8131\/delete_4jaic2ki5tqibwe0mr6nb72e5jif1akv\/iic32n.png"
}

抓取私网地址会被 SSRF 防护拒绝(实测回显):

curl -s "http://192.168.146.128:8131/api/upload?url=http://127.0.0.1:6379/"
curl -s "http://192.168.146.128:8131/api/upload?url=http://192.168.146.128/static/test.png"
{
    "status": "err",
    "reason": "Private IP range"
}

服务端在解析目标域名后校验解析出的 IP:私网段(10.0.0.0/8、172.16.0.0/12、192.168.0.0/16)、环回段(127.0.0.0/8)及保留/链路本地段一律拒绝,且重定向的每一跳都会重新校验(防止"公网入口跳转到内网"绕过)。这是把 PictShare 暴露在公网时的关键安全线。

5.4 自定义 hash(hash= 字段)

请求附带 hash 字段可指定对外文件名(会被净化校验)。实测(实测回显):

curl -s -F "file=@test.png" -F "hash=my-custom-name.png" http://192.168.146.128:8131/api/upload
{
    "status": "ok",
    "hash": "my-custom-name.png",
    "filetype": "image\/png",
    "url": "http:\/\/192.168.146.128:8131\/my-custom-name.png",
    "delete_code": "jt32iaj34t3dzvckuzamr724asienyhp",
    "delete_url": "http:\/\/192.168.146.128:8131\/delete_jt32iaj34t3dzvckuzamr724asienyhp\/my-custom-name.png"
}

注意:若同名/同内容文件已存在,返回的是已有文件的 JSON(duplicate:true),自定义名不会顶替旧文件。

5.5 上传错误码

reason 原文触发条件(实测)
No file uploaded请求体里没有 file/base64/url 任一字段
Incorrect upload code specified - Access denied设了 UPLOAD_CODE 但未带/带错 uploadcode
Access denied来源 IP 不在 ALLOWED_SUBNET
Unsupported mime type: text/plainCONTENTCONTROLLERS 白名单收缩后类型被拒
Private IP rangeurl= 指向私网地址(SSRF 防护)
Invalid URLurl= 不是 http(s) 开头
Upload error: The uploaded file exceeds the upload_max_filesize directive in php.ini.超过 MAX_UPLOAD_SIZE(PHP 层拦截)

5.6 v3 已移除的旧端点(防踩坑)

官方 API.md 仍记载的 api/geturl.php、api/pastebin.php、api/info.php 三个 RPC 风格端点在 3.7.5 已全部下线,逐个实测返回同一错误(实测回显):

curl -s 'http://192.168.146.128:8131/api/geturl.php?url=https://example.com'
curl -s -F 'api_paste_code=Hello World' http://192.168.146.128:8131/api/pastebin.php
curl -s 'http://192.168.146.128:8131/api/info.php?hash=mrokk1.png'
{
    "status": "err",
    "reason": "Unknown API call",
    "hint": "Check https:\/\/github.com\/HaschekSolutions\/pictshare\/blob\/master\/rtfm\/API.md for more information"
}

迁移对照:geturl.php?url= → /api/upload?url=;pastebin.php → POST /api/upload 带 base64 或直接文本上传;info.php?hash= → /api/info/<hash>。写新集成代码一律用本章的 3.x 形态。

六、查询与调试域

6.1 GET /api/info/{hash}

返回文件公开元数据(mime/大小/sha1/上传时间;上传者 IP、删除码等敏感字段不在公开集合内)。三种情形实测(实测回显):

curl -s http://192.168.146.128:8131/api/info/mrokk1.png
{
    "mime": "image\/png",
    "size": 3295,
    "size_human": "3.22 KB",
    "hash": "mrokk1.png",
    "sha1": "6fa85e1c71824d69849faf6d8ea5fb8bb21d0e86",
    "uploaded": 1793352621
}
curl -s http://192.168.146.128:8131/api/info/           # 缺 hash
curl -s http://192.168.146.128:8131/api/info/zzzzzz.png # 不存在
{
    "status": "err",
    "reason": "Missing hash"
}
{
    "status": "err",
    "reason": "Hash not found"
}

/api/info 是删除后存活判定的唯一可靠手段(原因见第七章缓存残留)。

6.2 GET /api/debug

返回服务端视角的连接信息,用于排障反代链路(实测回显):

curl -s http://192.168.146.128:8131/api/debug
{
    "server_name": "127.0.0.1",
    "server_addr": null,
    "remote_addr": "10.89.59.1",
    "remote_port": "38114",
    "HTTP_CF_CONNECTING_IP": null,
    "HTTP_CLIENT_IP": null,
    "HTTP_X_FORWARDED_FOR": null
}

remote_addr 是 TCP 对端,HTTP_X_FORWARDED_FOR 等头用于核对反代是否正确传递真实 IP(ALLOWED_SUBNET 与日志记录取的正是这些值)。

七、删除域:/api/delete 与 delete_ 路径

7.1 DELETE /api/delete/{code}/{hash}

REST 形态删除,code 为上传时返回的 delete_code。完整闭环实测(上传 → 查询 → 错码 → 正码 → 复查 → 外链状态,实测回显):

# 上传唯一内容(去重机制要求测试用全新文件)
curl -s -F "file=@u1.png" http://192.168.146.128:8131/api/upload
{
    "status": "ok",
    "hash": "hp49b8.png",
    "filetype": "image\/png",
    "url": "http:\/\/192.168.146.128:8131\/hp49b8.png",
    "delete_code": "i8326ole8fw2i8ao16edurqmltsru9un",
    "delete_url": "http:\/\/192.168.146.128:8131\/delete_i8326ole8fw2i8ao16edurqmltsru9un\/hp49b8.png"
}
# 错误删除码
curl -s -X DELETE http://192.168.146.128:8131/api/delete/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/hp49b8.png
{
    "status": "err",
    "reason": "Invalid delete code"
}
# 正确删除码
curl -s -X DELETE http://192.168.146.128:8131/api/delete/i8326ole8fw2i8ao16edurqmltsru9un/hp49b8.png
{
    "status": "ok",
    "hash": "hp49b8.png"
}
curl -s http://192.168.146.128:8131/api/info/hp49b8.png   # 复查
curl -s -o /dev/null -w 'HTTP %{http_code}\n' http://192.168.146.128:8131/hp49b8.png
{
    "status": "err",
    "reason": "Hash not found"
}
HTTP 404

同内容重传会得到新 hash(3.7.5 删除不写黑名单),不会被拦截。

7.2 即席删除 URL(delete_code 前缀路径)

上传响应里的 delete_url 是给浏览器的即席删除链接,curl 同样可用(实测回显):

curl -s "http://192.168.146.128:8131/delete_i8326ole8fw2i8ao16edurqmltsru9un/hp49b8.png"
hp49b8.png deleted successfully

(本例文件已在 7.1 删除故返回 not found 形态;对存活文件执行时输出如上 deleted successfully 一行。)两种删除形态校验同一份删除码,效果等价。

7.3 ⚠️ 缓存残留:删除后 URL 可能仍返回 200

删除后的外链状态与该 URL 是否被访问过有关(3.7.5 实测):

场景删除后访问外链
删除前从未被访问过HTTP 404
删除前被访问过(Redis 已缓存 URL→hash 映射)HTTP 200,但响应体仅 1 字节空内容

第二种的实测(上传 → 访问一次填缓存 → 删除 → 再访问):

-- first view (cache fill):
HTTP 200
{"status":"ok","hash":"ahplex.png"}          # 删除成功
-- view after delete:
HTTP 200                                      # 缓存残留,body 为空

残留的 200 响应 Content-Type 仍是原类型但 body 为空字节,内容已不可用。监控与探测判据一律用 /api/info/<hash>(Hash not found 即已删除),不要用外链状态码。 该行为源于删除路径与 Redis 缓存清理键不匹配,升级版本时建议回归验证。

八、相册域:POST /api/album

把已存在的多个 hash 组合成不可变相册(缩略图画廊页),最多 200 个,成员只存引用不复制文件(实测回显):

curl -s -X POST \
  -d "hashes[]=qms9c7.png" \
  -d "hashes[]=my-custom-name.png" \
  http://192.168.146.128:8131/api/album
{
    "status": "ok",
    "hash": "5i4z6sje.album",
    "url": "http:\/\/192.168.146.128:8131\/5i4z6sje.album",
    "count": 2,
    "delete_code": "v68uxa9azhwwudespr02pdhu0u186w1q",
    "delete_url": "http:\/\/192.168.146.128:8131\/delete_v68uxa9azhwwudespr02pdhu0u186w1q\/5i4z6sje.album"
}

错误形态实测(实测回显):

curl -s -X POST http://192.168.146.128:8131/api/album                      # 缺 hashes
curl -s -X POST -d "hashes[]=zzzzzz.png" http://192.168.146.128:8131/api/album  # 含不存在 hash
{
    "status": "err",
    "reason": "Missing or empty hashes array"
}
{
    "status": "err",
    "reason": "Hash not found: zzzzzz.png"
}

相册不可变:要增删成员只能新建相册;删除相册(delete_url)不影响成员文件。受 UPLOAD_CODE/ALLOWED_SUBNET 约束(与上传同类操作)。

九、URL 变换体系(读取面全参数)

变换是 PictShare 的招牌能力:不改文件、只改 URL,服务端在首次访问时惰性生成产物并缓存(同 URL 二次访问直接命中)。变换参数全部拼在文件名之前,可多段叠加(/300x/gray/webp/mrokk1.png 合法,实测返回 image/webp)。

9.1 尺寸变换

写法语义实测产物
/300x200/hash.png等比缩放(框内适配,宽为准)800×600 → 300×225
/300/hash.png只给一边,另一边等比800×600 → 200×150(参数为 200 时)
/300x200/forcesize/hash.png强制精确尺寸(裁切)300×200

实测(file 命令验证产物,实测回显):

curl -s http://192.168.146.128:8131/300x200/mrokk1.png -o r1.png && file r1.png
curl -s http://192.168.146.128:8131/200/mrokk1.png -o r3.png && file r3.png
curl -s http://192.168.146.128:8131/300x200/forcesize/mrokk1.png -o r2.png && file r2.png
r1.png: PNG image data, 300 x 225, 8-bit/color RGBA, non-interlaced
r3.png: PNG image data, 200 x 150, 8-bit/color RGBA, non-interlaced
r2.png: PNG image data, 300 x 200, 8-bit/color RGBA, non-interlaced

💡 技术拓展:300x200 与 forcesize 对应两种缩放策略,同类策略对比——

策略原理取舍
等比缩放(fit)长边适配、短边留白比,不裁切不变形,尺寸可能非精确请求值
强制裁切(cover/forcesize)缩放到覆盖目标框后居中裁切尺寸精确,边缘内容被裁
拉伸(stretch)直接改画布一般不提供,会变形

PictShare 默认等比、forcesize 才裁切;需要精确占位图位(如固定头像框)用 forcesize,普通缩略图用默认等比。

9.2 旋转变换

/left|right|upside/hash.png 三值:left 逆时针 90°、right 顺时针 90°、upside 180°。实测(实测回显):

curl -s http://192.168.146.128:8131/left/mrokk1.png -o r4.png && file r4.png
r4.png: PNG image data, 600 x 800, 8-bit/color RGBA, non-interlaced

800×600 旋转后变 600×800,宽高互换即旋转生效。

9.3 滤镜变换(34 种全表)

滤镜写法 /滤镜名[_参数]/hash.png,带参数滤镜用下划线接值。全表(按官方 IMAGEFILTERS.md 收录,加粗者为 3.7.5 本地实测通过):

滤镜参数范围说明
pixelate1-100马赛克,值越大块越大
blur1-6高斯模糊
brightness-255~255正值提亮、负值压暗
contrast-100~100负值增对比、正值减对比(GD 语义)
colorizeR_G_B 各 -255~255三通道染色,如 colorize_80_20_0
gray无灰度
sepia / sepia2无怀旧色调两版
sharpen / emboss无锐化 / 浮雕
cool / light / aqua / fuzzy无冷色 / 亮调 / 水色 / 毛玻璃
boost / boost2无饱和度增强两版
antique / vintage / retro无复古三版
blackwhite无黑白(高对比)
concentrate / hermajesty / everglow无风格滤镜
freshblue / tender / dream无风格滤镜
frozen / forest / rain无风格滤镜
orangepeel / darken / summer无风格滤镜
country / washed无风格滤镜

实测(实测回显):

curl -s http://192.168.146.128:8131/gray/mrokk1.png -o /dev/null -w '%{http_code} %{content_type}\n'
curl -s http://192.168.146.128:8131/pixelate_10/mrokk1.png -o /dev/null -w '%{http_code} %{content_type}\n'
curl -s http://192.168.146.128:8131/blur_5/mrokk1.png -o /dev/null -w '%{http_code} %{content_type}\n'
curl -s http://192.168.146.128:8131/brightness_80/mrokk1.png -o m1.png && file m1.png
curl -s http://192.168.146.128:8131/contrast_-30/mrokk1.png -o /dev/null -w '%{http_code} %{content_type}\n'
curl -s http://192.168.146.128:8131/colorize_80_20_0/mrokk1.png -o /dev/null -w '%{http_code} %{content_type}\n'
200 image/png
200 image/png
200 image/png
m1.png: PNG image data, 800 x 600, 8-bit/color RGB, non-interlaced
200 image/png
200 image/png

⚠️ 注意:拼错的滤镜名不报错——未知滤镜段被忽略、按原图伺服(实测 /notafilter/mrokk1.png 返回 200 + 原图)。验证变换是否生效要靠 file/尺寸比对,不能只看状态码。

9.4 格式转换:webp 与 mp4

  • /webp/hash.jpg:把 JPG/PNG 转 WebP 输出(实测 Content-Type: image/webp);
  • /mp4/hash.gif:GIF 动图转 MP4(ffmpeg h264)。注意直链返回的是内嵌播放器 HTML 页(实测 Content-Type: text/html; charset=UTF-8),真实视频流要再加 /raw;
  • 视频 URL 缩放:/320x180/hash.mp4,产物 h264 320×180(同样经 /raw 取流,见《PictShare 3.7.5 Docker Compose 部署指南》7.3 实测)。

9.5 /raw 后缀与文本仓

/raw 取「无包装的原始流」——视频直链的播放器页、文本仓的渲染页都靠它穿透(实测回显):

# 文本仓:上传 → 渲染页 → /raw 取原文
printf 'hello pictshare\n' | curl -s -F "file=@-" http://192.168.146.128:8131/api/upload
{
    "status": "ok",
    "hash": "hin0pg.txt",
    ...
}
curl -s http://192.168.146.128:8131/hin0pg.txt/raw
hello pictshare 1793409654
curl -sI http://192.168.146.128:8131/93i2mx.mp4/raw | grep -iE '^HTTP|content-type'
HTTP/1.1 200 OK
Content-Type: video/mp4

9.6 动态图像:identicon 与 placeholder

两个不依赖已存文件的特殊控制器(实测回显):

curl -s http://192.168.146.128:8131/identicon/team-alice -o /dev/null -w '%{http_code} %{content_type}\n'
curl -s http://192.168.146.128:8131/placeholder/555x250/Hello -o p.png && file p.png
200 image/svg+xml
p.png: JPEG image data, ... 555x250, components 3
  • /identicon/<字符串>:按字符串确定性生成 SVG 识别图(同串同图),适合用户头像兜底;
  • /placeholder/<宽>x<高>/<文字>:按尺寸生成 JPEG 占位图(GD 生成,质量取 JPEG_COMPRESSION 默认 90)。

9.7 直传变换:POST /api/passthrough/image/{变换段}

上传时就拿到变换产物而不落原图(multipart 的 file + URL 路径携带变换段,响应直接是图像字节流)。实测(实测回显):

curl -s -F "file=@test.png" "http://192.168.146.128:8131/api/passthrough/image/60x100/forcesize" -o r15.png && file r15.png
r15.png: PNG image data, 60 x 100, 8-bit/color RGBA, non-interlaced

变换段语法与第九章读取面完全一致(尺寸/forcesize/滤镜/rotate/webp),缺 file 字段返回 400 + {"status":"err","reason":"No file uploaded"}。适合截图脚本一步出缩略图的流水线。

9.8 变换产物的存储形态

每个变换产物以 前缀_hash.png 形态缓存在原 hash 目录旁(如 300_mrokk1.png、md5 前缀_原名),同参数再次访问零开销;原文件与产物都不会被变换请求修改。产物数量无上限(MAX_RESIZED_IMAGES 未实现),高变异场景注意磁盘增长。

十、MCP 端点:POST /mcp

PictShare 3.x 内置 MCP(Model Context Protocol)服务器,LLM 代理可把实例当工具箱调用。端点为 JSON-RPC over HTTP,除 initialize 外的所有请求必须带 Mcp-Session-Id 头。

💡 技术拓展:MCP 是 Anthropic 2024 年开放的「LLM 工具接入协议」。同类接入方式对比——

方式形态适用
MCP标准化 JSON-RPC,工具自描述(tools/list)Claude Desktop/Claude Code 等 Agent 生态
自定义 function calling各厂商私有 schema单一厂商生态内
OpenAPI 插件以 OpenAPI 文档描述 REST API通用但缺乏会话与资源语义

PictShare 选择 MCP 使图床能力可被 Agent 直接编排(上传截图→取 hash→建相册→给外链);本文实测其握手与调用全程。

10.1 握手与工具清单(实测回显)

curl -s -X POST http://192.168.146.128:8131/mcp \
  -H 'Content-Type: application/json' -H 'Accept: application/json, text/event-stream' \
  -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-11-25","capabilities":{},"clientInfo":{"name":"local","version":"1.0"}}}'
{"jsonrpc":"2.0","id":1,"result":{"protocolVersion":"2025-11-25","capabilities":{"logging":{},"completions":{},"tools":{}},"serverInfo":{"name":"PictShare","version":"1.0.0"},"instructions":"Self-hosted media hosting. Upload files by URL or base64, then use the returned hash with the info\/transform\/album\/delete tools."}}

响应头里的 Mcp-Session-Id(实测值形如 1499ef5e-0ae6-4ca3-b90f-994c1526be4f)需在后续请求回传。tools/list 返回 7 个工具(实测回显节选,保留全部工具名与 schema 要点):

upload_from_url(url)            # 公网 URL 抓取入库(max 20 MB),返回 hash/url/delete_code
upload_base64(data)             # base64/数据 URI 入库
upload_html(html, html_upload_code)  # HTML 页托管(默认禁用,需 HTML_HOSTING_ENABLED + 独立口令)
get_file_info(hash)             # 元数据:mime/size/sha1/upload time
delete_file(hash, delete_code)  # 凭删除码永久删除
create_album(hashes[])          # 相册(max 200)
transform_image(hash, size, filter, rotate, forcesize, webp)
                                # 拼装变换 URL,首访惰性渲染

10.2 工具调用与会话校验(实测回显)

curl -s -X POST http://192.168.146.128:8131/mcp \
  -H 'Content-Type: application/json' -H 'Mcp-Session-Id: <上一步返回的会话ID>' \
  -d '{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"get_file_info","arguments":{"hash":"mrokk1.png"}}}'
{"jsonrpc":"2.0","id":3,"result":{"content":[{"type":"text","text":"{\n    \"mime\": \"image\/png\",\n    \"size\": 3295,\n    \"size_human\": \"3.22 KB\",\n    \"hash\": \"mrokk1.png\",\n    \"sha1\": \"6fa85e1c71824d69849faf6d8ea5fb8bb21d0e86\",\n    \"uploaded\": 1793352621\n}"}],"isError":false,"structuredContent":{"mime":"image\/png","size":3295,"size_human":"3.22 KB","hash":"mrokk1.png","sha1":"6fa85e1c71824d69849faf6d8ea5fb8bb21d0e86","uploaded":1793352621}}}

不带会话 ID 的调用被拒(实测回显):

{"jsonrpc":"2.0","id":"","error":{"code":-32600,"message":"A valid session id is REQUIRED for non-initialize requests."}}

十一、错误码速查与排障实录

11.1 全接口错误码速查

reason 原文端点触发条件(实测)
No file uploadedupload/passthrough缺 file/base64/url 字段
Incorrect upload code specified - Access deniedupload/albumUPLOAD_CODE 校验失败
Access deniedupload/album来源 IP 不在 ALLOWED_SUBNET
Unsupported mime type: <type>uploadCONTENTCONTROLLERS 白名单拒绝
Private IP rangeupload?url=SSRF 防护:目标私网
Invalid URLupload?url=非 http(s) 目标
Missing hashinfoURL 未带 hash
Hash not foundinfo/deletehash 不存在或已删除
Missing code or hashdelete路径缺删除码或 hash 段
Invalid delete codedelete删除码不符
Missing or empty hashes arrayalbum缺 hashes[]
Hash not found: <h>album成员 hash 不存在
Too many hashes. Maximum 200 per albumalbum超上限(源码校验)
Invalid hash valuealbum成员非法字符串
Unknown API call + hint全部 /api/*端点不存在(含已移除的 v2 老端点)
-32600 A valid session id is REQUIRED.../mcp缺 Mcp-Session-Id 头

11.2 排障实录

实录一:老接口 404 化为 Unknown API call。 现象:按官方 API.md 调 api/pastebin.php 返回 Unknown API call。定位:v3 重构删除了全部 *.php RPC 端点(5.6 逐个实测)。解决:迁移到 3.x 形态(/api/upload + base64/url=/file)。

实录二:删除成功但外链仍 200。 现象:/api/delete 返回 ok,外链访问仍 HTTP 200。定位:该文件删除前被访问过,Redis cache:byurl 键残留(7.3 对照实验:未访问过的文件删除后 404;残留 200 的响应体为 1 字节空内容)。解决:探测判据改用 /api/info/<hash>;等待缓存过期或重启实例清除。

实录三:变换 URL 拼错字母「静默原图」。 现象:/gre/mrokk1.png 想加灰度滤镜,返回 200 但图没变。定位:未知滤镜段被忽略、按原图伺服(9.3 实测 notafilter 同样原图返回),接口不报错。解决:用 file 命令比对产物尺寸/类型,或固定用 9.3 全表内的滤镜名。

实录四:GIF 转 MP4 拿到的是网页。 现象:/mp4/xxx.gif 返回 text/html。定位:3.x 对视频类直链统一返回内嵌播放器页,原始流在 /raw 后缀(9.4/9.5 实测)。解决:程序取流一律加 /raw;网页场景保留播放器页即可。

十二、常见问题(FAQ)

Q1:上传返回 duplicate:true,我的文件没传上去吗? 传上了——内容已存在(SHA1 相同),服务器返回已有 hash 且不生成新删除码。想拿新删除码只能用新文件名+微调内容(哪怕改 1 个字节)。

Q2:为什么 /300x200/ 出来的图不是精确 300×200? 默认等比缩放保证不变形;要精确尺寸在路径里加 forcesize(9.1 对照实测)。

Q3:删除码丢了怎么办? 无法补发(只在上传响应出现一次)。管理员可用 MASTER_DELETE_CODE 或 /admin 后台删除兜底;否则文件将永久留存。

Q4:怎么在删除后确认真的删掉了? 唯一可靠判据 curl /api/info/<hash> 返回 Hash not found;外链状态码受缓存残留影响不可信(7.3、实录二)。

Q5:上传口令能不能放进 URL? 可以——uploadcode 支持 GET 查询参数(/api/upload?uploadcode=xxx)与 POST 字段两种形态;URL 形态会进访问日志,介意留痕就用 POST。

Q6:Agent 接入用哪套接口? 有 MCP 客户端(Claude Desktop/Claude Code 等)直接走 /mcp(第十章,7 个工具自描述);脚本集成走 /api/*;两者共用同一份存储与删除码体系。

十三、参考链接

  1. 官方 API 手册:https://github.com/HaschekSolutions/PictShare/blob/master/rtfm/API.md(注意其仍记载已移除的 v2 端点,以本文实测为准)
  2. 官方变换手册:https://github.com/HaschekSolutions/PictShare/blob/master/rtfm/MODIFIERS.md 与 IMAGEFILTERS.md
  3. 官方 MCP 说明:https://github.com/HaschekSolutions/PictShare/blob/master/rtfm/MCP.md
  4. 官方仓库:https://github.com/HaschekSolutions/PictShare(最新稳定版 v3.7.5,2026-10-27 发布,查询日期 2026-10-31)
  5. 本专栏系列:《PictShare 介绍与选型指南》、《PictShare 3.7.5 Docker Compose 部署指南》、《PictShare 3.7.5 配置参数详解》。