Acme

Utils

S3 对象存储 SDK

A flexible S3-compatible object storage SDK with automatic STS credential refresh, progress tracking, and batch upload support.

特性

  • ✅ 自动缓存和刷新 STS 临时凭证
  • ✅ 支持单文件和批量上传
  • ✅ 上传进度回调
  • ✅ 错误处理
  • ✅ TypeScript 类型支持
  • ✅ 灵活的配置选项

安装依赖

npm install @aws-sdk/client-s3 @aws-sdk/lib-storage dayjs

快速开始

创建 SDK 实例

import { createS3SDK } from './s3';

// 定义获取 STS 凭证的函数
async function fetchSTSCredentials() {
  const response = await fetch('/api/tos/session');
  const data = await response.json();

  return {
    accessKeyId: data.result.credentials.accessKeyId,
    secretAccessKey: data.result.credentials.secretAccessKey,
    sessionToken: data.result.credentials.sessionToken,
    expiredTime: data.result.credentials.expiredTime,
  };
}

// 创建 SDK 实例
const s3SDK = createS3SDK({
  endpoint: 'tos-s3-cn-guangzhou.volces.com',
  bucket: 'your-bucket-name',
  region: 'cn-guangzhou',
  fetchCredentials: fetchSTSCredentials,
});

上传文件

// 基础上传
const url = await s3SDK.upload({
  file: yourFile,
});

// 带进度的上传
const url = await s3SDK.upload({
  file: yourFile,
  prefix: 'uploads', // 文件路径前缀
  onProgress: (percent) => {
    console.log(`上传进度: ${percent}%`);
  },
  onError: (error) => {
    console.error('上传失败:', error);
  },
});

自定义文件路径

const url = await s3SDK.upload({
  file: yourFile,
  key: 'custom/path/2024-01-01/filename.jpg', // 完全自定义路径
});

批量上传

const results = await s3SDK.uploadBatch(files, {
  prefix: 'batch-uploads',
  onBatchProgress: (completed, total) => {
    console.log(`进度: ${completed}/${total}`);
  },
});

// 检查结果
results.forEach(({ file, url, error }) => {
  if (url) {
    console.log(`✓ ${file.name} 上传成功`);
  } else {
    console.error(`✗ ${file.name} 上传失败:`, error);
  }
});

API 文档

createS3SDK(config)

创建 S3 SDK 实例。

参数:

interface S3SDKConfig {
  endpoint: string;              // S3 端点
  bucket: string;                // 存储桶名称
  region: string;                // 区域
  fetchCredentials: () => Promise<STSCredentials>; // 获取凭证的函数
  refreshBeforeExpire?: number;  // 提前刷新时间(毫秒),默认 60000
}

返回: S3SDK 实例


s3SDK.upload(options)

上传单个文件。

参数:

interface UploadOptions {
  file: File;                    // 要上传的文件
  key?: string;                  // 自定义文件路径
  prefix?: string;               // 路径前缀(默认: 'uploads')
  onProgress?: (percent: number) => void;  // 进度回调
  onError?: (error: unknown) => void;      // 错误回调
  partSize?: number;             // 分片大小(字节),默认 5MB
}

返回: Promise<string | null> - 成功返回文件 URL,失败返回 null


s3SDK.uploadBatch(files, options)

批量上传文件。

参数:

files: File[]  // 要上传的文件数组

options: Omit<UploadOptions, 'file'> & {
  onBatchProgress?: (completed: number, total: number) => void;
}

返回:

Promise<Array<{
  file: File;
  url: string | null;
  error?: unknown;
}>>

s3SDK.getClient()

获取 S3 客户端实例(通常不需要直接调用)。

返回: Promise<S3Client>


s3SDK.destroy()

销毁 SDK 实例,清理资源。

// 在应用卸载时调用
s3SDK.destroy();

React 组件示例

import React, { useState } from 'react';
import { createS3SDK } from './s3';

const s3SDK = createS3SDK({
  // ... 配置
});

function FileUploader() {
  const [progress, setProgress] = useState(0);
  const [url, setUrl] = useState<string | null>(null);

  const handleUpload = async (e: React.ChangeEvent<HTMLInputElement>) => {
    const file = e.target.files?.[0];
    if (!file) return;

    const uploadedUrl = await s3SDK.upload({
      file,
      onProgress: setProgress,
      onError: (error) => {
        console.error('上传失败:', error);
      },
    });

    setUrl(uploadedUrl);
  };

  return (
    <div>
      <input type="file" onChange={handleUpload} />
      {progress > 0 && <progress value={progress} max={100} />}
      {url && <img src={url} alt="Uploaded" />}
    </div>
  );
}

高级用法

自定义凭证刷新策略

const s3SDK = createS3SDK({
  // ...
  refreshBeforeExpire: 5 * 60 * 1000, // 提前 5 分钟刷新凭证
});

自定义分片大小

await s3SDK.upload({
  file: largeFile,
  partSize: 10 * 1024 * 1024, // 10MB 分片
});

与原有代码兼容

如果你需要保持与原有代码的兼容性:

// 创建全局实例
const s3SDK = createS3SDK({ /* ... */ });

// 导出兼容函数
export async function uploadToS3({ file, key, onProgress, onError }) {
  return s3SDK.upload({ file, key, onProgress, onError });
}

export async function getS3Client() {
  return s3SDK.getClient();
}

错误处理

const url = await s3SDK.upload({
  file: yourFile,
  onError: (error) => {
    if (error instanceof Error) {
      // 处理特定错误
      if (error.message.includes('credentials')) {
        console.error('凭证错误,请检查 STS 配置');
      }
    }
  },
});

if (!url) {
  // 上传失败的处理逻辑
}

注意事项

  1. 凭证自动刷新: SDK 会在凭证过期前自动刷新,无需手动处理
  2. 客户端缓存: S3 客户端会被缓存以提高性能
  3. 资源清理: 在应用卸载时记得调用 destroy() 方法
  4. 文件路径: 如果不指定 key,文件会保存到 {prefix}/{YYYY-MM-DD}/{filename}

支持的对象存储服务

这个 SDK 兼容所有支持 S3 协议的对象存储服务:

  • AWS S3
  • 火山引擎 TOS
  • 阿里云 OSS (S3 兼容模式)
  • 腾讯云 COS (S3 兼容模式)
  • MinIO
  • 其他 S3 兼容服务