NestJS + FastifyプラットフォームとNestJS + Expressプラットフォームのどちらかを選択して、NestJS + Fastifyを選択しました。Expressのreqオブジェクトに追加のプロパティをぶら下げて、アプリケーションのさまざまな部分の間で通信するという、理解できない状況での開発者の傾向を知っていたので、Expressは次のプロジェクトには含まれないと固く決心しました。
Content-Type:multipart / form-dataの技術的な問題を解決するだけで済みました。また、Content-Type:multipart / form-dataリクエストで受信したファイルをS3ストレージに保存する予定でした。この点で、NestJS + ExpressプラットフォームでのContent-Type:multipart / form-dataリクエストの実装は、ストリームでは機能しないことを私に混乱させました。
S3ローカルストレージの起動
S3は、httpプロトコルを介してアクセスできるデータストア(厳密に言えば、ファイルストアと言うかもしれません)です。 S3は元々AWSによって提供されていました。 S3 APIは現在、他のクラウドサービスでもサポートされています。しかし、それだけではありません。開発中に使用するためにローカルで起動できるS3サーバーの実装があり、場合によってはS3サーバーを本番環境に移行することもできます。
まず、S3データストレージを使用する動機を決定する必要があります。場合によっては、これによりコストを削減できます。たとえば、バックアップを保存するために、最も低速で最も安価なS3ストレージを使用できます。ストレージからデータをロードするためのトラフィックの多い高速ストレージ(トラフィックは別途課金されます)は、おそらく同じサイズのSSDドライブに匹敵するコストになります。
より強力な動機は、1)スケーラビリティ(ディスクスペースが不足する可能性があるという事実を考慮する必要がない)、および2)信頼性(サーバーはクラスター内で動作し、必要な数のコピーが常に利用可能であるため、バックアップについて考える必要がない)です。
S3サーバー(minio)の実装をローカルで上げるには、コンピューターにdockerとdocker-composeをインストールするだけで済みます。対応するdocker-compose.ymlファイル:
version: '3'
services:
minio1:
image: minio/minio:RELEASE.2020-08-08T04-50-06Z
volumes:
- ./s3/data1-1:/data1
- ./s3/data1-2:/data2
ports:
- '9001:9000'
environment:
MINIO_ACCESS_KEY: minio
MINIO_SECRET_KEY: minio123
command: server http://minio{1...4}/data{1...2}
healthcheck:
test: ['CMD', 'curl', '-f', 'http://localhost:9000/minio/health/live']
interval: 30s
timeout: 20s
retries: 3
minio2:
image: minio/minio:RELEASE.2020-08-08T04-50-06Z
volumes:
- ./s3/data2-1:/data1
- ./s3/data2-2:/data2
ports:
- '9002:9000'
environment:
MINIO_ACCESS_KEY: minio
MINIO_SECRET_KEY: minio123
command: server http://minio{1...4}/data{1...2}
healthcheck:
test: ['CMD', 'curl', '-f', 'http://localhost:9000/minio/health/live']
interval: 30s
timeout: 20s
retries: 3
minio3:
image: minio/minio:RELEASE.2020-08-08T04-50-06Z
volumes:
- ./s3/data3-1:/data1
- ./s3/data3-2:/data2
ports:
- '9003:9000'
environment:
MINIO_ACCESS_KEY: minio
MINIO_SECRET_KEY: minio123
command: server http://minio{1...4}/data{1...2}
healthcheck:
test: ['CMD', 'curl', '-f', 'http://localhost:9000/minio/health/live']
interval: 30s
timeout: 20s
retries: 3
minio4:
image: minio/minio:RELEASE.2020-08-08T04-50-06Z
volumes:
- ./s3/data4-1:/data1
- ./s3/data4-2:/data2
ports:
- '9004:9000'
environment:
MINIO_ACCESS_KEY: minio
MINIO_SECRET_KEY: minio123
command: server http://minio{1...4}/data{1...2}
healthcheck:
test: ['CMD', 'curl', '-f', 'http://localhost:9000/minio/health/live']
interval: 30s
timeout: 20s
retries: 3
開始します-そして問題なく、4つのS3サーバーのクラスターを取得します。
NestJS + Fastify + S3
NestJSサーバーの操作については、最初のステップから説明しますが、この資料の一部はドキュメントで完全に説明されています。CLINestJSをインストールします。
npm install -g @nestjs/cli
新しいNestJSプロジェクトが作成されます。
nest new s3-nestjs-tut
必要なパッケージがインストールされます(S3での作業に必要なパッケージを含む)。
npm install --save @nestjs/platform-fastify fastify-multipart aws-sdk sharp
npm install --save-dev @types/fastify-multipart @types/aws-sdk @types/sharp
デフォルトでは、プロジェクトはNestJS + Expressプラットフォームをインストールします。Fastifyのインストール方法については、docs.nestjs.com / techniques / performanceのドキュメントで説明されています。さらに、Content-Typeを処理するためのプラグインをインストールする必要があります:multipart / form-data --fastify-multipart
import { NestFactory } from '@nestjs/core';
import {
FastifyAdapter,
NestFastifyApplication,
} from '@nestjs/platform-fastify';
import fastifyMultipart from 'fastify-multipart';
import { AppModule } from './app.module';
async function bootstrap() {
const fastifyAdapter = new FastifyAdapter();
fastifyAdapter.register(fastifyMultipart, {
limits: {
fieldNameSize: 1024, // Max field name size in bytes
fieldSize: 128 * 1024 * 1024 * 1024, // Max field value size in bytes
fields: 10, // Max number of non-file fields
fileSize: 128 * 1024 * 1024 * 1024, // For multipart forms, the max file size
files: 2, // Max number of file fields
headerPairs: 2000, // Max number of header key=>value pairs
},
});
const app = await NestFactory.create<NestFastifyApplication>(
AppModule,
fastifyAdapter,
);
await app.listen(3000, '127.0.0.1');
}
bootstrap();
ここで、ファイルをS3リポジトリにアップロードするサービスについて説明し、いくつかのタイプのエラーを処理するためのコードを削減しました(全文は記事リポジトリにあります)。
import { Injectable, HttpException, BadRequestException } from '@nestjs/common';
import { S3 } from 'aws-sdk';
import fastify = require('fastify');
import { AppResponseDto } from './dto/app.response.dto';
import * as sharp from 'sharp';
@Injectable()
export class AppService {
async uploadFile(req: fastify.FastifyRequest): Promise<any> {
const promises = [];
return new Promise((resolve, reject) => {
const mp = req.multipart(handler, onEnd);
function onEnd(err) {
if (err) {
reject(new HttpException(err, 500));
} else {
Promise.all(promises).then(
data => {
resolve({ result: 'OK' });
},
err => {
reject(new HttpException(err, 500));
},
);
}
}
function handler(field, file, filename, encoding, mimetype: string) {
if (mimetype && mimetype.match(/^image\/(.*)/)) {
const imageType = mimetype.match(/^image\/(.*)/)[1];
const s3Stream = new S3({
accessKeyId: 'minio',
secretAccessKey: 'minio123',
endpoint: 'http://127.0.0.1:9001',
s3ForcePathStyle: true, // needed with minio?
signatureVersion: 'v4',
});
const promise = s3Stream
.upload(
{
Bucket: 'test',
Key: `200x200_${filename}`,
Body: file.pipe(
sharp()
.resize(200, 200)
[imageType](),
),
}
)
.promise();
promises.push(promise);
}
const s3Stream = new S3({
accessKeyId: 'minio',
secretAccessKey: 'minio123',
endpoint: 'http://127.0.0.1:9001',
s3ForcePathStyle: true, // needed with minio?
signatureVersion: 'v4',
});
const promise = s3Stream
.upload({ Bucket: 'test', Key: filename, Body: file })
.promise();
promises.push(promise);
}
});
}
}
機能のうち、画像がロードされる場合、入力ストリームを2つの出力ストリームに書き込むことに注意してください。ストリームの1つは、画像を200x200のサイズに圧縮します。いずれの場合も、ストリームを使用した作業スタイルが使用されます。ただし、発生する可能性のあるエラーをキャッチしてコントローラーに返すために、aws-sdkライブラリで定義されているpromise()メソッドを呼び出します。受信したpromiseをpromise配列に蓄積します。
const promise = s3Stream
.upload({ Bucket: 'test', Key: filename, Body: file })
.promise();
promises.push(promise);
そして、さらに、メソッドでの解決を期待してい
Promise.all(promises)ます。
FastifyRequestをサービスに転送する必要があったコントローラーコード:
import { Controller, Post, Req } from '@nestjs/common';
import { AppService } from './app.service';
import { FastifyRequest } from 'fastify';
@Controller()
export class AppController {
constructor(private readonly appService: AppService) {}
@Post('/upload')
async uploadFile(@Req() req: FastifyRequest): Promise<any> {
const result = await this.appService.uploadFile(req);
return result;
}
}
プロジェクトが開始されます:
npm run start:dev
記事 リポジトリgithub.com/apapacy/s3-nestjs-tutapapacy@gmail.com2020年 8月13日