npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@meta-1/nest-assets

v0.0.11

Published

NestJS assets module for S3 and OSS object storage

Readme

@meta-1/nest-assets

NestJS 资源管理模块,支持亚马逊 S3、阿里云 OSS 和 MinIO 对象存储。

功能特性

  • ✅ 支持亚马逊 S3、阿里云 OSS 和 MinIO
  • ✅ 预签名 URL 模式,客户端直传
  • ✅ 统一接口,内部自动切换存储提供商
  • ✅ 支持私桶和公桶
  • ✅ 私桶访问自动签名授权
  • ✅ 可配置签名有效期

安装

npm install @meta-1/nest-assets

所有必需的依赖(包括 AWS S3 SDK、阿里云 OSS SDK 和 MinIO SDK)会自动安装。

使用方法

1. 导入并配置模块

import { Module } from '@nestjs/common';
import { AssetsModule } from '@meta-1/nest-assets';
import { StorageProvider } from '@meta-1/nest-types';

@Module({
  imports: [
    // 使用 S3
    AssetsModule.forRoot({
      storage: {
        provider: StorageProvider.S3,
        publicBucket: 'my-public-bucket',
        privateBucket: 'my-private-bucket',
        expiresIn: '30m'  // 支持字符串(如 '30m', '1h', '2d')或数字(毫秒)
      },
      s3: {
        region: 'us-east-1',
        accessKeyId: 'your-access-key-id',
        secretAccessKey: 'your-secret-access-key',
        endpoint: 'https://s3.amazonaws.com'  // 可选,用于兼容 S3 的服务
      }
    }),
    
    // 或使用阿里云 OSS
    AssetsModule.forRoot({
      storage: {
        provider: StorageProvider.OSS,
        publicBucket: 'my-public-bucket',
        privateBucket: 'my-private-bucket',
        expiresIn: '30m'
      },
      oss: {
        region: 'oss-cn-hangzhou',
        accessKeyId: 'your-access-key-id',
        accessKeySecret: 'your-secret-access-key'
      }
    }),
    
    // 或使用 MinIO
    AssetsModule.forRoot({
      storage: {
        provider: StorageProvider.MINIO,
        publicBucket: 'my-public-bucket',
        privateBucket: 'my-private-bucket',
        expiresIn: '30m'
      },
      minio: {
        endpoint: 'http://localhost:9000',  // 支持 http://host:port 或 https://host:port 格式
        accessKeyId: 'your-access-key-id',
        secretAccessKey: 'your-secret-access-key',
        useSSL: false,  // 可选,默认 false。也可以通过 endpoint 的协议自动推断
        region: 'us-east-1'  // 可选,默认 'us-east-1'
      }
    })
  ]
})
export class AppModule {}

2. 从配置文件加载

// main.ts
import { NestFactory } from '@nestjs/core';
import { ConfigLoader, ConfigSourceType } from '@meta-1/nest-common';

interface AppConfig {
  assets: AssetsConfig;
}

async function bootstrap() {
  // 加载配置
  const loader = new ConfigLoader<AppConfig>({
    type: ConfigSourceType.LOCAL_YAML,
    filePath: './config/app.yaml'
  });
  
  const config = await loader.load();
  
  // 创建模块
  @Module({
    imports: [AssetsModule.forRoot(config.assets)]
  })
  class AppModule {}
  
  const app = await NestFactory.create(AppModule);
  await app.listen(3000);
}
bootstrap();

配置文件示例 (config/app.yaml):

assets:
  storage:
    provider: s3  # 's3' | 'oss' | 'minio'
    publicBucket: my-public-bucket
    privateBucket: my-private-bucket
    expiresIn: 30m
  s3:
    region: us-east-1
    accessKeyId: ${AWS_ACCESS_KEY_ID}
    secretAccessKey: ${AWS_SECRET_ACCESS_KEY}
    endpoint: ${AWS_S3_ENDPOINT}  # 可选
  oss:
    region: oss-cn-hangzhou
    accessKeyId: ${ALIYUN_ACCESS_KEY_ID}
    accessKeySecret: ${ALIYUN_ACCESS_KEY_SECRET}
  minio:
    endpoint: ${MINIO_ENDPOINT}  # 支持 http://host:port 或 https://host:port 格式
    accessKeyId: ${MINIO_ACCESS_KEY_ID}
    secretAccessKey: ${MINIO_SECRET_ACCESS_KEY}
    useSSL: false  # 可选,默认 false
    region: us-east-1  # 可选,默认 'us-east-1'

3. 在业务方创建 Controller

重要: AssetsModule 只提供 Service,Controller 需要由业务方实现。这样可以添加权限控制、日志等业务逻辑。

// src/controllers/assets.controller.ts
import { Body, Controller, Post, UseGuards } from '@nestjs/common';
import { ApiTags, ApiOperation, ApiBearerAuth } from '@nestjs/swagger';
import { JwtAuthGuard } from '@/guards/jwt-auth.guard';
import {
  AssetsService,
  PresignedUploadUrlRequest,
  PresignedUploadUrlResponse,
  PresignedDownloadUrlRequest,
  PresignedDownloadUrlResponse,
} from '@meta-1/nest-assets';

@ApiTags('Assets')
@Controller('api/assets')
@UseGuards(JwtAuthGuard)  // ✅ 添加权限验证
@ApiBearerAuth()
export class AssetsController {
  constructor(private readonly assetsService: AssetsService) {}

