SuperAPI AI Directory

storage-url-safety

Agents & models

View repository

Updated 10-15 21:11

About this skill

Storage URL Policy Selection Specifications

When using object storage such as MinIO/OSS/S3, correctly choose the URL generation policy.


Pitfall #1: Using presigned URLs for long-term resources such as avatars

Scenario: Resources that require long-term access, such as avatars, logos, and product images

Root Cause of the Problem

Presigned URLs have expiration limits (MinIO/S3 maximum 7 days). Long-term resources such as avatars will expire and become inaccessible.

Incorrect Example

// ❌ 错误: 预签名 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 天后用户头像显示"图片加载失败"

Correct Approach

// ✅ 方案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 Policy Selection Table

Resource Type Recommended Policy Validity Period Applicable Scenario Example
Avatar/Logo Public URL / CDN Permanent Requires long-term access User avatar, company logo
Product Image Public URL / CDN Permanent Requires long-term access E-commerce product images, article illustrations
Public Document Public URL / CDN Permanent Requires long-term access User manual, API documentation
Temporary File Presigned URL 1h-7d Download credential Exported Excel, temporary sharing
Private Document Presigned URL 15min-1h Temporary authorization Contract, financial statement
Upload Credential Presigned URL 5min-30min Direct client upload Frontend direct upload to OSS

Pitfall #2: The bucket for a public URL is not configured for public read

Scenario: Using a public URL but the bucket policy is not configured

Incorrect Example

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

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

Correct Approach

MinIO Configure Public Read:

# 方案1: 使用 mc 命令配置(推荐)
mc anonymous set download minio/bucket/avatars

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

Bucket Policy Example:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {"AWS": ["*"]},
      "Action": ["s3:GetObject"],
      "Resource": ["arn:aws:s3:::bucket/avatars/*"]
    }
  ]
}

Alibaba Cloud OSS Configuration:

# 设置 bucket 公共读
ossutil64 set-acl oss://bucket-name public-read

# 或只设置特定目录
ossutil64 set-acl oss://bucket-name/avatars/ public-read --recursive

Pitfall #3: Improper validity period setting for presigned URLs

Scenario: The validity period of a temporary file download link is too long or too short

Specification

Scenario Recommended Validity Period Description
Direct client upload credential 5-30 minutes Upload time is usually very short
Temporary sharing link 1-24 hours The user may download later
Exported file download 1-7 days The user may download multiple times
Private document viewing 15-60 minutes High security requirements

Incorrect Example

// ❌ 错误: 客户端直传凭证有效期 7 天,安全风险高
String uploadUrl = minioService.getPresignedUrl(filePath, 60 * 24 * 7);

// ❌ 错误: 导出文件下载链接只有 5 分钟,用户可能来不及下载
String downloadUrl = minioService.getPresignedUrl(filePath, 5);

Correct Approach

// ✅ 客户端直传凭证: 15 分钟
String uploadUrl = minioService.getPresignedUrl(filePath, 15);

// ✅ 导出文件下载: 24 小时
String downloadUrl = minioService.getPresignedUrl(filePath, 60 * 24);

// ✅ 私密文档查看: 30 分钟
String viewUrl = minioService.getPresignedUrl(filePath, 30);

Pitfall #4: File type and size are not validated during frontend direct upload

Scenario: Frontend directly uploads to OSS, backend generates the upload credential

Incorrect Example

// ❌ 错误: 未校验文件类型和大小,任何文件都能上传
@PostMapping("/upload/token")
public ApiResponse<String> getUploadToken(@RequestParam String filename) {
    String uploadUrl = minioService.getPresignedUrl("uploads/" + filename, 15);
    return ApiResponse.success(uploadUrl);
}

Correct Approach

// ✅ 后端校验文件类型和大小
@PostMapping("/upload/token")
public ApiResponse<UploadToken> getUploadToken(
    @RequestParam String filename,
    @RequestParam String contentType,
    @RequestParam Long fileSize) {

    // 校验文件类型
    List<String> allowedTypes = Arrays.asList("image/jpeg", "image/png", "image/gif");
    if (!allowedTypes.contains(contentType)) {
        return ApiResponse.error("不支持的文件类型");
    }

    // 校验文件大小(5MB)
    if (fileSize > 5 * 1024 * 1024) {
        return ApiResponse.error("文件大小不能超过 5MB");
    }

    // 生成安全的文件名(防止路径遍历)
    String safeFilename = UUID.randomUUID() + getExtension(filename);
    String filePath = "avatars/" + LocalDate.now() + "/" + safeFilename;

    String uploadUrl = minioService.getPresignedUrl(filePath, 15);
    return ApiResponse.success(new UploadToken(uploadUrl, filePath));
}

Pitfall #5: Incorrect CDN origin fetch configuration

Scenario: Using CDN acceleration but the origin-pull configuration is incorrect

Incorrect example

// ✅ 代码正确返回 CDN URL
String avatarUrl = "https://cdn.example.com/avatars/xxx.jpeg";

// ❌ 但 CDN 回源配置错误:
// 1. 回源 Host 未设置为 MinIO endpoint
// 2. 回源协议未设置为 HTTP
// 3. 回源路径未包含 bucket 名称
// 导致 CDN 返回 404 或 403

Correct approach

Alibaba Cloud CDN origin-pull configuration:

回源 Host: minio.example.com
回源协议: HTTP
回源地址: minio.example.com:9000
回源路径: /bucket${uri}

Tencent Cloud CDN origin-pull configuration:

源站类型: 自有源
源站地址: minio.example.com:9000
回源协议: HTTP
回源 Host: minio.example.com
回源路径: /bucket${uri}

Checklist (storage URL strategy)

URL strategy selection:

  • Do avatars/logos use public URLs or CDN
  • Do temporary files use presigned URLs
  • Is the validity period of presigned URLs ≤ 7 days
  • Does the validity period of presigned URLs match the business scenario

Bucket configuration:

  • Is a public-read policy configured for buckets with public URLs
  • Is the public-read policy limited to specific directories (such as avatars/)
  • Is CORS configured (required for direct frontend uploads)

CDN configuration:

  • Is CDN acceleration used in the production environment
  • Is the CDN origin-pull Host correct
  • Does the CDN origin-pull path include the bucket name
  • Are cache rules configured for the CDN

Security:

  • Are file type and size validated for direct frontend uploads
  • Are filenames using UUIDs to prevent path traversal
  • Do private files use presigned URLs instead of public URLs

Scope of application

  • MinIO
  • Alibaba Cloud OSS
  • Tencent Cloud COS
  • AWS S3
  • Qiniu Cloud Kodo
  • Huawei Cloud OBS

Rule traceability

> 📋 本回复遵循:`storage-url-safety` - 存储 URL 策略选择规范