File Upload

  • 파일 업로드를 위한 form은 반드시 method가 post 이어야 하고 enctype을 multipart/form-data로 설정해야 합니다.

  • spring은 Multipart 기능을 지원하기 때문에 별다른 설정없이도 multipart form의 데이터를 받을 수 있습니다.

  • spring에서는 filed을 server에 저장할 때 commons-fileupload 의존성이 필요하고 CommonsMultipartReslover Bean이 필요합니다.

  • Spring에서는 3가지 방법으로 file Upload를 처리할 수 있습니다.

    • HttpServletRequest 대신에 MultipartHttpServletRequest를 이용해서 getFiles 메소드로 처리

    • 파라미터의 자료형을 MultipartFile로 해서 처리하는 방법이 있습니다.

    • (@RequestParam, Command 객체 이용도 가능)

  • MultipartFile 에서 업로드하는 방법

    • getBytes() 메소드로 파일이 내용을 바이트 배열로 리턴받아서 직접 전송하는 방법
  • transferTo(File dest) 메소드에게 File 객체를 대입해서 업로드

  • 파일을 업로드 할 때 주의할 점은 동일한 파일명은 ?

    • UUID 클래스를 이용해서 중복되지 않는 이름을 만들던가 업로드하는 유저의 id를 붙여서 중요한 파일명을 만들지 않도록 하는 방법이 있습니다.

이모티콘 -> 유저별로 디렉토리를 만들어서 서비스 블로그, 메모 -> 회원가입 테이블, 디렉토리 만들어서 별도의 디렉토리에 저장함. 본인테이블에서만 로드하는 형태

  • 삽입의 처리 과정

    • 삽입링크 클릭 -> 삽입할 수 있는 페이지로 포워딩 -> 입력을 하고 전송을 누르면 유효성 검사를 해서 유효성 검사를 통과하면 서버에게 전송 -> 서버는 처리를 하고 결과 페이지로 리다이렉트를 합니다.
  • 서버가 처리를 하고 포워딩을 하게 되면 에러가 발생하던지 동일한 데이터가 중복해서 삽입되게 됩니다.

  • 새로고침을 했을 때 이전에 수행한 작업이 다시 발생해도 되면 포워딩을 해도 되고 다시 발생하면 안되는 경우는 반드시 리다이렉트를 해야 합니다.

1.파일업로드를 위한 데이터 삽입을 위한 링크를 home.jsp 파일에 추가

    <a href="insert">데이터삽입 </a><br/>

2.HomeController에서 insert 요청을 처리하는 메소드를 추가

    @RequestMapping(value="/insert", method=RequestMethod.GET)
    public String insert(HttpServletRequest request, Model model ) {
        //뷰이름을 리턴 
        return "insert";
    }

3.views 디렉토리에 insert.jsp 파일을 만들고 데이터 삽입 폼을 작성

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>데이터 삽입</title>
</head>
<body>
    <div align='center'>
        <h3>데이터 삽입</h3>
        <form method="post" enctype="multipart/form-data" id="myform">
            <table border="1">
                <tr>
                    <td rowspan="5" align="center">
                        <p></p> <img id="img" width="100" height="100" border="1" /> 
                        <br />
                        <input type="file" id="pictureurl" name="pictureurl" accept=".jpg, .jpeg, .gif, .png" /> 
                        <br />
                    </td>
                </tr>
                <tr>
                    <td align="right">코드</td>
                    <td><input type="text" name="itemid" id="itemid" required="required" />
                    <div id="iddiv"></div>
                    </td>
                </tr>
                <tr>
                    <td align="right">이름</td>
                    <td><input type="text" name="itemname" id="itemname" required="required" />
                    </td>
                </tr>
                <tr>
                    <td align="right">가격 </td>
                    <td><input type="text" name="price" id="price" required="required" />
                    </td>
                </tr>
                <tr>
                    <td align="right">효과  </td>
                    <td><input type="text" name="description" id="description" required="required" />
                    </td>
                </tr>
                <tr>
                    <td colspan="3" align="center">
                        <input type="submit" value="전송"/>
                        <input type="button" value="메인" id="mainbtn" />
                    </td>
                </tr>
            </table>
        </form>
    </div>
</body>
</html>

