|
|
@@ -41,100 +41,100 @@ export class S3Controller {
|
|
|
});
|
|
|
}
|
|
|
|
|
|
- @Get('image/stream')
|
|
|
- @ApiQuery({
|
|
|
- name: 'key',
|
|
|
- required: true,
|
|
|
- description: '必须以 img/ 开头的 key',
|
|
|
- })
|
|
|
- @ApiResponse({ status: 200, description: '返回 base64 解码的图片 buffer' })
|
|
|
- async streamImage(@Query('key') key: string, @Res() res: FastifyReply) {
|
|
|
- this.validateKey(key, 'img/');
|
|
|
-
|
|
|
- const { url } = await this.s3Service.getSignedReadUrl(key);
|
|
|
- const response = await fetch(url);
|
|
|
-
|
|
|
- if (!response.ok) {
|
|
|
- throw new BadRequestException('S3 响应失败');
|
|
|
- }
|
|
|
-
|
|
|
- const base64Text = await response.text();
|
|
|
- const [, mimeType, base64Data] =
|
|
|
- base64Text.match(/^data:(image\/[a-zA-Z]+);base64,(.+)$/) || [];
|
|
|
-
|
|
|
- if (!mimeType || !base64Data) {
|
|
|
- throw new BadRequestException('无效的 base64 图片内容');
|
|
|
- }
|
|
|
-
|
|
|
- const buffer = Buffer.from(base64Data, 'base64');
|
|
|
- res.header('Content-Type', mimeType).send(buffer);
|
|
|
- }
|
|
|
-
|
|
|
- @Get('image/chunked')
|
|
|
- @ApiQuery({
|
|
|
- name: 'key',
|
|
|
- required: true,
|
|
|
- description: '必须以 img/ 开头的 key',
|
|
|
- })
|
|
|
- @ApiResponse({ status: 200, description: '以 stream 方式返回图片' })
|
|
|
- async chunkImage(@Query('key') key: string, @Res() res: FastifyReply) {
|
|
|
- this.validateKey(key, 'img/');
|
|
|
-
|
|
|
- try {
|
|
|
- const { url } = await this.s3Service.getSignedReadUrl(key);
|
|
|
- const response = await fetch(url);
|
|
|
-
|
|
|
- if (!response.ok || !response.body) {
|
|
|
- throw new BadRequestException('S3 响应失败');
|
|
|
- }
|
|
|
-
|
|
|
- const contentType = response.headers.get('Content-Type') || 'image/jpeg';
|
|
|
- res
|
|
|
- .header('Content-Type', contentType)
|
|
|
- .header('Cache-Control', 'public, max-age=31536000');
|
|
|
-
|
|
|
- return res.send(response.body);
|
|
|
- } catch (err) {
|
|
|
- this.logger.error(`Chunked image stream failed for key: ${key}`, err);
|
|
|
- throw new BadRequestException('图片读取失败');
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- @Get('image/cruise')
|
|
|
- @ApiQuery({
|
|
|
- name: 'key',
|
|
|
- required: true,
|
|
|
- description: '必须以 img/ 开头的 key',
|
|
|
- })
|
|
|
- @ApiResponse({ status: 200, description: '返回巡游图片 (buffer)' })
|
|
|
- async streamCruiseImage(@Query('key') key: string, @Res() res: FastifyReply) {
|
|
|
- this.validateKey(key, 'img/');
|
|
|
-
|
|
|
- try {
|
|
|
- const { url } = await this.s3Service.getSignedReadUrl(key);
|
|
|
- const response = await fetch(url);
|
|
|
-
|
|
|
- this.logger.log(
|
|
|
- `Fetching cruise image from S3: ${url} — ${response.status}`,
|
|
|
- );
|
|
|
-
|
|
|
- if (!response.ok) {
|
|
|
- throw new BadRequestException('S3 响应失败');
|
|
|
- }
|
|
|
-
|
|
|
- const arrayBuffer = await response.arrayBuffer();
|
|
|
- const buffer = Buffer.from(arrayBuffer);
|
|
|
-
|
|
|
- res.header(
|
|
|
- 'Content-Type',
|
|
|
- response.headers.get('Content-Type') || 'image/jpeg',
|
|
|
- );
|
|
|
- return res.send(buffer);
|
|
|
- } catch (err) {
|
|
|
- this.logger.error(`Cruise image fetch failed for key: ${key}`, err);
|
|
|
- throw new BadRequestException('获取图片失败');
|
|
|
- }
|
|
|
- }
|
|
|
+ // @Get('image/stream')
|
|
|
+ // @ApiQuery({
|
|
|
+ // name: 'key',
|
|
|
+ // required: true,
|
|
|
+ // description: '必须以 img/ 开头的 key',
|
|
|
+ // })
|
|
|
+ // @ApiResponse({ status: 200, description: '返回 base64 解码的图片 buffer' })
|
|
|
+ // async streamImage(@Query('key') key: string, @Res() res: FastifyReply) {
|
|
|
+ // this.validateKey(key, 'img/');
|
|
|
+
|
|
|
+ // const { url } = await this.s3Service.getSignedReadUrl(key);
|
|
|
+ // const response = await fetch(url);
|
|
|
+
|
|
|
+ // if (!response.ok) {
|
|
|
+ // throw new BadRequestException('S3 响应失败');
|
|
|
+ // }
|
|
|
+
|
|
|
+ // const base64Text = await response.text();
|
|
|
+ // const [, mimeType, base64Data] =
|
|
|
+ // base64Text.match(/^data:(image\/[a-zA-Z]+);base64,(.+)$/) || [];
|
|
|
+
|
|
|
+ // if (!mimeType || !base64Data) {
|
|
|
+ // throw new BadRequestException('无效的 base64 图片内容');
|
|
|
+ // }
|
|
|
+
|
|
|
+ // const buffer = Buffer.from(base64Data, 'base64');
|
|
|
+ // res.header('Content-Type', mimeType).send(buffer);
|
|
|
+ // }
|
|
|
+
|
|
|
+ // @Get('image/chunked')
|
|
|
+ // @ApiQuery({
|
|
|
+ // name: 'key',
|
|
|
+ // required: true,
|
|
|
+ // description: '必须以 img/ 开头的 key',
|
|
|
+ // })
|
|
|
+ // @ApiResponse({ status: 200, description: '以 stream 方式返回图片' })
|
|
|
+ // async chunkImage(@Query('key') key: string, @Res() res: FastifyReply) {
|
|
|
+ // this.validateKey(key, 'img/');
|
|
|
+
|
|
|
+ // try {
|
|
|
+ // const { url } = await this.s3Service.getSignedReadUrl(key);
|
|
|
+ // const response = await fetch(url);
|
|
|
+
|
|
|
+ // if (!response.ok || !response.body) {
|
|
|
+ // throw new BadRequestException('S3 响应失败');
|
|
|
+ // }
|
|
|
+
|
|
|
+ // const contentType = response.headers.get('Content-Type') || 'image/jpeg';
|
|
|
+ // res
|
|
|
+ // .header('Content-Type', contentType)
|
|
|
+ // .header('Cache-Control', 'public, max-age=31536000');
|
|
|
+
|
|
|
+ // return res.send(response.body);
|
|
|
+ // } catch (err) {
|
|
|
+ // this.logger.error(`Chunked image stream failed for key: ${key}`, err);
|
|
|
+ // throw new BadRequestException('图片读取失败');
|
|
|
+ // }
|
|
|
+ // }
|
|
|
+
|
|
|
+ // @Get('image/cruise')
|
|
|
+ // @ApiQuery({
|
|
|
+ // name: 'key',
|
|
|
+ // required: true,
|
|
|
+ // description: '必须以 img/ 开头的 key',
|
|
|
+ // })
|
|
|
+ // @ApiResponse({ status: 200, description: '返回巡游图片 (buffer)' })
|
|
|
+ // async streamCruiseImage(@Query('key') key: string, @Res() res: FastifyReply) {
|
|
|
+ // this.validateKey(key, 'img/');
|
|
|
+
|
|
|
+ // try {
|
|
|
+ // const { url } = await this.s3Service.getSignedReadUrl(key);
|
|
|
+ // const response = await fetch(url);
|
|
|
+
|
|
|
+ // this.logger.log(
|
|
|
+ // `Fetching cruise image from S3: ${url} — ${response.status}`,
|
|
|
+ // );
|
|
|
+
|
|
|
+ // if (!response.ok) {
|
|
|
+ // throw new BadRequestException('S3 响应失败');
|
|
|
+ // }
|
|
|
+
|
|
|
+ // const arrayBuffer = await response.arrayBuffer();
|
|
|
+ // const buffer = Buffer.from(arrayBuffer);
|
|
|
+
|
|
|
+ // res.header(
|
|
|
+ // 'Content-Type',
|
|
|
+ // response.headers.get('Content-Type') || 'image/jpeg',
|
|
|
+ // );
|
|
|
+ // return res.send(buffer);
|
|
|
+ // } catch (err) {
|
|
|
+ // this.logger.error(`Cruise image fetch failed for key: ${key}`, err);
|
|
|
+ // throw new BadRequestException('获取图片失败');
|
|
|
+ // }
|
|
|
+ // }
|
|
|
|
|
|
@Get('video/play')
|
|
|
@ApiQuery({
|
|
|
@@ -160,15 +160,15 @@ export class S3Controller {
|
|
|
return this.s3Service.getSignedReadUrl(key);
|
|
|
}
|
|
|
|
|
|
- @Get('pdf/view')
|
|
|
- @ApiQuery({
|
|
|
- name: 'key',
|
|
|
- required: true,
|
|
|
- description: '必须以 pdf/ 开头的 key',
|
|
|
- })
|
|
|
- @ApiResponse({ status: 200, description: '返回 PDF 文件的签名 URL' })
|
|
|
- async getSignedPdf(@Query('key') key: string) {
|
|
|
- this.validateKey(key, 'pdf/');
|
|
|
- return this.s3Service.getSignedReadUrl(key);
|
|
|
- }
|
|
|
+ // @Get('pdf/view')
|
|
|
+ // @ApiQuery({
|
|
|
+ // name: 'key',
|
|
|
+ // required: true,
|
|
|
+ // description: '必须以 pdf/ 开头的 key',
|
|
|
+ // })
|
|
|
+ // @ApiResponse({ status: 200, description: '返回 PDF 文件的签名 URL' })
|
|
|
+ // async getSignedPdf(@Query('key') key: string) {
|
|
|
+ // this.validateKey(key, 'pdf/');
|
|
|
+ // return this.s3Service.getSignedReadUrl(key);
|
|
|
+ // }
|
|
|
}
|