LoginSignup
3

More than 3 years have passed since last update.

NestJS でローカルに画像を保存する API を作成する

Last updated at Posted at 2020-08-06

事前説明

  • 画像の保存処理はデコレータで行われる
  • 画像保存等の処理には multer というモジュールが使われる
  • デコレータでは保存するファイル名の指定、保存先の指定などが行える
  • デコレータでは、形式、サイズ等によって受け取るファイルのフィルタリングが行える
  • 既存のデコレータの実装では、関数の中の処理は画像の保存後に行われる

方針

  • 保存処理はデコレータに任せ、オプションを渡してファイル名の指定、フィルタリングなどを行う
  • デコレータでは実現できない部分はファイルの保存後に追加で実施する

ひながた

これだけ書けば、POST HOST/somathing/images の file に画像を乗せることで保存自体はできるはず。

@Controller('images')
class SampleController {
    @Post()
    @UseInterceptors(FilesInterceptor('files'))
    async uploadFile(
        @UploadedFile() file: Express.Multer.File,
    ) {}
}

細かい実装

追加で必要になったモジュール

multer, @nestjs/platform-express

$ yarn install multer @nestjs/platform-express

Intercepter

@UseInterceptors() に渡すことで、保存するファイル名の指定や、フィルタリングを行える。

import { FileInterceptor } from '@nestjs/platform-express';
import { diskStorage } from 'multer';
import { BadRequestException } from '@nestjs/common';

export const customFileIntercept = (options: {
    fieldname: string;        // file が乗っている body の field
    dest: string;             // 保存したい場所
    maxFileSize: number;      // 最大画像サイズ
    fileCount: number;        // 最大画像枚数
    allowFileTypes: string[]; // 許可する画像形式
}) =>
    FileInterceptor(options.fieldname, {
        storage: diskStorage({
            destination: options.dest,
        }),
        limits: {
            fileSize: options.maxFileSize,
            files: options.fileCount,
        },
        fileFilter(
            req: any,
            file: {
                fieldname: string;
                originalname: string;
                encoding: string;
                mimetype: string;
                size: number;
                destination: string;
                filename: string;
                path: string;
                buffer: Buffer;
            },
            callback: (error: Error | null, acceptFile: boolean) => void,
        ) {
            if (!options.allowFileTypes.includes(file.mimetype))
                return callback(
                    new BadRequestException('invalid file type.'),
                    false,
                );
            return callback(null, true);
        },
    });

Controller

import {
    Post,
    UseInterceptors,
    Controller,
    UploadedFile,
    BadRequestException,
    Body,
} from '@nestjs/common';
import { PostImagesDto } from '../dto/post-images.dto';
import { PostImagesClass } from '../class/post-images.class';
import { Image } from 'src/domain/image/image.domain';
// 上で作ったもの
import { customFileIntercept } from './fileIntercepter';

@Controller('images')
export class ImagesController {
    @Post()
    @UseInterceptors(customFileIntercept({
        fieldname: 'file',
        dest: './uploads',
        maxFileSize: 200000,
        fileCount: 1,
        allowFileTypes: ['image/png'],
    }))
    async uploadFile(
        @UploadedFile() file: Express.Multer.File | undefined,
        @Body() dto: PostImagesDto,
    ): Promise<PostImagesClass> {
        if (!file) throw new BadRequestException('file field must be set.');

        // body で受け取った値を使った処理
        // db に問い合わせて、その結果ファイル名を変えるとか、ファイル削除するとか
        something(file, body)

        return new PostImagesClass(file);
    }
}

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3