4.insert.jsp 파일에 이미지 미리보기 기능 구현

  • javascript에서 파일을 불러오는 동작은 비동기(콜백)적으로 처리됩니다.
    <script>
        //이미지 미리보기
        var pictureurl = document.getElementById("pictureurl");
        var img = document.getElementById("img");
        //이미지 선택이 변경되면 
        pictureurl.addEventListener("change", function(){
            //업로드한 이미지 파일의 내용을 img에 출력


            //파일 읽기 객체 생성 
            var reader = new FileReader();
            if(pictureurl.files && pictureurl.files[0]){
                //파일 읽기 
                reader.readAsDataURL(pictureurl.files[0]);
                //파일의 내용을 전부 읽으면 출력(비동기)
                reader.addEventListener("load", function(e){
                    img.src = e.target.result;
                })
            }
        });
    </script>

5.itemid 중복 검사

1) itemid를 받아서 중복 여부를 판별해서 리턴할 서버의 요청 처리 메소드를 생성

  • itemid를 파라미터로 받아서 서버에가서 데이터가 존재하는지 확인하면 됩니다.

데이터가 하나인걸 찾아올때면 있으면 데이터가 오지만 없으면 null이 리턴됨

Service 클래스의

  • JSONController 클래스에 itemid 중복 검사를 위한 메소드를 생성
    // itemid를 파라미터로 받아서 중복체크를 해서 리턴하는 메소드
    @RequestMapping(value = "/itemidcheck", method = RequestMethod.GET)
    public Map<String, Object> itemidCheck(HttpServletRequest request, @RequestParam("itemid") int itemid) {
        // 서비스 메소드 호출
        Item item = itemService.getItem(request, itemid);
        // 리턴할 Map을 생성
        Map<String, Object> map = new HashMap<String, Object>();
        if (item == null) {
            map.put("result", "ture");
        } else {
            map.put("result", "false");
        }
        return map;
    }
}

2) insert.jsp 에서 중복 체크 하는 코드를 작성

    // itemid 중복체크를 위한 
        var itemid = document.getElementById('itemid');
        var iddiv = document.getElementById('iddiv');

        // 중복체크를 통과했는지 여부를 저장할 변수
        var idcheck = false;

        // itemid 란에서 포커스가 떠나면 중복 체크 
        itemid.addEventListener('focusout', function(e) {
            //ajax 객체 만들기 
            request = new XMLHttpRequest();
            //요청 생성 
            request.open('GET', 'itemidcheck?itemid=' + itemid.value);
            //요청 전송
            request.send('');
            //요청에 대한 처리를 위한 콜백 메소드 등록
            request.onreadystatechange = function() {
                // 정상응답이 오면 
                if (request.readyState == 4 && request.status >= 200
                        && request.status < 300) {
                    //넘어온 데이터 파싱
                    var data = JSON.parse(request.responseText);
                    if (data.result == 'ture') {
                        iddiv.innerHTML = '멋진코드네요';
                        iddiv.style.color = 'green';
                        idcheck.value=true;

                    } else {
                        iddiv.innerHTML = '이미사용중인코드입니다.';
                        iddiv.style.color = 'red';
                        idcheck.value=false;
                    }
                }
            }
        })

Validation Check(유효성 검사)

  • 데이터를 삽입하거나 삭제 또는 갱신 및 조회를 할 때 올바른 데이터인지 확인하는 작업
    • 웹 애플리케이션에서는 클라이언트(웹 브라우저 - 자바스크립트) 와 서버의 service 클래스 그리고 데이터베이스에서 할 수 있다
    • 클라이언트 측에서만 하는 경우에는 데이터를 전송할 때 헤더를 변경해서 자바스크립트에서 는 올바른 데이터였지만 서버에 도달할 때는 다른 데이터일 수 있습니다.
    • ip와 userAgent 값을 변경해서 전송하는 경우가 많습니다.
    • 서버에서 header 값들을 읽어서 확인해야 합니다.

데이터베이스에서 제약조건을 이용해서 다시 한번 검사를 하는 것이 안전합니다.

보안이 필요한 유효성 검사는 클라이언트에서는 하면 안됩니다.

게시판 글번호(기본키) - 일련번호 번호만 가지고 만들지 말것

기본키 - 자동으로 인덱싱 설정 , 검색과 분류를 할 가능성이 있다.

ID, emal : 입력 - 중복체크 상품번호 : 자동생성 (분류코드 + 일련번호로 생성)

상품번호 : 그룹화 - 종류별, 추천

