NestJS:图像上传和服务API

问题描述 投票:1回答:2

我试图创建一个API,以使用NestJS上传和检索图像。图像应存储在S3上。

我目前拥有的:

Controller

@Post()
@UseInterceptors(FileFieldsInterceptor([
    {name: 'photos', maxCount: 10},
]))
async uploadPhoto(@UploadedFiles() files): Promise<void> {
    await this.s3Service.savePhotos(files.photos)
}


@Get('/:id')
@Header('content-type', 'image/jpeg')
async getPhoto(@Param() params,
               @Res() res) {
    const photoId = PhotoId.of(params.id)
    const photoObject = await this.s3Service.getPhoto(photoId)
    res.send(photoObject)
}

S3Service

async savePhotos(photos: FileUploadEntity[]): Promise<any> {
    return Promise.all(photos.map(photo => {
        const filePath = `${moment().format('YYYYMMDD-hhmmss')}${Math.floor(Math.random() * (1000))}.jpg`
        const params = {
            Body: photo.buffer,
            Bucket: Constants.BUCKET_NAME,
            Key: filePath,
        }
        return new Promise((resolve) => {
            this.client.putObject(params, (err: any, data: any) => {
                if (err) {
                    logger.error(`Photo upload failed [err=${err}]`)
                    ExceptionHelper.throw(ErrorCodes.SERVER_ERROR_UNCAUGHT_EXCEPTION)
                }
                logger.info(`Photo upload succeeded [filePath=${filePath}]`)
                return resolve()
            })
        })
    }))
}

async getPhoto(photoId: PhotoId): Promise<AWS.S3.Body> {
    const object: S3.GetObjectOutput = await this.getObject(S3FileKey.of(`${Constants.S3_PHOTO_PATH}/${photoId.value}`))
        .catch(() => ExceptionHelper.throw(ErrorCodes.RESOURCE_NOT_FOUND_PHOTO)) as S3.GetObjectOutput
    logger.info(JSON.stringify(object.Body))
    return object.Body
}

async getObject(s3FilePath: S3FileKey): Promise<S3.GetObjectOutput> {
    logger.info(`Retrieving object from S3 s3FilePath=${s3FilePath.value}]`)
    return this.client.getObject({
        Bucket: Constants.BUCKET_NAME,
        Key: s3FilePath.value
    }).promise()
        .catch(err => {
            logger.error(`Could not retrieve object from S3 [err=${err}]`)
            ExceptionHelper.throw(ErrorCodes.SERVER_ERROR_UNCAUGHT_EXCEPTION)
        }) as S3.GetObjectOutput
}

照片对象实际上以S3结尾,但是当我下载它时,无法打开它。GET =>相同,无法显示。

我在这里犯了什么普遍错误?

amazon-web-services amazon-s3 nestjs
2个回答
0
投票

不确定您要返回给消费者的价值是什么,以及他们使用了哪些价值来再次获得图片;如果FQDN和路径匹配,您能否发布实际响应,请求是什么并验证?似乎您也忘记了ACL,也就是说,默认情况下,以这种方式上传的资源不是public-read

顺便说一句,您可以在此处使用aws SDK

import { Injectable } from '@nestjs/common'
import * as AWS from 'aws-sdk'
import { InjectConfig } from 'nestjs-config'
import { AwsConfig } from '../../config/aws.config'
import UploadedFile from '../interfaces/uploaded-file'

export const UPLOAD_WITH_ACL = 'public-read'

@Injectable()
export class ImageUploadService {
  s3: AWS.S3
  bucketName
  cdnUrl

  constructor(@InjectConfig() private readonly config) {
    const awsConfig = (this.config.get('aws') || { bucket: '', secretKey: '', accessKey: '', cdnUrl: '' }) as AwsConfig // read from envs
    this.bucketName = awsConfig.bucket
    this.cdnUrl = awsConfig.cdnUrl
    AWS.config.update({
      accessKeyId: awsConfig.accessKey,
      secretAccessKey: awsConfig.secretKey,
    })
    this.s3 = new AWS.S3()
  }

  upload(file: UploadedFile): Promise<string> {
    return new Promise((resolve, reject) => {
      const params: AWS.S3.Types.PutObjectRequest = {
        Bucket: this.bucketName,
        Key: `${Date.now().toString()}_${file.originalname}`,
        Body: file.buffer,
        ACL: UPLOAD_WITH_ACL,
      }
      this.s3.upload(params, (err, data: AWS.S3.ManagedUpload.SendData) => {
        if (err) {
          return reject(err)
        }
        resolve(`${this.cdnUrl}/${data.Key}`)
      })
    })
  }

}

0
投票

对于同样遇到麻烦的人,我终于明白了:

我在API网关(<your-gateway> Settings-> Binary Media Types-> */*]上启用了二进制支持,然后从lambda base64编码返回了所有响应。在将响应返回给客户端之前,API Gateway会自动进行解码。使用无服务器快递,您可以在创建服务器时轻松启用自动base64编码:

const BINARY_MIME_TYPES = [
    'application/javascript',
    'application/json',
    'application/octet-stream',
    'application/xml',
    'font/eot',
    'font/opentype',
    'font/otf',
    'image/jpeg',
    'image/png',
    'image/svg+xml',
    'text/comma-separated-values',
    'text/css',
    'text/html',
    'text/javascript',
    'text/plain',
    'text/text',
    'text/xml',
]

async function bootstrap() {
    const expressServer = express()
    const nestApp = await NestFactory.create(AppModule, new ExpressAdapter(expressServer))
    await nestApp.init()

    return serverlessExpress.createServer(expressServer, null, BINARY_MIME_TYPES)
}

在Controller中,您现在可以只返回S3响应主体:

@Get('/:id')
async getPhoto(@Param() params,
               @Res() res) {
    const photoId = PhotoId.of(params.id)
    const photoObject: S3.GetObjectOutput = await this.s3Service.getPhoto(photoId)
    res
        .set('Content-Type', 'image/jpeg')
        .send(photoObject.Body)
}

希望这对某人有帮助!

© www.soinside.com 2019 - 2024. All rights reserved.