NestJS. Mengupload file ke penyimpanan S3 (minio)

NestJS adalah kerangka kerja untuk membangun aplikasi sisi server yang efisien dan dapat diskalakan di platform Node.js. Anda mungkin menemukan klaim bahwa NestJS adalah kerangka kerja platform independen. Artinya dapat bekerja berdasarkan salah satu dari dua framework pilihan Anda: NestJS + Express atau NestJS + Fastify. Ini benar-benar begitu, atau hampir begitu. Independensi platform ini diakhiri dengan penanganan Content-Type: permintaan data multipart / form-data. Artinya, praktis pada hari kedua pembangunan. Dan ini bukan masalah besar jika Anda menggunakan platform NestJS + Express - ada contoh cara kerja Content-Type: multipart / form-data dalam dokumentasi. Tidak ada contoh seperti itu untuk NestJS + Fastify, dan tidak banyak contoh di internet. Dan beberapa dari contoh ini mengikuti jalur yang sangat rumit.



Memilih antara platform NestJS + Fastify dan NestJS + Express, saya membuat pilihan terhadap NestJS + Fastify. Mengetahui kecenderungan pengembang dalam situasi apa pun yang tidak dapat dipahami untuk menggantung properti tambahan pada objek req di Express dan dengan demikian berkomunikasi di antara berbagai bagian aplikasi, saya dengan tegas memutuskan bahwa Express tidak akan ada di proyek berikutnya.



Saya hanya perlu menyelesaikan masalah teknis dengan Content-Type: multipart / form-data. Juga, saya berencana untuk menyimpan file yang diterima melalui Content-Type: permintaan data multipart / form-data di penyimpanan S3. Dalam hal ini, implementasi permintaan Content-Type: multipart / form-data pada platform NestJS + Express membuat saya bingung karena itu tidak berfungsi dengan aliran.



Meluncurkan Penyimpanan Lokal S3



S3 adalah penyimpanan data (bisa dikatakan, meskipun tidak tegasnya, penyimpanan file) dapat diakses melalui protokol http. S3 awalnya disediakan oleh AWS. API S3 saat ini juga didukung oleh layanan cloud lainnya. Tapi tidak hanya itu. Ada implementasi server S3 yang dapat Anda gunakan secara lokal untuk digunakan selama pengembangan, dan mungkin mengaktifkan dan menjalankan server S3 Anda dalam produksi.



Pertama, Anda perlu memutuskan motivasi untuk menggunakan penyimpanan data S3. Dalam beberapa kasus, ini dapat mengurangi biaya. Misalnya, Anda dapat menggunakan penyimpanan S3 paling lambat dan termurah untuk menyimpan cadangan. Penyimpanan cepat dengan lalu lintas tinggi (lalu lintas dibebankan secara terpisah) untuk memuat data dari penyimpanan mungkin akan berharga sebanding dengan drive SSD dengan ukuran yang sama.



Motif yang lebih kuat adalah 1) skalabilitas - Anda tidak perlu memikirkan fakta bahwa ruang disk mungkin habis, dan 2) keandalan - server bekerja dalam cluster dan Anda tidak perlu memikirkan tentang cadangan, karena jumlah salinan yang diperlukan selalu tersedia.



Untuk meningkatkan implementasi server S3 - minio - secara lokal Anda hanya perlu docker dan docker-compose yang diinstal di komputer. File docker-compose.yml yang sesuai:



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


Kami memulai - dan tanpa masalah kami mendapatkan sekelompok 4 server S3.



NestJS + Fastify + S3



Saya akan menjelaskan cara bekerja dengan server NestJS dari langkah pertama, meskipun beberapa materi ini dijelaskan dengan sempurna dalam dokumentasi. Instal CLI NestJS:



npm install -g @nestjs/cli


Proyek NestJS baru dibuat:



nest new s3-nestjs-tut


Paket yang diperlukan diinstal (termasuk yang diperlukan untuk bekerja dengan S3):




npm install --save @nestjs/platform-fastify fastify-multipart aws-sdk sharp
npm install --save-dev @types/fastify-multipart  @types/aws-sdk @types/sharp


Secara default, proyek menginstal platform NestJS + Express. Cara menginstal Fastify dijelaskan dalam dokumentasi docs.nestjs.com/techniques/performance . Selain itu, kita perlu menginstal plugin untuk menangani 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();


Sekarang kami akan menjelaskan layanan yang mengunggah file ke repositori S3, setelah mengurangi kode untuk menangani beberapa jenis kesalahan (teks lengkap ada di repositori artikel):



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);
      }
    });
  }
}


Dari fitur-fiturnya, perlu dicatat bahwa kita menulis aliran input menjadi dua aliran output jika gambar dimuat. Salah satu aliran memampatkan gambar menjadi ukuran 200x200. Dalam semua kasus, gaya aliran digunakan. Tetapi untuk menangkap kemungkinan kesalahan dan mengembalikannya ke pengontrol, kita memanggil metode promise (), yang didefinisikan dalam pustaka aws-sdk. Kami mengakumulasi janji yang diterima dalam larik promise:



        const promise = s3Stream
          .upload({ Bucket: 'test', Key: filename, Body: file })
          .promise();
        promises.push(promise);


Dan, selanjutnya, kami mengharapkan resolusi mereka dalam metode tersebut Promise.all(promises).



Kode pengontrol, di mana saya masih harus meneruskan FastifyRequest ke layanan:



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;
  }
}


Proyek diluncurkan:



npm run start:dev


Repositori artikel github.com/apapacy/s3-nestjs-tut



apapacy@gmail.com

13 Agustus 2020



All Articles