생각기록

2022-11-26 미들웨어, 파일 업로드 본문

SeSAC 풀스택/Nodejs

2022-11-26 미들웨어, 파일 업로드

끼록관 2022. 11. 26. 17:59

미들웨어 개념 (Middleware)

 

중간에 껴있기 때문에 req, res 객체를 다 가지고 있다.

1. 서버가 작업하기 전에 사이에 함수를 껴놓을 때 = 미들웨어 함수라고 한다

app.get 로직까지 가기 전에 = 클라이언트의 요청이 서버로 가기전에 

사전에 함수로 먼저 검사할 수 있는 것

 

2. 미들웨어에서는 reques, response 객체를 모두 사용 가능하고!

    next() 함수를 이용해 다음 미들웨어로 접근이 가능하다.

 

중간에 껴있기 때문에 req, res 객체를 다 가지고 있다. 

 next()함수 : 미들웨어 동작이 끝나면, 원래 가고자 하던 서버 로직으로 가는 것


multer 

설치 : npm install multer

모듈 추가 : path와 multer 모듈 객체로 불러와서 써야함

 

파일의 경로(목적지)를 설정하는 multer 객체 사용

 

 

1. 서버의 요청

enctype="multipart/form-data"
enc타입 서버가 어찌 읽을지 정함  =>  ( multipart문자를 인코딩 하지 않겟다 )
기본적으로 서버가 인코딩하게 됨
BUT 파일같은 경우 인코딩이 진행되면, 깨지니까 이 속성을 반드시 지정해야 한다. ( input 파일 필요 )

    <h5>upload.single()</h5>
    <form action="/upload-single" method="post" enctype="multipart/form-data">
        <!-- 하나는 userfile기준으로 위에는 name, 아래는 name2가 존재한다. 
        userfile값은 받을 수 있는데 name2는 받을 수 없다
        파일이 중간에 껴있으면 파일 이전까지 데이터는 머터가 처리가 가능한데
        이후의 데이터는 처리 할 수 없다.
        즉 파일보다 이름입력창을 위에 위치 시켜줘야 한다.-->
        <input type="text" name="name"><!--multer 미들웨어 처리 o-->
        <input type="file" name="userfile">
        <input type="text" name="name2"><!--multer 미들웨어 처리 x-->
        <button>업로드</button>
    </form>

2. 서버의 응답 두 가지

경로 설정만 : 확장자  없이 파일명 자동 설정

const upload = multer ({  dest : " uploads/"  });

> 경로에 필요한 폴더도 생성해줘야 함 

 

 

2. 경로 + 상세 설정 (파일명과 확장자 직접 설정)

multer 인자

1. 어디로?

2. 어떤 이름으로?

const upload = multer({
    storage: multer.diskStorage({             //diskStorage 하드디스크에 저장할 때 사용하는 함수
        destination(req,file,done){           
            done( null, 'uploads/')
        },
        filename(req,file,done){
            console.log( "filename : ",req.body);
            const ext = path.extname(file.originalname);// file.originalname => 파일 원래 이름.확장자를 불러옴
														// path.extname() 함수를 이용해서 확장자만 추출 함
            	 // const filename = Date.now() + ext; // 123213123.jpg 지금날짜 밀리세컨트로 파일이름
            const filename = req.body.name + ext; // 머터는 파일보다 늦게 지정된 얘들은 처리 못합니다. 즉 파일에서 처리해야하는 데이터는 파일보다 위에
            done( null, filename);
        }
    })
})
  • storage 저장할 공간에 대한 정보. 디스크나 메모리 저장 가능
  • diskStorage 하드디스크에 업로드(저장)할 때 사용하는 함수
  • destination 저장할 경로
  • filename 저장할 파일명 ( 파일명+ 날짜 + 확장자 형식 )
  • Limits 파일 개수나 파일 사이즈를 제한 할 수 있음

// file.originalname > 파일 원래 이름.확장자를 불러옴
// path.extname() 함수를 이용해서 확장자만 추출 함

 

위의 설정이 끝나면,

3. upload의 다양한 미들웨어 설정

  • single - 파일을 1개만 업로드
  • array - 2개 이상 파일 업로드
  • fileds - 1개씩 여러번의 파일 업로드

single

multer 설정에 따라 파일을 업로드 후 req.file 객체가 생성 됨 

 

인수는

  • input태그의 name
  • 또는 폼데이터의 키와 일치

