Поскольку nestjs является экспресс-приложением, можно использовать любую библиотеку для обработки загрузки с использованием nest, и, поскольку она предоставляет Midlewares, также можно использовать multer. Мой вопрос: как лучше всего обрабатывать загрузку файлов с помощью nestjs?

7
Victor Ivens 4 Мар 2018 в 16:43

4 ответа

Лучший ответ

Как сообщает @Kamyl по вопросу https://github.com/nestjs/nest/issues/262, поскольку v4.6.0 можно загружать файлы, используя multer, в nestjs, используя общий перехватчик файлов.

import { ... , UseInterceptors, FileInterceptor, UploadedFile } from '@nestjs/common'

... 

@UseInterceptors(FileInterceptor('file'))
async upload( @UploadedFile() file) {
  console.log(file)
}

Таким образом, переменная file будет иметь buffer


Использование параметров Multer

Также в качестве первого параметра необходимо указать имя поля, а затем массив с параметрами Multer.

import { ... , UseInterceptors, FileInterceptor, UploadedFile } from '@nestjs/common'
import { diskStorage } from 'multer'
import { extname } from 'path'

...

@UseInterceptors(FileInterceptor('file', {
  storage: diskStorage({
    destination: './uploads'
    , filename: (req, file, cb) => {
      // Generating a 32 random chars long string
      const randomName = Array(32).fill(null).map(() => (Math.round(Math.random() * 16)).toString(16)).join('')
      //Calling the callback passing the random name generated with the original extension name
      cb(null, `${randomName}${extname(file.originalname)}`)
    }
  })
}))
async upload( @UploadedFile() file) {
  console.log(file)
}

Таким образом, переменная file будет иметь filename, destination и path.

Параметр destination из diskStorage также может быть функцией с параметрами и ожидающим обратным вызовом, аналогичным filename. При передаче diskStorage файл будет автоматически сохранен в месте назначения с указанным именем файла.

Также возможно обрабатывать несколько файлов, используя @UploadedFiles и FilesInterceptor (множественное число)

15
Victor Ivens 4 Мар 2018 в 13:43

Более чистым способом было бы извлечь конфигурации в отдельный файл и затем вызвать его внутри метода перехватчика.

import { extname } from 'path';
import { existsSync, mkdirSync } from 'fs';
import { diskStorage } from 'multer';
import { v4 as uuid } from 'uuid';
import { HttpException, HttpStatus } from '@nestjs/common';

// Multer configuration
export const multerConfig = {
    dest: process.env.UPLOAD_LOCATION,
};

// Multer upload options
export const multerOptions = {
    // Enable file size limits
    limits: {
        fileSize: +process.env.MAX_FILE_SIZE,
    },
    // Check the mimetypes to allow for upload
    fileFilter: (req: any, file: any, cb: any) => {
        if (file.mimetype.match(/\/(jpg|jpeg|png|gif)$/)) {
            // Allow storage of file
            cb(null, true);
        } else {
            // Reject file
            cb(new HttpException(`Unsupported file type ${extname(file.originalname)}`, HttpStatus.BAD_REQUEST), false);
        }
    },
    // Storage properties
    storage: diskStorage({
        // Destination storage path details
        destination: (req: any, file: any, cb: any) => {
            const uploadPath = multerConfig.dest;
            // Create folder if doesn't exist
            if (!existsSync(uploadPath)) {
                mkdirSync(uploadPath);
            }
            cb(null, uploadPath);
        },
        // File modification details
        filename: (req: any, file: any, cb: any) => {
            // Calling the callback passing the random name generated with the original extension name
            cb(null, `${uuid()}${extname(file.originalname)}`);
        },
    }),
};

А затем назвать его под перехватчиком, как так

import { ... , UseInterceptors, FileInterceptor, UploadedFile } from '@nestjs/common'
import { diskStorage } from 'multer'
import { extname } from 'path'
import { multerOptions } from 'src/config/multer.config';
...

@Post('/action/upload')
@UseInterceptors(FileInterceptor('file', multerOptions))
async upload( @UploadedFile() file) {
  console.log(file)
}
6
Sandeep K Nair 24 Янв 2019 в 09:16

Простой способ - использовать контроллеры. Вам нужно определить контроллер загрузки и добавить его в свой app.module, это пример того, каким должен быть контроллер (бэкэнд):

@Controller()
export class Uploader {
  @Post('sampleName')
  @UseInterceptors(FileInterceptor('file'))
  uploadFile(@UploadedFile() file) {
  // file name selection 
    const path = `desired path`;
    const writeStream = fs.createWriteStream(path);  
    writeStream.write(file.buffer);
    writeStream.end();
    return {
      result: [res],
    };
  }
}

И позвоните своему контроллеру при помощи fetch:

    fetch('controller address', {
          method: 'POST',
          body: data,
        })
          .then((response) => response.json())
          .then((success) => {
            // What to do when succeed 
});
          })
          .catch((error) => console.log('Error in uploading file: ', error));
-1
Alex Trn 5 Мар 2019 в 13:37

Чистая реализация с использованием параметров Multer (включая импорт)

Спасибо @VictorIvens за лучший ответ из всех.

Тем не менее, я нашел следующие проблемы в коде.

  • импорт с именем FileInterceptor не существует в пакете @nestjs/common в последней версии NestJS.
  • код, который использует параметры Multer, чтобы быть слишком загроможденным для моих глаз.

Итак, чтобы упростить ситуацию, я пришел к следующему решению.

import {
  Controller,
  Post,
  UseInterceptors,
  UploadedFile
} from "@nestjs/common";
import { FileInterceptor } from "@nestjs/platform-express";

import { diskStorage } from "multer";
import { extname } from "path";

// You may want to move this variable into a separate file then import it to make it cleaner
const storageOptions = diskStorage({
  destination: "./uploads",
  filename: (req, file, callback) => {
    callback(null, generateFilename(file));
  }
});

// You may want to move this function into a separate file then import it to make it cleaner
function generateFilename(file) {
  return `${Date.now()}.${extname(file.originalname)}`;
}

@Controller()
export class YourController {
  @Post("upload") // API path
  @UseInterceptors(
    FileInterceptor(
      "file", // name of the field being passed
      {
        storage: storageOptions
      }
    )
  )
  async upload(@UploadedFile() file) {
    return file;
  }
}
3
Nechar Joshi 28 Дек 2019 в 10:24