기본키 중복 검사

  • 기본키를 직접 입력받는 경우에는 중복검사를 해주는 것이 좋다.

    • 이벤트는 포커스가 잃어버릴 때를 많이 이용하고 이 때 ajax나 websocket을 이용해서 서버에게 기본키 입력 값을 전송하고 서버는 넘어온 기본키 입력 값을 가지고 데이터베이스에서 조회해서 그 결과를 json이나 xml 형태로 클라이언트에게 알려주며 클라이언트는 그 값을 가지고 중복 여부를 판단해서 처리를 한다.
  • 기본키를 일련번호 형태로 자동 생성하는 경우는 auto_increment 나 sequencs를 이용하기도 하고 현재 입력된 번호 중 가장 큰 번호를 찾고 여기에 +1을 해서 생성하는 방법도 있다

    • 이 방법은 일반적으로 비추천하고 되도록이면 숫자나 문자의 조합을 이용해서 분류의 기능을 갖도록 만드는 것을 권장한다. 대표적으로 주민번호나 학번.

폼의 데이터 전송 시 유효성 검사

  • 클라이언트에서의 유효성 검사

    • 클라이언트에서 유효성 검사를 하게 되면 서버로 전송하지 않고 수행을 하기 때문에 속도나 트래픽 면에서는 유리하지만 보안이 되지 않기 때문에 클라이언트가 유효성 검사 로직을 파악 할수 있다.
  • 폼의 경우는 submit 이벤트에서 유효성 검사를 하고 유효성 검사를 통과하지 못하면 서버로 전송하지 못하도록 이벤트객체의 preventDefault()를 호출하면 된다.

  • 가장 많이 수행하는 유효성검사는 필수 입력, 중복검사 통과여부, 패턴 일치, 2개의 값일치 등이 있다.

실습 - insert.jsp 에 유효성 검사를 위한 스크립트 코드를 추가

        // 폼의 데이터를 전송할 때 발생하는 이벤트 처리
        document.getElementById("myform").addEventListener("submit", function(e) {
            //중복체크 통과 여부 확인
            if(idcheck.value == false) { 
                iddiv.innerHTML = "아이디 중복검사를 하세요";
                iddiv.style.color = "red";
                itemid.focus();
                //폼의 데이터를 전송하지 않도록 하기 
                e.preventDefault();
                return;
            }
            var price = document.getElementById("price")
            //price 입력란의 숫자만 입력되었는지 체크 
            // +나 -기호를 앞에 붙일 수 있는지
            // ,의 경우는 어떻게 할것인지 
            // 하나씩 가져와서 비교 
            // 길이는 lenghth, 갯수는 count
            for(var i=0; i<price.value.length; i=i+1) {
                var ch = price.value.charAt(i);

                //유효성 검사를 통과하지 못할때     
                if(i==0){
                    if(!(ch== '+'|| ch == '-' || (ch >= '0' && ch <= '9' ))){
                        price.focus();
                        alert("가격의 첫번째 자리는 숫자나 +, - 기호여야 한다. ")
                        //폼의 데이터를 전송하지않도록 하기 
                        e.preventDefault();
                        return ;
                    }
                }else {
                    if(!(ch >= '0' && ch <= '9')){
                        price.focus();
                        alert("가격은 숫자로만 입력하세요 ")
                        //폼의 데이터를 전송하지 않도록  하기 
                        e.preventDefault();
                        return ;
                        }
                }
            }
        });
  • type을 number로 바뀌면 스마트폰에서 입력할때 숫자키만 입력하게 나옴
  • type을 email로 바꾸면 '@' 이 안들어 가면 입력 불가
                  <tr>
                      <td align="right">가격</td>
                      <td><input type="number" name="price" id="price"
                          required="required" /></td>
                  </tr>
                  <tr>
                      <td align="right">효과</td>
                      <td><input type="email" name="description" id="description"
                          required="required" /></td>
                  </tr>
    

spring 에서의 file upload 처리

  • MultipartFile 타입으로 처리

1. 준비사항

1) commons-fileupload 라이브러리의 의존성 설정

2) CommonsMultipartResolver 클래스의 bean을 생성

2.MultipartFile

  • 파라미터를 직접 이 타입으로 받아도 되고 HttpServletRequest 대신에 MultipartHttpServletRequest를 이용해서 요청을 받고 getFile(String parameterName)을 이용해서 가져올 수 있습니다.

3. 실습

1) 파일 업로드 처리를 위한 라이브러리의 의존성을 pom.xml 파일에 추가

        <!--  파일 업로드 처리를 위한 의존성 라이브러리  -->
        <dependency>
            <groupId>commons-fileupload</groupId>        
            <artifactId>commons-fileupload</artifactId>
            <version>1.3.1</version>
        </dependency>

