Chapter 34

techniques-fileupload

kisihara.c
kisihara.c
2021.06.05に更新

ファイルのアップロード

Nestでは、ファイルアップロードを処理する為に、Express用のミドルウェアパッケージ multerをベースにしたモジュールを内蔵している。Multerはmultipart/form-data形式でポストされたデータを処理する。これはHTTP POSTリクエストを介したアップロードに使用されるものだ。このモジュールはフルにカスタマイズ可能で、アプリケーションの要件に合わせて動作を調整する事ができる。

WARNING
Multerはサポートされているマルチパート形式(multipart/form-data)以外のデータを処理できない。また、このパッケージはFastifyAdapterとは互換性がない事にも注意。

方の安全性を高める為に、Multer typingsパッケージを導入しよう。

$ npm i -D @types/multer

基本的な例

単一のファイルをアップロードするには、インターセプタFileInterceptor()をルートハンドラに結びつけ、@UploadedFile()デコレータを使用してリクエストからファイルを抽出する。シンプルだ。

@Post('upload')
@UseInterceptors(FileInterceptor('file'))
uploadFile(@UploadedFile() file: Express.Multer.File) {
  console.log(file);
}

HINT
FileInterceptor()デコレータは@nestjs/platform-expressパッケージからエクスポートされる。@UploadedFile() デコレータは@nestjs/commonからエクスポートされる。

FileInterceptor()デコレータは2つの引数を取る。

  • fieldName:ファイルを格納するHTMLフォームのフィールド名を指定する文字列
  • options:省略可能。MulterOptionsタイプのオブジェクト。これはmulterのコンストラクタで使用されるオブジェクトと同じもの(詳細)。

WARNING
FileInterceptor()はGoogle Firebaseなどのサードパーティのクラウドプロバイダと互換性がない場合がある。

ファイルの配列

ファイルの配列(単一のフィールド名で識別)をアップロードするには、FilesInterceptor()デコレータを使用する(デコレータ名に複数形のFilesが含まれている事に注目)。

  • fieldName:上記通り
  • maxCount:省略可能な数値、受け入れ可能なファイル最大数を定義する
  • options:省略可能。MulterOptionsタイプのオブジェクト。上記通り。

FilesInterceptor()を使用する場合は、@UploadedFiles()デコレータを使用してrequestからファイルを抽出する。

@Post('upload')
@UseInterceptors(FilesInterceptor('files'))
uploadFile(@UploadedFiles() files: Array<Express.Multer.File>) {
  console.log(files);
}

HINT
FilesInterceptor()デコレータは@nestjs/platform-expressパッケージからエクスポートする。@UploadedFiles() @nestjs/commonパッケージからエクスポートする。

複数のファイル

複数のフィールド(全てのフィールド名のキーが異なる)をアップロードするには、FileFieldsInterceptor()デコレータを使用する。このデコレータは2つの引数を取る。

  • uploadFields:オブジェクトの配列。各オブジェクトはフィールド名を指定する文字列値を持つ必須のnameプロパティと、オプションのmaxCountプロパティを指定する。いずれも上述通りの性質。
  • options:省略可能。MulterOptionsタイプのオブジェクト。上記通り。

FileFieldsInterceptor() を使用する場合は、@UploadedFiles()デコレータを使用してリクエストからファイルを抽出する。

@Post('upload')
@UseInterceptors(FileFieldsInterceptor([
  { name: 'avatar', maxCount: 1 },
  { name: 'background', maxCount: 1 },
]))
uploadFile(@UploadedFiles() files) {
  console.log(files);
}

あらゆるファイル

任意のフィールド名キーを持つすべてのフィールドをアップロードするには、AnyFilesInterceptor()デコレータを使用する。このデコレータは前述のように省略可能なオプションオブジェクトを受け入れられる。

AnyFIlesInterceptor()を使用する場合は、@UploadedFIles()デコレータを使用して、リクエストからファイルを抽出する。

@Post('upload')
@UseInterceptors(AnyFilesInterceptor())
uploadFile(@UploadedFiles() files: Array<Express.Multer.File>) {
  console.log(files);
}

デフォルトのオプション

上記のようにファイルインターセプタではmulterオプションが使える。もしデフォルト値を設定したいなら、MulterModuleをインポートする際に、静的メソッドregister()を呼び出しサポートされている引数を渡す。ここで挙がっているすべてのオプションを使用可能。

MulterModule.register({
  dest: './upload',
});

HINT
MulterModuleクラスは@nestjs/platform-expressパッケージからエクスポートする。

非同期の設定

MulterModuleのオプションを静的に設定するのではなく、非同期に設定する必要がある場合は、registerAsync()メソッドを使う。ほとんどの動的モジュールと同様に、Nestは非同期の設定を扱う為のいくつかのテクニックを提供している。

一つはファクトリー関数を使う事だ。

MulterModule.registerAsync({
  useFactory: () => ({
    dest: './upload',
  }),
});

他のファクトリープロバイダと同様、ファクトリー関数もasyncであり、injectで依存関係をインジェクションできる。

MulterModule.registerAsync({
  imports: [ConfigModule],
  useFactory: async (configService: ConfigService) => ({
    dest: configService.getString('MULTER_DEST'),
  }),
  inject: [ConfigService],
});

他の選択肢として、以下のようにクラスを使ってMulterModuleを設定する事もできる。

MulterModule.registerAsync({
  useClass: MulterConfigService,
});

上記のコードではMulterModule内でMulterConfigServiceをインスタンス化し、それを使用して必要なオプションオブジェクトを作成している。この例では、MulterConfigServiceは以下に示すようにMulterOptionsFactoryインターフェイスを実装しなければならないことに注意。MulterModuleは、提供されたクラスのインスタンス化されたオブジェクトのcreateMulterOptions()メソッドを呼び出す。

@Injectable()
class MulterConfigService implements MulterOptionsFactory {
  createMulterOptions(): MulterModuleOptions {
    return {
      dest: './upload',
    };
  }
}

MulterModule内にprivateなコピーを作成するのではなく、既存のオプションプロバイダを再利用したい場合は、useExisting構文を使用する。

MulterModule.registerAsync({
  imports: [ConfigModule],
  useExisting: ConfigService,
});

サンプル

動くサンプルはこちら