File upload in Node.js with “Memes”

Node.js와 Express를 사용한 웹앱에서 파일업로드를 하는 방법을 공부하기 위해 구글링을 하다보면, 적절한 html form에서 enctype=”multipart/form-data”를 사용하면 서버측의 controller가 req.files으로 바로 접근할 수 있는 것 처럼 나와있는 예제가 있었다.

그러나 Express 4 부터 Connect 기반 미들웨어들을 많이 없애는 과정에서 file upload를 다루는 부분도 함께 없어진 것 같다. 아래는 Express 공식 문서의 설명이다.

Connect/Express 팀은 이전에 Connect와 함께 포함되어 있었던 일부 미들웨어 모듈을 더 이상 지원하지 않습니다. 이러한 모듈은 대안적인 모듈로 대체되었으며, 그렇지 않은 경우에는 더 나은 모듈로 대체해야 합니다. 다음의 대안 중 하나를 사용하십시오.

express.multipart

이 중에서 가장 high-level 모듈이라고 할 수 있는 multer를 이용하여 file upload를 해보겠다.

Front-end

html form에서 enctype=”multipart/form-data”로 설정하고 post method로 http request를 보냄으로써 file을 업로드 할 수 있다. multipart는 MIME 타입 중 하나이며 보통 브라우저나 http client가 multipart request를 통해 file전송을 할 수 있다.

파일 전송을 위해 가장 기본적으로 갖추어야 할 코드는 단 세줄이다.

//Jade
form(method="post" enctype="multipart/form-data")
    input(type="file" multiple)
    button(name='apply') 제출

div(class="uploadImgList") // 후에 jQuery를 이용하여 이미지 썸네일을 붙이기 위한
태그

나는 upload를 위한 url routing을 해놓았고, 굳이 제출하는 과정에서 다른 페이지로 가고싶지 않았기 때문에 action attribute를 설정하지 않았다. input type=file에서 multiple은 옵션으로써 여러개의 파일을 선택할 수 있게 해준다. 위의 코드는 아주 담백한 편이지만 나는 약간의 외모지상주의가 있어  부트스트랩과 CSS, Javascript코드를 덧붙였다.

%e1%84%89%e1%85%b3%e1%84%8f%e1%85%b3%e1%84%85%e1%85%b5%e1%86%ab%e1%84%89%e1%85%a3%e1%86%ba-2016-11-13-%e1%84%8b%e1%85%a9%e1%84%92%e1%85%ae-12-50-00

그리고 여기에 이미지를 올렸을 때 이미지 미리보기를 할 수 있는 기능을 jQeury를 활용해 추가했다.

$(document).ready(function(){
    var imgTarget = $('.input-group .uploadImg');

    imgTarget.on('change', function(){  // 값이 변경되면
        var files = $(this)[0].files;
        if (!$(this)[0].files[0].type.match(/image\//)) return;
        var reader = new FileReader();
        reader.onload = function(e){
            var src = e.target.result;
            $('.uploadImgList').append('

‘); }; for (var i in files ){ if(files.hasOwnProperty(i)) { reader.readAsDataURL($(this)[0].files[i]); } } }); });

스크린샷 2016-11-13 오후 12.54.23.png

그럼 이제 ‘제출’ 버튼을 눌렀을 때 Server side에서 disk와 database에 저장할 수 있도록 router handler를 만들어 보자. 위에서도 얘기 했듯이 multer라는 모듈을 사용한다.

var express = require('express');
var router = express.Router();
var multer = require('multer');

router가 router.post를 이용해 http request를 처리할 때, post()함수의 callback 함수로 multer 인스턴스의 single(), array(), field()를 호출하여 file upload를 처리한다. 각각의 callback function들의 특징과 사용방법은 다음과 같다.

//https://github.com/expressjs/multer

.single(fieldname)

Accept a single file with the name fieldname. The single file will be stored in req.file.

.array(fieldname[, maxCount])

Accept an array of files, all with the name fieldname. Optionally error out if more than maxCount files are uploaded. The array of files will be stored in req.files.

.fields(fields)

Accept a mix of files, specified by fields. An object with arrays of files will be stored in req.files.

fields should be an array of objects with name and optionally a maxCount. Example:

[
  { name: 'avatar', maxCount: 1 },
  { name: 'gallery', maxCount: 8 }
]

우선 내가 프로젝트에서 관심 있는 부분은 여러 개의 이미지 파일을 업로드 하는 일이다. 따라서 .array(fieldname[, maxCount])을 이용한다. 받으려하는 파일을 전송하는 input file 태그의 name attribute를 fieldname으로 지정해주면 된다. 최대 업로드 가능 개수를 제한하고 싶을 때는 maxCount를 전달할 수 있으며, 이 parameter는 사용하지 않아도 된다.  이렇게 multer 인스턴스의 함수들을 callback으로 전달만 해줘도, 파일 업로드가 너무나 쉽게 진행된다.

이젠 callback 함수로 전달될 function들을 갖고 있는 instance를 생성하면 된다. 가장 쉬운 방법은 dest property를 이용하여 instance를 생성하는 경우다.

var upload = multer({ dest: 'uploads/' })
router.post('/', upload.array('image'), function (req, res) {
    console.log(req.body);
    console.log(req.files);
});

간단히 multer instance를 생성하고 이것의 .array() function을 callback으로 전달해주면 손쉽게 파일업로드가 진행된다. 하지만 이렇게 생성된 instance는 지정된 경로에 파일을 쉽게 저장해주지만, 저장되는 파일 이름을 지정하는 등의 자유로운 작업을 방해한다. multer.diskStorage()를 통해서 disk에 file을 저장하는데에 대한 full control을 얻을 수 있다.

var storage = multer.diskStorage({
    destination: function (req, file, cb) {
        cb(null, 'uploads/');
    },
    filename: function (req, file, cb) {
        cb(null, file.originalname+'-'+Date.now())
    }
});

 

답글 남기기

아래 항목을 채우거나 오른쪽 아이콘 중 하나를 클릭하여 로그 인 하세요:

WordPress.com 로고

WordPress.com의 계정을 사용하여 댓글을 남깁니다. 로그아웃 /  변경 )

Google photo

Google의 계정을 사용하여 댓글을 남깁니다. 로그아웃 /  변경 )

Twitter 사진

Twitter의 계정을 사용하여 댓글을 남깁니다. 로그아웃 /  변경 )

Facebook 사진

Facebook의 계정을 사용하여 댓글을 남깁니다. 로그아웃 /  변경 )

%s에 연결하는 중

search previous next tag category expand menu location phone mail time cart zoom edit close