# sphotos **Repository Path**: CodeSeeed/sphotos ## Basic Information - **Project Name**: sphotos - **Description**: No description available - **Primary Language**: Unknown - **License**: AGPL-3.0 - **Default Branch**: main - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-08-16 - **Last Updated**: 2026-08-16 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # Sphotos — 图片托管工具 上传图片,得到一个可公开访问的直链,方便嵌入到 Markdown、HTML 或任何需要图片 URL 的地方。 ![demo](https://img.shields.io/badge/stack-Gin%20+%20Vue%203-4f8cff) ![License](https://img.shields.io/badge/license-AGPLv3-blue.svg) ## 功能 - 拖拽 / 点击上传图片,支持 JPG / PNG / GIF / WebP / SVG / AVIF,**可批量多选** - 返回公开直链,可一键复制 **URL / Markdown / HTML** 三种嵌入代码,**批量结果一键复制全部 URL** - **过期时间可选**:网页端可在「永久 / 1 小时 / 1 天 / 7 天 / 30 天」中选择,画廊实时显示剩余时间倒计时,过期自动清理 - 图片面板:缩略图列表(使用 `?w=300` 动态缩略图)、复制链接、删除(删除密钥保存在你的浏览器本地) - **PicGo 兼容接口**:支持 Typora / PicGo / ShareX / uPic 等图床工具直接上传 - **上传安全**:格式白名单(魔数校验,不看文件后缀)、图片解码验证、SVG XSS 清洗、每 IP 频率限制 - **防盗链**:Referer 白名单(支持 `*.domain.com` 通配符),防止他人白嫖带宽 - **自动水印**:文字水印(内置 ASCII 字体,可指定 TTF 字体文件)或图片水印(PNG),可调位置 / 透明度 / 边距 - **自动压缩转 WebP**:JPEG/PNG 上传时自动转 WebP(libwebp 静态编译进二进制),省带宽和磁盘;透明 PNG 保持 PNG - **动态图片处理**:URL 参数 `?w=300&h=200&mode=crop|fit|scale&fmt=webp|jpeg|png&q=80` 实时裁剪/缩放/转格式 - **回收站**:软删除,可恢复,N 天后自动彻底清理(默认 7 天) - **统计与监控**:`/healthz` 健康检查,`/api/stats` 统计接口(总数/总大小/访问量/热门排行),每张图片自动计数 - **多用户认证**:环境变量 `SPS_ADMIN_USER`/`SPS_ADMIN_PASS` 开启管理员认证,图片归属用户 - **存储后端抽象**:支持本地磁盘(默认)和 **S3 兼容对象存储**(MinIO/S3),通过环境变量切换 - 删除需要密钥(上传时返回,仅可删除自己上传的图片) ## 快速开始 ### 构建 需要 Go 1.21+(且系统装有 `libwebp-dev`,用于静态链接 WebP 编码)和 Node.js 18+(含 pnpm)。 ```bash make build # 构建前端并编译出后端/sphotos 单文件静态二进制 ``` ### 运行 ```bash ./backend/sphotos ``` 默认监听 `0.0.0.0:8080`,打开 http://localhost:8080 即可使用。 ## 配置(环境变量) ### 基础 | 变量 | 默认值 | 说明 | |------|--------|------| | `SPS_PORT` | `8080` | 监听端口 | | `SPS_UPLOAD_DIR` | `./uploads` | 图片文件存储目录 | | `SPS_DATA_DIR` | `./data` | 元数据(metadata.json)目录 | | `SPS_BASE_URL` | 自动推断 | 返回给客户端的图片 URL 前缀,如 `https://img.example.com`;不设置时取请求的 Host | | `SPS_MAX_UPLOAD_MB` | `100` | 单张图片大小上限(MB) | | `SPS_DEFAULT_EXPIRY` | 无 | 默认过期时长,如 `24h`、`7d`(Go duration 格式);不设置则默认永久 | | `SPS_RATE_LIMIT_PER_MIN` | `30` | 每 IP 每分钟允许的上传次数,`0` 关闭限流 | ### 防盗链 | 变量 | 默认值 | 说明 | |------|--------|------| | `SPS_HOTLINK_ALLOW_REFERERS` | 空 | 允许的 Referer 域名白名单,逗号分隔,支持 `*.example.net` 通配符;空 = 不限制 | | `SPS_HOTLINK_ALLOW_EMPTY` | `true` | 是否允许无 Referer 的请求 | ### 水印 | 变量 | 默认值 | 说明 | |------|--------|------| | `SPS_WATERMARK_TEXT` | 空 | 水印文字 | | `SPS_WATERMARK_FONT` | 内置 basicfont | TTF/OTF 字体路径;不设置则用内置点阵字体(仅 ASCII) | | `SPS_WATERMARK_IMAGE` | 空 | 水印图片路径(PNG) | | `SPS_WATERMARK_POSITION` | `bottom-right` | `top-left` / `top-right` / `bottom-left` / `bottom-right` | | `SPS_WATERMARK_OPACITY` | `30` | 0–100 | | `SPS_WATERMARK_PADDING` | `10` | 距边缘像素 | ### 压缩 | 变量 | 默认值 | 说明 | |------|--------|------| | `SPS_LOSSY_COMPRESS` | `false` | 开启后 JPEG/PNG 自动转 WebP | | `SPS_LOSSY_QUALITY` | `80` | 1–100 | ### 回收站 | 变量 | 默认值 | 说明 | |------|--------|------| | `SPS_TRASH_RETAIN_DAYS` | `7` | 软删除图片保留天数,到期自动彻底清理;`0` 关闭自动清理 | ### 认证 | 变量 | 默认值 | 说明 | |------|--------|------| | `SPS_ADMIN_USER` | 空 | 管理员用户名;空 = 关闭认证 | | `SPS_ADMIN_PASS` | 空 | 管理员密码 | ### 存储后端 | 变量 | 默认值 | 说明 | |------|--------|------| | `SPS_STORAGE` | `local` | 存储后端:`local` 或 `s3` | | `SPS_S3_ENDPOINT` | — | S3 兼容服务的端点(如 `play.min.io`) | | `SPS_S3_ACCESS_KEY` | — | S3 Access Key | | `SPS_S3_SECRET_KEY` | — | S3 Secret Key | | `SPS_S3_BUCKET` | — | S3 存储桶名 | | `SPS_S3_REGION` | — | S3 区域(如 `us-east-1`) | | `SPS_S3_PREFIX` | — | 可选前缀(子目录) | | `SPS_S3_USE_SSL` | `false` | 是否使用 HTTPS | ## API ### 上传图片 ``` POST /api/upload Content-Type: multipart/form-data 字段:file=<图片文件>,可选 expiry=<过期时长,如 1h/24h> ``` ### PicGo 兼容上传 ``` POST /api/upload/picgo Content-Type: multipart/form-data 字段:file=<图片文件>,可选 expiry=<过期时长> ``` 返回格式兼容 PicGo 自定义上传器、uPic、ShareX 等工具,无需额外 JSON 路径配置。 可配合 Typora 的「自定义命令」或 PicGo 的「自定义 Web 上传」使用,上传 URL 填 `http://your-host:8080/api/upload/picgo` 即可。 响应: ```json { "id": "f3719e2afc92a7a6", "url": "http://localhost:8080/files/f3719e2afc92a7a6.webp", "delete_secret": "4f122b47b800a287c95080e6a7ccfd5f", "original_name": "test.png", "mime": "image/webp", "size": 234, "expires_at": null } ``` > 开启 `SPS_LOSSY_COMPRESS` 后,JPEG/PNG 会被转成 WebP,返回的 `url` 扩展名和 `mime` 会相应变为 `.webp` / `image/webp`。 `delete_secret` 仅在此时返回,请自行保存(网页版会自动存在浏览器 localStorage)。 ### 获取直链 ``` GET /files/{id}.{ext} ``` 在启用防盗链时,请求需携带允许的 `Referer` 头。 ### 动态图片处理 在直链 URL 后添加查询参数,实时裁剪/缩放/转格式: ``` GET /files/{id}.{ext}?w=300&h=200&mode=crop&fmt=webp&q=80 ``` | 参数 | 说明 | |------|------| | `w` | 目标宽度(像素) | | `h` | 目标高度(像素) | | `mode` | `fit`(适应,默认) / `crop`(裁剪) / `scale`(拉伸) | | `fmt` | `webp` / `jpeg` / `png`(输出格式) | | `q` | 质量 1–100,默认 80 | ### 列出图片 ``` GET /api/images ``` 返回所有未删除图片的公开元数据(不含 `delete_secret`)。 ### 回收站 ``` GET /api/trash # 列出已删除的图片 POST /api/images/:id/restore?secret={secret} # 恢复 DELETE /api/images/:id?secret={secret} # 软删除(移入回收站) ``` ### 统计与监控 ``` GET /healthz # {"status":"ok"} GET /api/stats # 返回 total_images / total_size / total_views / top_images ``` ### 认证 ``` POST /api/auth/login # JSON {"username":"...","password":"..."} → {"token":"...","username":"..."} GET /api/auth/me # Authorization: Bearer → {"username":"..."} ``` 仅在配置了 `SPS_ADMIN_USER` / `SPS_ADMIN_PASS` 时启用。认证用户上传的图片会标记 `owner` 字段。 ## 目录结构 ``` ├── backend/ # Gin 后端(单文件静态二进制,内嵌前端 + libwebp) │ ├── main.go # 入口 & 路由 & SPA 托管 │ ├── handlers.go # 上传 / 列表 / 删除 / 文件服务 / 过期清理 / 认证 / 统计 │ ├── auth/ # 用户认证与 session 管理 │ ├── compress/ # WebP 压缩编码 │ ├── config/ # 配置加载(30+ 环境变量) │ ├── imaging/ # 动态图片处理(裁剪/缩放/转格式) │ ├── middleware/ # 限流 + 防盗链 │ ├── sanitize/ # SVG XSS 清洗 │ ├── storage/ # 元数据存储 + 存储后端接口(Local / S3) │ ├── watermark/ # 文字/图片水印 │ └── dist/ # 前端构建产物(跟随前端构建自动生成) ├── frontend/ # Vue 3 + Vite 前端 │ └── src/App.vue # 上传区 + 结果面板 + 图片画廊 ├── uploads/ # 图片文件(运行期生成) ├── data/ # 元数据(运行期生成) └── Makefile # 一键构建 / 运行 ``` ## 开发模式 ```bash # 终端 1:后端(需要 libwebp-dev) cd backend && go run . # 终端 2:前端(热更新,/api 与 /files 自动代理到 8080) cd frontend && pnpm dev ```