我试图创建一个API,用于使用NestJS上传和检索图像。图片应存储在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 =>相同,无法显示。
我在这里犯什么普遍错误?
答案 0 :(得分: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}`)
})
})
}
}
答案 1 :(得分: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)
}
希望这对某人有帮助!