req.file에 어떤 경로에 저장되어있는지 담아서 전달

업로드가 성공하면, req.body에는 파일이 아닌 input 데이터인 title이 들어감

 

 

array

2개 이상의 파일이여서 req.files 사용

inptut 에 multiple 속성을 추가 => 서버에서 upload.arry()로 받게 된다.

 

fieds

2개 이상의 파일이여서 req.files 사용

input태그나 폼 데이터의 키가 다른 경우 사용

// 1개 파일을 사용할 때는 single() 함수를 사용한다.
// upload.single('userfile')
// <input type="file" name="userfile"> .single('form 의 name과 같아야 한다')
    app.post('/upload', upload.single('userfile'), (req, res) => {
    console.log(req.file);
    console.log(req.body);
    res.send('upload complete');
});
// upload.single() 함수는 multer의 내장 함수로 이름을 받아서 업로드까지 하고 next() 함수까지 실행해주는 역할을 한다.
// single() 함수는 1개의 파일만 업로드 할 때 사용한다.


// 2개 이상의 파일을 보내면 array로 받아진다.
// single() 함수 대신에 array 함수를 사용한다.
    app.post('/upload-multiple', upload.array('userfile'), (req, res) => {
    // 2개 이상의 파일이기 때문에 req.file이 아닌 req.files로 사용한다.
    console.log(req.files);
    console.log(req.body);
    res.send('upload complete multiple');
});


// 1개씩 여러 번의 파일을 업로드하는 경우
// 1개씩 여러번의 파일 업로드를 하는 경우 fields() 함수를 사용하며, list - json 방식으로 받는다.
    app.post('/upload-fields', upload.fields([{name: 'userfile1'}, {name: 'userfile2'}, {name: 'userfile3'}]), (req, res) => {
    // 2개 이상의 파일이기 때문에 req.file이 아닌 req.files로 사용한다.
    console.log(req.files);
    console.log(req.body);
    res.send('upload complete fields');
});

 

 

 

주의할 점

    <h5>upload.single()</h5>
    <form action="/upload-single" method="post" enctype="multipart/form-data">
        <input type="text" name="name"><!--multer 미들웨어 처리 o-->
        <input type="file" name="userfile">
        <input type="text" name="name2"><!--multer 미들웨어 처리 x-->
        <button>업로드</button>
    </form>

userfile기준으로 위에는 name, 아래는 name2가 존재한다. 
name2는 받을 수 없다
이유 : 파일이 중간에 껴있으면 파일 이전까지 데이터는 multer가 처리가 가능한데 이후의 데이터는 처리 할 수 없다.

           =  즉  input type="file"보다 입력창을 위에 위치 시켜줘야 한다.


실습 1.

일반 폼 전송 사진 업로드

//ejs 파일

<html>
    <head>
        <title>Practice35-register</title>
    </head>
    <body>
        <form action="/register" target="_blank" method="post" enctype="multipart/form-data">
            <fieldset style="width:300px;">
                <legend> 개인 정보 </legend>
                아이디 : <input type="text" name="id"><br>
                비밀번호 : <input type="password" name="pw"><br>
                이름 : <input type="text" name="name"><br>
                나이 : <input type="text" name="age"><br>
            </fieldset>
            <input type="file" name="userfile" id="userfile">
            <button>회원가입</button>
        </form>
    </body>
</html>
//서버 파일
const express = require('express');
const multer = require('multer');
const path = require('path');
const app = express();
const port = 8000;

app.set("view engine", "ejs");
app.use( "/uploads", express.static( "uploads" ) );
app.use(express.urlencoded({extended: true}));
app.use(express.json());

const upload = multer({
    storage: multer.diskStorage({
        destination(req,file,done){
            done( null, 'uploads/');
        },
        filename(req,file,done){
            const ext = path.extname(file.originalname);
            done(null, req.body.id + ext );
        }
    })
});
app.get("/register", (req,res) => {
    res.render("practice35-register");
});
app.post("/register", upload.single("userfile"), (req,res) => {
    res.render("practice35-result", { path: req.file.path } );
});
app.listen(port, ()=>{
    console.log( "Server Port : ", port );
});
//result.ejs
<html>
    <body>
        <img src="<%=path%>">
    </body>
</html>


실습 2 동적 폼전송