2) 파일 업로드를 처리해 줄 수 있는 CommonsMultipartFile 클래스의 bean을 추가

  • servlet-context.xml 파일에 추가
    <!-- 파일 업로드를 처리할 Bean(spring이 생성하고 관리하는 instance)을 생성  -->
    <beans:bean class="org.springframework.web.multipart.commons.CommonsMultipartResolver" 
    id="multipartResolver">
    </beans:bean>

3) DAO 클래스에 데이터를 삽입하기 위한 메소드를 생성하고 추가

    //데이터 1개를 삽입하는 메소드 int , void
    public int insertItem(Item item) {
        sessionFactory.getCurrentSession().save(item);
        return 1;
    }
        // 지울때는 save(item); -> delete(item);으로 수정

4) ItemService 인터페이스에 데이터를 삽입하기 위한 메소드를 선언


    //데이터 삽입(파일 업로드) 처리를 위한 메소드 
    public int insertItem(MultipartHttpServletRequest request);

5) ItemServiceImpl 클래스에 데이터를 삽입하기 위한 메소드를 구현

어노테이션이 붙으면 내가 모르는 코드가 있는 것이다.

    @Override
    @Transactional
    public int insertItem(MultipartHttpServletRequest request) {
        // 파라미터 읽기 
        String itemid = request.getParameter("itemid");
        String itemname = request.getParameter("itemname");
        String price = request.getParameter("price");
        String description = request.getParameter("description");

        // 파라미터는 문자로만 전송 가능 
        // 데이터베이스에서의 타입에 맞춰서 변경이 필요하다. 
        // DAO 객체의 파라미터 만들기 
        Item item = new Item();
        item.setItemid(Integer.parseInt(itemid));
        item.setItemname(itemname);
        item.setPrice(Integer.parseInt(price));
        item.setDescription(description);

        //파일 읽기
        MultipartFile mf = request.getFile("pictureurl");

        // 업로드할 파일이 있는 경우에만(파일 유/무)
        if(mf.isEmpty() == false) {
            // 원본 파일이름 가져오기 
            String originName = request.getFile("pictureurl").getOriginalFilename();
            // 원본 파일은 여러개의 파일을 업로드 하다보면 중복될 수 있기 때문에 
            // 파일 이름을 만들 때는 동일한 디렉토리에 저장한다면 중복되지 않도록 
            // 파일 이름을 생성할 필요가 있습니다. 
            // 기본키와 파일명을 합치는 방법이 있고 UUID 클래스를 이용해서 만드는 방법 
            String uploadName = itemid + originName ; 
            item.setPictureurl(uploadName);

            //파일을 저장할 경로를 생성 
            // 프로젝트 내의 경로를 가지고 절대경로를 생성
            // 프로젝트 외의 경로면 직접 경로를 작성 
            String uploadPath = request.getRealPath("/img");
            //servlet 3.0 이상인 경우는
            //request.getServletContext().getRealPath("/img");

            //업로드할 File 객체 생성  (파일경로생성)
            File file = new File(uploadPath + File.separator + uploadName);
            try {
                request.getFile("pictureurl").transferTo(file);
            } catch (IllegalStateException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }

        //데이터 삽입
        return hibernateDao.insertItem(item);    
    }

6) HomeController 클래스에 insert를 post 방식으로 요청했을 때 처리를 위한 메소드를 생성

    @RequestMapping(value="/insert", method=RequestMethod.POST)
    public String insert(MultipartHttpServletRequest request, Model model ) {
        //서비스의 메소드를 호출 
        itemService.insertItem(request) ;

        // 삽입, 삭제, 갱신 등 새로고침을 했을 때 이전 작업을 다시 수행하면 안되는 경우에는
        // 리다이렉트를 해야 한다.
        // 리다이렉트를 할 때는 View 이름이 아니고 URL을작성 
        return "redirect:./ ";
    }

7) 실행하고 파일을 확인해야 하는 디렉토리

  • 프로젝트는 원래 모양 그대로 있고 실행이 될 때는 프로젝트의 내용을 build 해서 위의 디렉토리에 있는 내용이 실행되는 것이다.
  • 프로젝트를 복사해서 다른 곳에서 실행하는 경우 프로젝트를 만들 때 존재했던 파일들은 그대로 있지만 실행 중 생성한 파일들은 없다.
    • 개발환경에서 운영환경으로 이전(이행) 할 때 기존 데이터베이스 내용을 전부 삭제해야할때도 있다.

image-20200224114814591

results matching ""

    No results matching ""