  @Post('presigned-upload-url')
  @ApiOperation({ summary: '生成预签名上传 URL' })
  async generatePresignedUploadUrl(
    @Body() request: PresignedUploadUrlRequest
  ): Promise<PresignedUploadUrlResponse> {
    return this.assetsService.generatePresignedUploadUrl(request);
  }

  @Post('presigned-download-url')
  @ApiOperation({ summary: '生成预签名下载 URL' })
  async generatePresignedDownloadUrl(
    @Body() request: PresignedDownloadUrlRequest
  ): Promise<PresignedDownloadUrlResponse> {
    return this.assetsService.generatePresignedDownloadUrl(request);
  }
}

然后在 AppModule 中注册:

@Module({
  imports: [AssetsModule],
  controllers: [AssetsController],  // ✅ 注册业务方的 Controller
})
export class AppModule {}

更多示例: 查看 CONTROLLER_EXAMPLE.md 了解如何添加权限控制、日志、限流等功能。

4. 在业务代码中使用服务

import { Injectable } from '@nestjs/common';
import { AssetsService, BucketType } from '@meta-1/nest-assets';

@Injectable()
export class MyService {
  constructor(private readonly assetsService: AssetsService) {}

  async uploadFile() {
    // 生成预签名上传 URL
    const uploadUrl = await this.assetsService.generatePresignedUploadUrl({
      fileName: 'example.jpg',
      contentType: 'image/jpeg',
      bucketType: BucketType.PUBLIC,  // 或 BucketType.PRIVATE
      prefix: 'images',  // 可选,文件路径前缀
    });

    // 返回给客户端
    return {
      uploadUrl: uploadUrl.uploadUrl,  // 客户端用此 URL 上传文件
      fileUrl: uploadUrl.fileUrl,      // 上传成功后的访问地址
      fileKey: uploadUrl.fileKey,      // 文件唯一标识
      expiresAt: uploadUrl.expiresAt,  // 过期时间
    };
  }

  async downloadFile(fileUrl: string) {
    // 生成预签名下载 URL
    // 如果是公桶,直接返回原 URL
    // 如果是私桶,返回带签名的临时 URL
    const downloadUrl = await this.assetsService.generatePresignedDownloadUrl({
      url: fileUrl,
    });

    return downloadUrl;
  }
}

5. API 请求示例

使用内置的 Controller:

# 生成上传 URL
POST /assets/presigned-upload-url
Content-Type: application/json

{
  "fileName": "example.jpg",
  "contentType": "image/jpeg",
  "bucketType": "public",
  "prefix": "images"
}

# 生成下载 URL
POST /assets/presigned-download-url
Content-Type: application/json

{
  "url": "http://meta1top:30900/wiki-private/1234567890_abc_example.jpg"
}

注意: 所有请求参数都会自动进行类型验证,Swagger UI 会显示完整的 Schema 信息。

客户端上传示例

// 1. 获取预签名上传 URL
const { uploadUrl, fileUrl } = await fetch('/assets/presigned-upload-url', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    fileName: 'example.jpg',
    contentType: 'image/jpeg',
    bucketType: 'public',
  }),
}).then(res => res.json());

// 2. 使用预签名 URL 上传文件
await fetch(uploadUrl, {
  method: 'PUT',
  headers: { 'Content-Type': 'image/jpeg' },
  body: fileBlob,
});

// 3. 上传成功后,使用 fileUrl 访问文件
console.log('文件地址:', fileUrl);

桶类型说明

模块支持两种桶类型,通过 BucketType 枚举指定,系统会自动将文件存储到对应的桶中:

  • 公桶 (BucketType.PUBLIC):

    • 文件存储到 storage.publicBucket 配置的桶中
    • 上传需要签名
    • 访问不需要签名,可直接访问
    • 适用于公开资源(如网站图片、文档等)
  • 私桶 (BucketType.PRIVATE):

    • 文件存储到 storage.privateBucket 配置的桶中
    • 上传需要签名
    • 访问需要签名,有时效性
    • 适用于私密资源(如用户私人文件、敏感数据等)

配置示例

assets:
  storage:
    provider: s3
    publicBucket: my-public-bucket    # 公共文件存储桶
    privateBucket: my-private-bucket  # 私密文件存储桶
    expiresIn: 30m

当调用 API 时指定 bucketType: "public"bucketType: "private",文件会自动存储到对应的桶中。

API 参考

AssetsService

generatePresignedUploadUrl(request)

生成预签名上传 URL。

参数:

  • fileName: 文件名
  • contentType: 文件 MIME 类型
  • bucketType: 桶类型 (BucketType.PUBLIC | BucketType.PRIVATE)
  • prefix: 可选,文件路径前缀

返回:

  • uploadUrl: 预签名上传 URL
  • fileUrl: 文件访问 URL(公桶可直接访问,私桶需要通过预签名下载 URL 访问)
  • fileKey: 文件唯一标识
  • expiresAt: 过期时间(Unix 时间戳)

generatePresignedDownloadUrl(request)

生成预签名下载 URL。会自动判断是公桶还是私桶:

  • 公桶:直接返回原 URL(不过期)
  • 私桶:返回带签名的临时 URL(有过期时间)

参数:

  • url: 文件完整 URL(从上传接口返回的 url 字段)

返回:

  • downloadUrl: 下载 URL(公桶返回原 URL,私桶返回预签名 URL)
  • expiresAt: 过期时间(0 表示不过期,仅公桶)

许可证

MIT