<div align="center">

<img src="logo.png" alt="picgo-plugin-s3_w" width="120" height="120"/>

# picgo-plugin-s3_w

🎨 一个功能强大的 [PicGo](https://github.com/PicGo/PicGo) Amazon S3 图片上传插件，基于 AWS SDK v3 构建，兼容所有 S3 协议的对象存储服务。

[![npm version](https://img.shields.io/npm/v/picgo-plugin-s3_w.svg?style=flat-square)](https://www.npmjs.com/package/picgo-plugin-s3_w)
[![npm downloads](https://img.shields.io/npm/dm/picgo-plugin-s3_w.svg?style=flat-square)](https://www.npmjs.com/package/picgo-plugin-s3_w)
[![license](https://img.shields.io/npm/l/picgo-plugin-s3_w.svg?style=flat-square)](./LICENSE)
[![GitHub stars](https://img.shields.io/github/stars/wangwenjier/picgo-plugin-s3_w.svg?style=flat-square)](https://github.com/wangwenjier/picgo-plugin-s3_w)

</div>

## 📖 简介

`picgo-plugin-s3_w` 是 [PicGo](https://github.com/PicGo/PicGo) 的第三方上传插件，用于将图片上传至 Amazon S3 或任意兼容 S3 协议的对象存储服务（如 Cloudflare R2、MinIO、阿里云 OSS、腾讯云 COS、Backblaze B2、七牛云 Kodo 等）。

插件基于 `@aws-sdk/client-s3` v3 实现，提供强大的自定义文件名与输出 URL 模板能力，支持代理、自定义 ACL、Path Style 访问、TLS 证书校验控制等高级特性。

## ✨ 特性

- 🚀 基于 AWS SDK v3 构建，性能更优、兼容性更好
- 🌐 兼容所有 S3 协议对象存储（Cloudflare R2、MinIO、阿里云 OSS 等）
- 📝 强大的文件名生成模板，支持 `{md5}`、`{sha1}`、`{timestamp}` 等占位符
- 🔗 灵活的输出 URL 自定义模板，支持正则替换
- 🔐 支持 ACL 访问控制
- 🛡️ 支持 Path Style 访问与 TLS 证书校验控制
- 🌍 支持 HTTP/HTTPS 代理
- 🖼️ 自动识别图片 MIME 类型
- 🎯 完美适配 PicGo GUI 与 CLI

## 📦 安装

### 在 PicGo GUI 中安装

1. 打开 PicGo 应用
2. 进入「插件设置」页面
3. 搜索 `s3_w` 并点击安装

### 在 PicGo CLI 中安装

```bash
picgo install s3_w
```

### 通过 npm 安装

```bash
npm install picgo-plugin-s3_w
```

## ⚙️ 配置项

在 PicGo 的「图床设置」中选择 `s3_w`，按以下配置项进行设置：

| 配置项 | 别名 | 必填 | 默认值 | 说明 |
| --- | --- | --- | --- | --- |
| `accessKeyID` | 应用密钥 ID | ✅ | - | AWS Access Key ID |
| `secretAccessKey` | 应用密钥 | ✅ | - | AWS Secret Access Key |
| `bucketName` | 桶名 | ✅ | - | S3 桶名称 |
| `uploadPath` | 上传文件路径 | ✅ | `docs/{timestamp}.{extName}` | 上传文件的存储路径模板 |
| `region` | 地区 | ❌ | - | 区域，如 `us-east-1`，留空则为 `auto` |
| `endpoint` | 自定义节点 | ❌ | - | 自定义 S3 服务地址，用于兼容第三方对象存储 |
| `proxy` | 代理 | ❌ | - | HTTP 代理地址，如 `http://127.0.0.1:1080` |
| `rejectUnauthorized` | 拒绝无效TLS证书连接 | ❌ | `true` | 是否拒绝无效 TLS 证书连接 |
| `acl` | ACL 访问控制列表 | ❌ | `public-read` | 上传资源的访问策略 |
| `pathStyleAccess` | ForcePathStyle | ❌ | `true` | 是否启用 Path Style 访问 |
| `outputURLPattern` | 自定义输出 URL 模板 | ❌ | - | 自定义最终输出 URL 模板，详见下方说明 |

> ⚠️ `urlPrefix`、`urlSuffix`、`disableBucketPrefixToURL` 配置项已废弃，建议使用 `outputURLPattern` 替代。

## 📝 文件名模板（uploadPath）

`uploadPath` 支持以下占位符，可自由组合生成上传文件路径：

### 时间相关占位符

| 占位符 | 说明 | 示例 |
| --- | --- | --- |
| `{year}` | 年份 | `2026` |
| `{month}` | 月份 | `07` |
| `{day}` | 日 | `26` |
| `{hour}` | 小时 | `14` |
| `{minute}` | 分钟 | `30` |
| `{second}` | 秒 | `45` |
| `{millisecond}` | 毫秒 | `123` |
| `{timestamp}` | Unix 时间戳（秒） | `1785031845` |
| `{timestampMS}` | Unix 时间戳（毫秒） | `1785031845123` |

### 文件信息占位符

| 占位符 | 说明 | 示例 |
| --- | --- | --- |
| `{fullName}` | 完整文件名 | `pic.png` |
| `{fileName}` | 文件名（不含扩展名） | `pic` |
| `{extName}` | 扩展名（不含点） | `png` |
| `{md5}` | 文件内容 MD5 哈希 | `d41d8cd98f00b204e9800998ecf8427e` |
| `{md5B64}` | MD5 的 Base64（URL 安全） | `1B2M2Y8AsgTpgAmY7PhCfg` |
| `{md5B64Short}` | MD5 Base64 前 7 位 | `1B2M2Y8` |
| `{sha1}` | 文件内容 SHA1 哈希 | `da39a3ee5e6b4b0d3255bfef95601890afd80709` |
| `{sha256}` | 文件内容 SHA256 哈希 | `e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855` |

### 截断与切片语法

支持对哈希类占位符进行截断或切片操作：

| 语法 | 说明 | 示例 |
| --- | --- | --- |
| `{md5:8}` | 取前 8 位 | `d41d8cd9` |
| `{md5:0,8}` | 从位置 0 开始取 8 位 | `d41d8cd9` |
| `{md5:8,16}` | 从位置 8 开始取 16 位 | `8f00b204e9800998` |

### 示例

```
# 按年月归档
images/{year}/{month}/{fullName}

# 使用时间戳
docs/{timestamp}.{extName}

# 使用 MD5 哈希避免重名
files/{md5:0,2}/{md5:2,2}/{md5}.{extName}

# 短哈希
img/{md5B64Short}.{extName}
```

## 🔗 输出 URL 模板（outputURLPattern）

`outputURLPattern` 用于自定义最终返回的图片 URL。如果不设置，将默认使用 S3 返回的签名 URL（去除查询参数）。

### 基础占位符

| 占位符 | 说明 |
| --- | --- |
| `{protocol}` | 协议（`https` / `http`） |
| `{host}` | 主机名 |
| `{port}` | 端口 |
| `{path}` | 完整路径 |
| `{dir}` | 目录部分 |
| `{fileName}` | 文件名（含扩展名） |
| `{uploadedFileName}` | 上传后的文件名 |
| `{extName}` | 扩展名（不含点） |
| `{query}` | URL 查询参数 |
| `{hash}` | URL hash 部分 |
| `{bucket}` | 桶名 |

同时支持上述所有时间相关占位符。

### 高级用法：正则替换

支持对占位符的输出值进行正则替换：

```
{key:/pattern/flags,'replacement'}
```

**示例：** 将主机名中的 `example.com` 替换为 `cdn.example.com`：

```
{protocol}://{host:/example\.com/i,'cdn.example.com'}/{path}
```

### 常见配置示例

#### Cloudflare R2 自定义域名

```
https://cdn.example.com/{path}
```

#### 阿里云 OSS / 腾讯云 COS

```
https://{bucket}.oss-cn-hangzhou.aliyuncs.com/{path}
```

#### 保留原始 host 并移除 bucket 前缀

```
{protocol}://{host}/{dir}/{fileName}
```

#### 使用 HTTPS 并附加图片处理参数

```
{protocol}://{host}/{path}?x-oss-process=image/resize,w_500
```

## 🎯 配置示例

### Cloudflare R2

| 配置项 | 值 |
| --- | --- |
| `accessKeyID` | 你的 R2 Access Key ID |
| `secretAccessKey` | 你的 R2 Secret Access Key |
| `bucketName` | 你的 R2 桶名 |
| `endpoint` | `https://<account_id>.r2.cloudflarestorage.com` |
| `region` | `auto` |
| `pathStyleAccess` | `true` |
| `outputURLPattern` | `https://cdn.example.com/{path}` |

### MinIO

| 配置项 | 值 |
| --- | --- |
| `accessKeyID` | 你的 MinIO Access Key |
| `secretAccessKey` | 你的 MinIO Secret Key |
| `bucketName` | 你的 MinIO 桶名 |
| `endpoint` | `http://127.0.0.1:9000` |
| `pathStyleAccess` | `true` |
| `rejectUnauthorized` | `false`（自签名证书时） |

### AWS S3

| 配置项 | 值 |
| --- | --- |
| `accessKeyID` | 你的 AWS Access Key ID |
| `secretAccessKey` | 你的 AWS Secret Access Key |
| `bucketName` | 你的 S3 桶名 |
| `region` | `us-east-1` |
| `pathStyleAccess` | `false` |
| `acl` | `public-read` |

## 🛠️ 开发

### 环境要求

- Node.js ≥ 18
- npm ≥ 9

### 本地开发

```bash
# 克隆仓库
git clone https://github.com/wangwenjier/picgo-plugin-s3_w.git
cd picgo-plugin-s3_w

# 安装依赖
npm install

# 启动开发监听
npm run dev

# 构建
npm run build
```

### 发布流程

本项目使用 GitHub Actions 自动发布到 npm：

1. 修改代码并提交
2. 更新 `package.json` 中的版本号
3. 执行 `npm version <patch|minor|major>`，会自动构建并推送 tag
4. GitHub Actions 检测到 `v*` tag 后会自动发布到 npm

> 🔑 发布前请在仓库 Settings → Secrets 中配置 `NPM_TOKEN`。

## 📚 常见问题

### 上传失败提示「TLS 证书无效」？

如果使用的是自签名证书（如本地 MinIO），请将 `rejectUnauthorized` 设置为 `false`。

### 第三方对象存储无法访问？

请确认 `endpoint` 配置正确，并将 `pathStyleAccess` 设置为 `true`。

### 输出 URL 包含签名参数？

请配置 `outputURLPattern` 自定义输出 URL，例如 `https://cdn.example.com/{path}`。

### 如何使用代理？

在 `proxy` 配置项中填写代理地址，例如 `http://127.0.0.1:1080`。

## 🤝 贡献

欢迎提交 Issue 和 Pull Request！

1. Fork 本仓库
2. 创建你的特性分支：`git checkout -b feature/amazing-feature`
3. 提交你的修改：`git commit -m 'Add some amazing feature'`
4. 推送到分支：`git push origin feature/amazing-feature`
5. 提交 Pull Request

## 📄 开源协议

[MIT License](./LICENSE) © 2026 [wangwenjier](https://github.com/wangwenjier)

## ⭐ 鸣谢

- [PicGo](https://github.com/PicGo/PicGo) - 优秀的图片上传工具
- [picgo-plugin-s3](https://github.com/wayjam/picgo-plugin-s3) - 本项目最初的灵感来源
- [AWS SDK for JavaScript v3](https://github.com/aws/aws-sdk-js-v3)

---

如果这个项目对你有帮助，欢迎 ⭐ Star 支持！