Google AdSense (text)

hidden logo stop

Moving

거지 같은 이글루스 광고노출 정책이 싫어서,
새 보금자리(http://blog.leocat.kr/)로 이사감.

[Express/Formidable] bodyParser 대신 Formidable을 사용한 파일 업로드 Computer & Program

ExpressConnect를 바탕으로 만들어졌다. 때문에 Connect의 middleware를 그대로 사용할 수 있다. Connect에는 GET/POST로 전달된 전달인자 등을 파싱하거나, 파일 업로드를 편하게 사용할 수 있는 bodyParser middleware가 있다.

bodyParser를 사용해서 간단한 파일 업로드 기능을 아래와 같이 만들 수 있다. 'http://localhost:3000/file/upload'로 접속(GET)하면 html 페이지가 보여지고, 업로드 (POST)하면 파일이 저장 저장된 다음 설정한 callback 함수가 호출된다.

var http = require('http');
var express = require('express');
var app = express();

app.use(express.bodyParser());

app.get('/file/upload', function(req, res) {
  res.sendfile(__dirname + '/views/file/upload.html');
});
app.post('/file/upload', function(req, res) {
  console.log(' - file is uploaded');
  console.log(req.files);
  res.render(SOME_PAGE);
});

var httpServer = http.createServer(app).listen(3000);

<!DOCTYPE html>
<html>
<body>
  <form action="/file/upload" method="POST" enctype="multipart/form-data">
    <input type="file" name="file" />
    <input type="submit" value="upload" />
  </form>
</body>
</html>


내 입맛대로 파일 업로드를 설정하려면 bodyParser middleware 대신 Formidable middleware (node-formidable)를 사용하면 된다. 사실은 bodyParser는 jsonurlencoded, multipart middleware를 합쳐놓은 것이기 때문에 아래 코드는 동일한 내용이다.

app.use(express.bodyParser());

// bodyParser는 아래와 동일하다
app.use(express.json());
app.use(express.urlencoded());
app.use(express.multipart()); // 파일 업로드에 사용


파일 업로드를 내 맘대로 컨트롤할 것이기 때문에 파일 업로드를 담당하는 multipart 대신 Formidable을 사용한다. (사실은 multipart도 Formidable을 사용한다.) Formidable은 함수안에서 직접 사용하면 된다.

var http = require('http');
var express = require('express');
var app = express();
var formidable = require('formidable');

// app.use(express.bodyParser());
app.use(express.json());
app.use(express.urlencoded());

app.get('/file/upload', function(req, res) {
  res.sendfile(__dirname + '/views/file/upload.html');
});
app.post('/file/upload', function(req, res) {
  var form = new formidable.IncomingForm();
  // 파일 전송 시 첫번째 chunk가 전달되면 호출
  form.on('fileBegin', function(name, file) {
    console.log('fileBegin - ' + name + ':' + JSON.stringify(file));
  })
  // 파일의 chunk가 전달될 때 마다 호출
  .on('progress', function(bytesReceived, bytesExpected) {
    console.log('progress: ' + bytesReceived + '/' + bytesExpected);
  })
  // 파일 전송도중 esc나 페이지 전환 등으로 중단될 때 호출
  .on('aborted', function() {
    console.log('aborted');
  })
  // error 발생 시 호출
  .on('error', function() {
    console.log('error');
  })
  // 파일 전송이 끝난 경우 호출
  .on('end', function() {
    console.log('end');
  });
  form.parse(req, function(err, fields, files) {
    console.log('parse - ' + JSON.stringify(files));
  });
});

var httpServer = http.createServer(app).listen(3000);


파일 업로드가 시작되면 fileBegin이 아닌 progress 이벤트가 먼저 발생한다. fileBegin은 첫번째 데이터 chunk가 들어와야 하기 때문에 progress가 먼저 발생한다. fileBegin 이벤트에는 전송되는 파일의 정보를 담은 Formidable.File 객체가 들어온다. 전송되는 파일이 저장되는 위치, mime 타입 등의 정보를 담는다. 그리고 파일이 전부 전송되고 디스크에 저장(flush)되면 end 이벤트가 발생한다. 마지막으로 parse 메소드에 설정한 callback 함수를 통해 뒷처리(?)를 할 수 있다.

progress: 0/509789
progress: 65536/509789
fileBegin - file:{"size":0,"path":"/var/folders/vm/7kq5_1nd3dg4hx_2m1wdt7y40000gn/T/630137ebdcbb650031245be59764d99a","name":"DSC_8883 copy.jpg","type":"image/jpeg","mtime":null}
progress: 131072/509789
progress: 196608/509789
progress: 262144/509789
progress: 327680/509789
progress: 393216/509789
progress: 458752/509789
progress: 509789/509789
end
parse - {"file":{"size":509598,"path":"/var/folders/vm/7kq5_1nd3dg4hx_2m1wdt7y40000gn/T/630137ebdcbb650031245be59764d99a","name":"DSC_8883 copy.jpg","type":"image/jpeg","mtime":"2013-05-27T15:35:55.038Z"}}


Formidable은 파일 전송 시 일단 임시 경로에 파일을 저장한다. 그리고 parse 메소드에서 fs.rename(), fs.renameSync() 등을 통해서 원하는 위치로 이동시키면 된다. 파일이 처음에 저장되는 위치는 os의 temp 경로이고, uploadDir 속성으로 변경할 수 있다. 이외에도 많은 설정이 있으니 Formidable github 페이지를 참고해보자.

exports.upload.upload = function(req, res) {
  var form = new formidable.IncomingForm();
  form.uploadDir = 'test/upload'; // 업로드 경로 변경
  form.hash = 'sha1'; // checksum 'sha1'과 'md5' 중 선택 가능
  form.keepExtensions = true; // 파일 확장자를 남길 것인지 여부
  form.parse(...);
};


uploadDir은 설정하지 않으면 OS에 설정된 temp 경로를 사용한다. 오픈소스라 여러 사람들이 개발하다보니 문서와 코드가 일치하지 않는 경우가 많은듯 하다. (문서 업데이트 좀 잘 하자!! 응?? -ㅅ-;;) 이런건 코드를 확인해 보는게 좋을 때가 많다.

// 현재 소스에 있는 uploadDir
this.uploadDir = opts.uploadDir || os.tmpDir();

// 하지만 Readme.md에 쓰여 있는 uploadDir 설명
form.uploadDir = process.env.TMP || process.env.TMPDIR || process.env.TEMP || '/tmp' || process.cwd();


덧글

댓글 입력 영역

Google AdSense (text/image)