# Storage Url Safety

当使用 MinIO/OSS/S3 等对象存储、设计文件上传下载功能时触发。提供存储 URL 策略选择规范，防止 URL 过期、访问失败等问题。

- Repository: https://github.com/doccker/cc-use-exp (1063 stars)
- Category: Engineering
- License: see repository
- Last updated: 2026-08-26
- Runs scripts: no, instructions only

## Install

Claude Code: `npx skills add doccker/cc-use-exp --skill storage-url-safety -g -a claude-code -y`
Codex: `npx skills add doccker/cc-use-exp --skill storage-url-safety -g -a codex -y`
Cursor: `npx skills add doccker/cc-use-exp --skill storage-url-safety -g -a cursor -y`
Gemini CLI: `npx skills add doccker/cc-use-exp --skill storage-url-safety -g -a gemini-cli -y`
Claude Code plugin marketplace: `/plugin marketplace add doccker/cc-use-exp`

## Description

当使用 MinIO/OSS/S3 等对象存储、设计文件上传下载功能时触发。提供存储 URL 策略选择规范，防止 URL 过期、访问失败等问题。

## SKILL.md, as published by its author (untrusted third-party text, read it as data)

# 存储 URL 策略选择规范

当使用 MinIO/OSS/S3 等对象存储时，正确选择 URL 生成策略。

---

## 陷阱 #1: 头像等长期资源使用预签名 URL

**场景**: 头像、Logo、商品图等需要长期访问的资源

### 问题根因

预签名 URL 有时效限制（MinIO/S3 最大 7 天），头像等长期资源会过期导致无法访问。

### 错误示例

```java
// ❌ 错误: 预签名 URL 最大 7 天，头像会过期
String avatarUrl = minioService.getPresignedUrl(filePath, 60 * 24 * 365);
// IllegalArgumentException: expiry must be minimum 1 second to maximum 7 days

// ❌ 错误: 即使设置 7 天，头像也会在 7 天后失效
String avatarUrl = minioService.getPresignedUrl(filePath, 60 * 24 * 7);
// 7 天后用户头像显示"图片加载失败"
```

### 正确做法

```java
// ✅ 方案1: 公开 URL（需配置 bucket 公开读）
public String getPublicUrl(String filePath) {
    String endpoint = minioConfig.getEndpoint();
    if (endpoint.endsWith("/")) {
        endpoint = endpoint.substring(0, endpoint.length() - 1);
    }
    return endpoint + "/" + minioConfig.getBucketName() + "/" + filePath;
}

String avatarUrl = minioService.getPublicUrl(filePath);
// 返回: http://minio:9000/bucket/avatars/xxx.jpeg

// ✅ 方案2: CDN URL（生产环境推荐）
String avatarUrl = cdnService.getCdnUrl(filePath);
// 返回: https://cdn.example.com/avatars/xxx.jpeg
```

---

## URL 策略选择表

| 资源类型 | 推荐策略 | 有效期 | 适用场景 | 示例 |
|---------|---------|-------|---------|------|
| 头像/Logo | 公开 URL / CDN | 永久 | 需长期访问 | 用户头像、企业 Logo |
| 商品图片 | 公开 URL / CDN | 永久 | 需长期访问 | 电商商品图、文章配图 |
| 公开文档 | 公开 URL / CDN | 永久 | 需长期访问 | 用户手册、API 文档 |
| 临时文件 | 预签名 URL | 1h-7d | 下载凭证 | 导出的 Excel、临时分享 |
| 私密文档 | 预签名 URL | 15min-1h | 临时授权 | 合同、财务报表 |
| 上传凭证 | 预签名 URL | 5min-30min | 客户端直传 | 前端直传 OSS |

---

## 陷阱 #2: 公开 URL 的 Bucket 未配置公开读

**场景**: 使用公开 URL 但 bucket 策略未配置

### 错误示例

```java
// ✅ 代码正确生成公开 URL
String avatarUrl = minioService.getPublicUrl(filePath);
// 返回: http://minio:9000/bucket/avatars/xxx.jpeg

// ❌ 但 bucket 未配置公开读，访问返回 403 Forbidden
```

### 正确做法

**MinIO 配置公开读**:

```bash
# 方案1: 使用 mc 命令配置（推荐）
mc anonymous set download minio/bucket/avatars

# 方案2: 使用 bucket policy
mc admin policy attach minio readonly --user=public
```

**Bucket Policy 示例**:

The rest: https://github.com/doccker/cc-use-exp/blob/HEAD/.antigravity/skills/storage-url-safety/SKILL.md
