Skip to content

上传文件到aws 的s3

使用nodejs 生成上传的预签名,然后浏览器根据预签名上传文件到s3,我使用的nestjs框架

nestjs 代码示例

controller

js
@Controller('aws')
export class AwsController {
  constructor(private readonly awsService: AwsService) {}

  @Post('presignedUrl')
  async getPresignedUrl(
    @Body() body: GetPresignedUrlDto,
  ): Promise<{ url: string }> {
    const url = await this.awsService.generatePresignedUrl(body);
    return { url };
  }
}

service

js
import { S3Client, PutObjectCommand } from '@aws-sdk/client-s3';
import { getSignedUrl } from '@aws-sdk/s3-request-presigner';
import { ConfigService } from '@nestjs/config';
import { GetPresignedUrlDto } from './dto/aws.dto';

@Injectable()
export class AwsService {
  private readonly s3Client: S3Client;
  private readonly bucketName: string;
  constructor(private readonly configService: ConfigService) {
    this.s3Client = new S3Client({
      region: configService.get('AWS_REGION'), // 你的 AWS 区域
      credentials: {
        accessKeyId: configService.get('AWS_ACCESS_KEY_ID'), // 你的 AWS 访问密钥 ID
        secretAccessKey: configService.get('AWS_SECRET_ACCESS_KEY'), // 你的 AWS 密钥
      },
    });
    this.bucketName = configService.get('AWS_BUCKET_NAME'); // 你的 S3 存储桶名称
  }

  async generatePresignedUrl(body: GetPresignedUrlDto) {
    const expiresInSeconds: number = 600;
    const command = new PutObjectCommand({
      Bucket: this.bucketName,
      Key: body.key,
      ContentType: body.contentType, // 设置 Content-Type,非常重要!
    });

    try {
      const url = await getSignedUrl(this.s3Client, command, {
        expiresIn: expiresInSeconds,
      });
      return url;
    } catch (error) {
      console.error('Error generating pre-signed URL', error);
      throw error;
    }
  }
}

aws.dto

js
import { IsNotEmpty, IsString } from 'class-validator';

export class GetPresignedUrlDto {
  @IsNotEmpty({ message: 'Key is required' })
  @IsString({ message: 'Key must be a string' })
  key: string;

  @IsNotEmpty({ message: 'contentType is required' })
  @IsString({ message: 'contentType must be a string' })
  contentType: string;
}

vue上传文件

注意不要用FormData上传,postman 选择 binary

js
import { v4 as uuidv4 } from 'uuid'
import axios from 'axios'
async function uploadImgFun (file) {
  const formObj = form.value
  formObj.loading = true
  form.value.img = ''
  try {
    const key = uuidv4()
    const params = {
      key,
      contentType: file.file.type
    }
    const { data } = await getAwsPresignedUrl(params)
    let url = data?.url.replace('https://xxxx.s3.us-east-1.amazonaws.com/', '/uploadS3/') //替换自己预签名url
    await axios({
      method: 'put',
      url,
      headers: {
        'Content-Type': file.file.type
      },
      data: file.file,
      timeout: 5 * 60 * 1000,
    })
    file.onSuccess()
    form.value.img = `https://xxxxx.cloudfront.net/${key}` // 对应的s3域名或者 cloudfront域名
  } catch (err) {
    file.onError()
    throw err
  } finally {
    formObj.loading = false
  }
}

vite.config.js

js
['/uploadS3']: {
  target: 'https://xxxx.s3.us-east-1.amazonaws.com',//替换自己的预签名地址,为了解决跨域
  changeOrigin: true,
  rewrite: (path) => path.replace(new RegExp(`^/uploadS3`), '')
},

备案号:豫ICP备17017964号