【问题标题】:NestJS: Image Upload & Serve APINestJS:图像上传和服务 API
【发布时间】:2019-10-09 12:21:38
【问题描述】:

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

我目前拥有的:

控制器

@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


    【解决方案1】:

    不确定您向消费者返回了哪些值,以及他们使用哪些值再次获取图像;如果 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}`)
          })
        })
      }
    
    }
    

    【讨论】:

    • 感谢您的回答。我的问题是由于我没有为我的 API 网关启用二进制支持。上传现在可以正常工作并正确保存到 s3。 GET 仍然无法正常工作
    • 不需要 :) 我只想通过 lambda 函数检索文件
    【解决方案2】:

    对于任何有同样烦恼的人,我终于想通了:

    我在 API Gateway (&lt;your-gateway&gt; Settings -> Binary Media Types -> */*) 上启用了二进制支持,然后从 lambda base64 编码返回所有响应。 API Gateway 将在将响应返回给客户端之前自动进行解码。 使用 serverless express,您可以在创建服务器时轻松启用自动 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)
    }
    

    在控制器中,您现在可以只返回 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)
    }
    

    希望这对某人有所帮助!

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-01-14
      • 2012-12-22
      • 1970-01-01
      • 1970-01-01
      • 2022-07-25
      • 2019-07-25
      • 2021-01-20
      • 1970-01-01
      相关资源
      最近更新 更多