busboy로 파일 업로드

세니·2024년 7월 25일
0
post-thumbnail

busboy란?

busboy는 multipart/form-data를 파싱하기 위해 사용하는 npm package입니다.

busboy는 HTML form data를 parsing하는 node js 모듈로 npm install busboy 로 설치할 수 있습니다.

const busboy = require('busboy')

app.post('/test', (req, res) => {
	const bb = busboy({ headers: req.headers })
})

와 같이 http 요청의 request.headers를 이용해 사용할 수 있습니다.

busboy의 npm 문서에 따르면 주로 file, field, close 이벤트로 multipart/form-data를 파싱합니다.

file event

새로운 파일이 발견될 때마다 발생합니다. 인자로는 name<string>, stream<Readable>, info<object> 를 받습니다.

  • name : form field name
  • stream : 파일 데이터를 포함하는 readable stream 입니다.
  • info : object로 'filename', 'encoding', 'mimeType' 속성을 포함합니다.
    - filename : 파일 이름을 제공합니다.
    - encoding : 파일의 'Content-Transfer-Encoding'값을 의미합니다.
    - mimeType : 파일의 'Content-Type'값을 의미합니다.

field event

multipart/form-data에 file 뿐만 아니라 다른 값들도 파싱하기 위한 이벤트입니다.
인자로는 name<string>, value<string>, info<object>를 받습니다.

  • name : form field name을 의미합니다.
  • value : field의 문자열 값을 의미합니다.
  • info
    - nameTruncated : boolean으로 limits.fieldNameSize 제한으로 인해 이름이 잘린 여부를 나타냅니다.
    - valueTruncated : boolean으로 limits.fieldSize 제한으로 인해 값이 잘린 여부를 나타냅니다.
    - encoding : field의 'Content-Transfer-Encoding' 값을 의미합니다.
    - mimeType : field의 'Content-Type' 값을 의미합니다.

사용 방법

bb.on('file', (name, stream, info) => {
      const { filename, encoding, mimeType } = info;
      console.log(
        `File [${name}]: filename: %j, encoding: %j, mimeType: %j`,
        filename,
        encoding,
        mimeType
      );
      file.on('data', (data) => {
        console.log(`File [${name}] got ${data.length} bytes`);
      }).on('close', () => {
        console.log(`File [${name}] done`);
      });
  });
  bb.on('field', (name, val, info) => {
      console.log(`Field [${name}]: value: %j`, val);
  });
  bb.on('close', () => {
      console.log('Done parsing form!');
      res.writeHead(303, { Connection: 'close', Location: '/' });
      res.end();
  });
  req.pipe(bb);

busboy로 파일 업로드


bb.on('file', (name, stream, info) => {
      const { filename, encoding, mimeType } = info;
      const path = '/usr/home/upload/' + filename  // 업로드 할 경로
      const writeStream = fs.createWriteStream(path)
      file.pipe(writeStream);
      writeStream.on('error', (err) => {
	      bb.emit('error', new Error(`File Write Stream Error ${err}`))
      })
  });
  bb.on('field', (name, val, info) => {
      console.log(`Field [${name}]: value: %j`, val);
  });
  bb.on('close', () => {
      console.log('Done parsing form!');
      res.writeHead(303, { Connection: 'close', Location: '/' });
      res.end();
  });
  req.pipe(bb);

busboy의 file event가 수행될 때 업로드 할 경로에 Writable Stream를 만들어 특정 경로에 write하면 됩니다.
여기서 write stream이 에러가 발생할 수 있으니 error도 핸들링 하면 좋습니다.

또한 busboy도 error 이벤트가 존재하는데 file, field 파싱 할 때 에러가 발생하면 busboy에서 에러를 캐치하기 위해 emit을 이용해 에러를 핸들링 해주어야 합니다.

bb.on('error', (err) => {
	console.error(err);
	res.status(500).send('File Upload Error!')
})

busboy를 이용해 form-data를 파싱하고 업로드 하는 과정을 정리하면서 배운 점은 메모리에 파일을 임시로 저장하지 않고 바로 stream해버리기 때문에 다른 multer, formidable에 비해 속도가 빠른것을 확인했습니다.

formidable이나 multer를 이용했을때 제 기준 5기가 정도 되는 파일을 업로드 했을때 30분이 걸렸지만 busboy는 10분이 걸렸습니다. 아직 좋은 성능은 아니지만 확실히 사용하기 쉽고 빠르기 때문에 busboy를 선택했습니다.

출처

profile
세니는 무엇을 하고 있을까

0개의 댓글