Ich entschied mich zwischen der NestJS + Fastify- und der NestJS + Express-Plattform und entschied mich für NestJS + Fastify. Da ich die Neigung von Entwicklern in einer unverständlichen Situation kannte, zusätzliche Eigenschaften an das erforderliche Objekt in Express zu hängen und so zwischen verschiedenen Teilen der Anwendung zu kommunizieren, entschied ich fest, dass Express nicht im nächsten Projekt enthalten sein wird.
Ich musste nur ein technisches Problem mit Content-Type lösen: Multipart / Formulardaten. Außerdem wollte ich die über Content-Type: Multipart / Formulardatenanforderungen empfangenen Dateien im S3-Speicher speichern. In dieser Hinsicht hat mich die Implementierung von Anforderungen für Content-Type: Multipart / Formulardaten auf der NestJS + Express-Plattform verwirrt, dass es nicht mit Streams funktioniert.
Starten des lokalen S3-Speichers
S3 ist ein Datenspeicher (man könnte sagen, wenn auch nicht streng genommen ein Dateispeicher), auf den über das http-Protokoll zugegriffen werden kann. S3 wurde ursprünglich von AWS bereitgestellt. Die S3-API wird derzeit auch von anderen Cloud-Diensten unterstützt. Aber nicht nur. Es gibt S3-Serverimplementierungen, die Sie lokal zur Verwendung während der Entwicklung aufrufen und möglicherweise Ihre S3-Server in die Produktion bringen können.
Zunächst müssen Sie die Motivation für die Verwendung der S3-Datenspeicherung festlegen. In einigen Fällen kann dies die Kosten senken. Sie können beispielsweise den langsamsten und billigsten S3-Speicher zum Speichern von Sicherungen verwenden. Schneller Speicher mit hohem Datenverkehr (der Datenverkehr wird separat berechnet) zum Laden von Daten aus dem Speicher kostet wahrscheinlich vergleichbare Kosten wie SSD-Datenträger derselben Größe.
Ein stärkeres Motiv ist 1) Skalierbarkeit - Sie müssen nicht daran denken, dass möglicherweise nicht genügend Speicherplatz zur Verfügung steht, und 2) Zuverlässigkeit - die Server arbeiten in einem Cluster und Sie müssen nicht an Backups denken, da immer die erforderliche Anzahl von Kopien verfügbar ist.
Um die Implementierung von S3-Servern - minio - lokal zu erhöhen, müssen nur Docker und Docker-Compose auf dem Computer installiert sein. Entsprechende Datei 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
Wir starten - und ohne Probleme erhalten wir einen Cluster von 4 S3-Servern.
NestJS + Fastify + S3
Ich werde die Arbeit mit dem NestJS-Server von den ersten Schritten an beschreiben, obwohl ein Teil dieses Materials in der Dokumentation perfekt beschrieben ist. Installiert CLI NestJS:
npm install -g @nestjs/cli
Ein neues NestJS-Projekt wird erstellt:
nest new s3-nestjs-tut
Die erforderlichen Pakete sind installiert (einschließlich der für die Arbeit mit S3 erforderlichen):
npm install --save @nestjs/platform-fastify fastify-multipart aws-sdk sharp
npm install --save-dev @types/fastify-multipart @types/aws-sdk @types/sharp
Standardmäßig installiert das Projekt die NestJS + Express-Plattform. Wie Fastify zu installieren , ist in der beschriebenen docs.nestjs.com/techniques/performance Dokumentation . Zusätzlich müssen wir ein Plugin für die Verarbeitung des Inhaltstyps installieren: Multipart / Formulardaten - 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();
Jetzt beschreiben wir den Dienst, der Dateien in das S3-Repository hochlädt, nachdem der Code für die Behandlung einiger Arten von Fehlern reduziert wurde (der vollständige Text befindet sich im Artikel-Repository):
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);
}
});
}
}
Von den Merkmalen sollte beachtet werden, dass wir einen Eingabestream in zwei Ausgabestreams schreiben, wenn ein Bild geladen wird. Einer der Streams komprimiert das Bild auf eine Größe von 200 x 200. In allen Fällen wird der Arbeitsstil mit Streams verwendet. Um mögliche Fehler abzufangen und an den Controller zurückzugeben, rufen wir die Methode commit () auf, die in der Bibliothek aws-sdk definiert ist. Wir sammeln die erhaltenen Versprechen im Versprechungsarray:
const promise = s3Stream
.upload({ Bucket: 'test', Key: filename, Body: file })
.promise();
promises.push(promise);
Und außerdem erwarten wir ihre Auflösung in der Methode
Promise.all(promises).
Der Controller-Code, in dem ich noch FastifyRequest an den Dienst weiterleiten musste:
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;
}
}
Das Projekt wird gestartet:
npm run start:dev
Artikel- Repository github.com/apapacy/s3-nestjs-tut
apapacy@gmail.com
13. August 2020