<html>
    <head>
        <title>Practice35-register</title>
        <script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>

        <script>
            function register(){
                var form = document.getElementById("form_register");
                var formData = new FormData(form);
                --------------------------------------------------------------------         
                // formData를 만들 때 기본값으로 form을 보내면 현재 form에 세팅된 값으로 formData 객체가 만들어진다.
                // append를 이용해 하나하나 만들 경우에는 아래 코드처럼 작성해야 한다.
        
/*
FormData라는 것은 html 에서가 아닌 javascript에서 폼 데이터를 다루기 위해 사용하는 객체
( html 에서는 <form> 태그를 이용해 폼 데이터를 만들 수 있지만, javascript에서는 form 태그를 이용하는 것이 불가능하기 때문에 formData라는 객체를 만들어 사용해야 한다. )
var formData = new FormData();
formData.append('id', form.id.value);

formData.append( key,value ) 라는 문법을 이용할 때 key가 <input name="~~"> 에서 ~~ 가 된다.
즉, 서버에서 req.body 로 받을 때 formData.append 할 때 이용한 key 가 req.body 에 key로 들어가게 된다.
formData.append(key,value) -> req.body에 { key:value } 로 들어간다.


axios({
    method: 'post',
    url: '~~',
    data: { id: '123', pw: 'abcd'}
}) 
로 전송하는 것을 formData를 이용해서 전송할 때는

var formData = new FormData();
formData.append("id", "123");
formData.append("pw", "abcd");
axios({
    method: 'post',
    url: '~~',
    data: formData
}) 
로 변경해서 사용할 수 있다.

                */
--------------------------------------------------------------------         
                axios({
                    method: "post",
                    url: "/register2",
                    data: formData,
                    headers: {
                        'Content-Type': 'multipart/form-data'
                    }
                }).then((a) => { return a.data; })
                .then((d) => {
                    document.querySelector("img").src = d.path;
                });
            }
        </script>
    </head>

    <body>
        <form id="form_register" enctype="multipart/form-data">
            <fieldset style="width:300px;">
                <legend> 개인 정보 </legend>
                아이디 : <input type="text" name="id"><br>
                비밀번호 : <input type="password" name="pw"><br>
                이름 : <input type="text" name="name"><br>
                나이 : <input type="text" name="age"><br>
            </fieldset>
            <input type="file" name="userfile">
            <button type="button" onclick="register();">회원가입</button>
        </form>
        <br>
        <img>
    </body>
</html>

 

const express = require('express');
const multer = require('multer');
const path = require('path');
const app = express();
const port = 8000;

app.set("view engine", "ejs");
app.use( "/uploads", express.static( "uploads" ) );
app.use(express.urlencoded({extended: true}));
app.use(express.json());

const upload = multer({
    storage: multer.diskStorage({
        destination(req,file,done){
            done( null, 'uploads/');
        },
        filename(req,file,done){
            const ext = path.extname(file.originalname);
            done(null, req.body.id + ext );
        }
    })
});
app.get("/register2", (req,res) => {
    res.render("practice36");
});
app.post("/register2", upload.single("userfile"), (req,res) => {
    res.send({ path: req.file.path } );
});
app.listen(port, ()=>{
    console.log( "Server Port : ", port );
});

 

formdata를 만들때

new FromData(form); 에  기본값 (form)을 보내면 현재 form에 셋팅된 값으로 formData 객체가 만들어 진다.

 

 

append를 이용해 하나하나 만들 경우

formData는 javaScript에서 폼 데이터를 다루기 위해 사용하는 객체

(html 에서는 <form>태그를 이용해 폼 데이터를 만들 수 있지만, js에서는 form태그를 이용하는 것이 불가능해서 formData객체를 만들어서 사용해야 한다.)

 

var formData = new FormData();
formData.append('id', form.id.value);

formData.append( key, value ) 라는 문법을 사용할 때

key가 <input name=" ~ " > 에서 ~가 된다.

 

즉, 서버에서 req.body 로 받을때  formData.append 할 때 이용한 key가 req.body 에 key로 들어가게 된다.

formData.append(key,value) --> req.body에 { key : value } 로 들어간다.

 

axios({

 method: 'post',

 url : '~~',

data:{ id : '123', pw : 'abcd' } })

를 ! formData를 이용해서 전송할 때는


var formData = new FormData();

formData.append("id", "123");

formData.append("pw", "abcd");

axios({

method:'post',

url:'~~',

data: formData

})로 사용할 수 있다.