nodejs

파일 업로드 (memoryStorage)

PJH 2022. 3. 15. 17:59

https://www.npmjs.com/package/multer

 

multer

Middleware for handling `multipart/form-data`.. Latest version: 1.4.4, last published: 3 months ago. Start using multer in your project by running `npm i multer`. There are 3171 other projects in the npm registry using multer.

www.npmjs.com

 

multer는 node.js에서 파일 업로드 / 다운로드 처리를 할 수 있는 기능을 제공해주는 모듈이다.

 

사용자의 파일을 서버에 업로드하고자 한다면 다음과 같은 코드를 작성할 수 있을 것이다.

import multer from 'multer';
...
const storage = multer.diskStorage({

  // callback을 통해 입력 파일을 저장할 디렉터리 설정
  destination(req, file, callback) {
    callback(null, 'uploadedFiles/');
  },

  // callback을 통해 입력 파일의 파일명 설정
  filename(req, file, callback) {
    callback(null, `${Date.now()}__${file.originalname}`);
  },
})
const upload = multer({storage:storage});

router.post("/upload", upload.array('img'), async (req, res, next)=>{
  ...
});
...

upload.array('img') 의 img는 파일을 받을 키값을 의미한다.

사용자가 파일을 업로드하고 콘솔에 파일을 찍으면 파일정보는 다음과 같이 나올 것이다.

{
  fieldname: 'img',
  originalname: 'star1.png',
  encoding: '7bit',
  mimetype: 'image/png',
  destination: 'uploaded/',
  filename: 'star1.png',
  path: 'uploaded\\star1.png',
  size: 1029
}

 

위의 fieldname을 보면 'img'라고 나와 있다.

 

클라이언트에서 서버로 파일 전송 시, 키 값이 img인 파일만 받아서 처리한다는 뜻이다.

 

또한 파일이 저장될 경로(destination)와 최종경로(path)까지 표시된다.

 

 

multer는 파일정보를 memory에 저장하는 것이 가능하다.

import multer from 'multer';

// diskStorage보다 코드가 더 간단하다.
const upload = multer({storage : multer.memoryStorage()});

router.post("/upload", upload.array('img'), async (req, res, next)=>{
  ...
})
...

memoryStorage에 보관된 파일의 정보를 출력하면 아래처럼 나온다.

 

{
  fieldname: 'img',
  originalname: 'star1.png',
  encoding: '7bit',
  mimetype: 'image/png',
  buffer: <Buffer 89 50 4e 47 0d ... 979 more bytes>,
  size: 1029
}

memoryStorage를 이용하면 파일이 저장될 경로를 입력에따라 동적 지정이 가능하다.

 

그러나, memoryStorage는 fs 모듈로 읽어올 수 없다.

 

 

memoryStroage로 동적 경로를 지정하기 위해서는 다음 모듈이 있어야 한다.

 

 

https://www.npmjs.com/package/streamifier

 

streamifier

Converts a Buffer/String into a readable stream. Latest version: 0.1.1, last published: 7 years ago. Start using streamifier in your project by running `npm i streamifier`. There are 242 other projects in the npm registry using streamifier.

www.npmjs.com

 

위 모듈은 createReadStream() 함수만 가지고 있다.

 

Q. fs 에도 위 함수가 있지 않은가?

fs 모듈에도 createReadStream을 지원하고 있다.

그러나 이 함수는 디렉터리 상의 존재하는 파일만 읽을 수 있다.

 

때문에, fs 모듈로는 메모리 상의 파일을 읽어올 수 없다.

 

 

 

아래의 코드를 살펴보자.

import fs from 'fs';
import multer from 'multer';
...

const upload = multer({storage : multer.memoryStorage()});
router.post("/upload", upload.array('img'), async (req, res, next)=>{
  const file = req.files[0];
  console.log(file);
  const x = fs.createWriteStream("./uploaded/image.png");
  fs.createReadStream(file.buffer).pipe(x);
  ...
});
...

위 코드에서 memoryStorage에 저장된 파일 정보를 읽어

uploaded 폴더에 image.png 형태의 파일이 생성되기를 기대한다.

 

당연히 사용자의 입력 파일도 image 파일이다.

그러나, 위 코드는 오류가 발생한다.

 

The "src" argument must be of type string or an instance of Buffer or URL

위 오류를 통해 fs에서 재공해주는 createReadStream과 createWriteStream은 파일의 경로를 명시해야 한다고 볼 수 있다.

실제로 두 함수의 파라미터를 확인해보면 path를 받고 있는 것을 알 수 있다.

 

fs 모듈 하나로는 memoryStorage의 파일을 받아올 수 없는 상황이다.

위 이슈는 streamifier의 createReadStream 함수가 해결해준다.

 

아래 코드를 살펴보자.

import multer from 'multer';
...
const upload = multer({storage : multer.memoryStorage()});
router.post("/upload", upload.array('img'), async (req, res, next)=>{
  const file = req.files[0];
  console.log(file);
  
  // 파일을 쓸 경로를 명시
  const stream = fs.createWriteStream("./uploaded/image.png");
  
  // 파일 데이터를 파일을 쓸 대상에 입력
  streamifier.createReadStream(file.buffer).pipe(stream);

  ...
});

파일을 쓰는 과정은 다음과 같다.

1. fs.createWriteStream()으로 파일을 쓸 경로를 명시한다.
2. streamifier로 파일의 buffer를 읽어들이고 pipe()에 1의 경로를 입력한다.

3. streamifier의 버퍼를 삽입하여 파일을 새로 쓴다.

위 그림처럼 성공적으로 파일이 생성됐다.

이미지 뿐만 아니라 동영상 파일도 가능하다.

(단 영상의 크기에 따라 시간이 달라진다.)