파일 업로드 상세 처리

김주언·2022년 10월 17일
0

Spring

목록 보기
13/15
post-thumbnail

파일 확장자, 크기 사전 처리

특정 확장자를 제외한 파일의 업로드 제한 → 첨부파일을 이용하는 웹 공격을 막기 위한 조치

파일 확장자의 경우 정규표현식을 이용하여 검사 가능하다.

ajaxUpload.jsp


<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
    <script src="https://code.jquery.com/jquery-3.6.1.min.js"
            integrity="sha256-o88AwQnZB+VDvE9tvIXrMQaPlFFSUTR+nldQm1LuPXQ=" crossorigin="anonymous"></script>

</head>
<body>
<div class="uploadDiv">
  <input type="file" name="uploadFiles" multiple>
</div>
<button id="uploadBtn" >Upload</button>

<script>
    $(function () {


        const regex = new RegExp("(.*?)\.(exe|sh|zip|alz)$");
        const maxSize = 5242880 // 5MB

        function checkExtentsion(fileName, fileSize){
            if (fileSize >= maxSize){
                alert("too large file size");
                return false;
            }

            if (regex.test(fileName)){
                alert("This extensions cannot be uploaded")
                return false;
            }
            return true;
        }

        $("#uploadBtn").on("click", function (e){
            let formData = new FormData()
            let inputFile = $("input[name='uploadFiles']");
            let files = inputFile[0].files;
            console.log(files)


            for (let i = 0; i < files.length; i++) {
                if(!checkExtentsion(files[i].name, files[i].size)) return false;
                formData.append("uploadFile", files[i]);
            }

            $.ajax({
                url : '/ajaxUpload',
                processData : false,
                contentType : false,
                method: 'post',
                data: formData,
                success : function(result) {
                    console.log(result);
                }
            }) // $.ajax
        }) // .on("click")
        
    }) // $(function(){})
</script>
</body>
</html>
  • const regex = new RegExp("(.*?)\.(exe|sh|zip|alz)$");
    생성자 함수를 호출하여 정규표현식을 생성한다.
    • . : 임의의 한 문자

    • * : 0회 이상 반복되는 문자와 가능한 많이 일치

    • ? : 없거나 1회 가능한 많이 일치
      .* : 아무 문자나 가능한 많이 일치시킨다. ( 하나의 단어 )
      .*? : 하나의 단어가 없거나 1회 매칭된다.

    • \. : 이스케이프 문자 + 마침표 → "." 를 의미한다.

    • $ : 문자열의 끝

      (.*?) 파일명 + \. 마침표 + (exe|sh|zip|alz) 확장자
      ⇢ 파일명.확장자 나타내는 정규표현식

  • 정규표현식 메서드
메소드문법설명
exec정규식.exec(문자열)일치하는 하나의 정보(Array) 반환
test정규식.test(문자열)일치 여부(Boolean) 반환
match문자열.match(정규식)일치하는 문자열의 배열(Array) 반환
search문자열.search(정규식)일치하는 문자열의 인덱스(Number) 반환
replace문자열.replace(정규식,대체문자)일치하는 문자열을 대체하고 대체된 문자열(String) 반환
split문자열.split(정규식)일치하는 문자열을 분할하여 배열(Array)로 반환
toString생성자_정규식.toString()생성자 함수 방식의 정규식을 리터럴 방식의 문자열(String)로 반환

년/월/일 폴더 생성

  • 한 폴더 내에 너무 많은 파일의 생성 문제
    • 년/월/일 단위의 폴더를 생성하여 생성일별로 파일을 저장
  • mkdir() : 디렉터리를 생성한다. 상위 폴더가 존재하지 않을 시 상위폴더도 생성한다.

UploadController

package com.ze.controller;

// ...

@Controller
@Log4j2
public class UploadController {


    private String getFolder(){
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        Date date = new Date();
        String str = sdf.format(date); // 오늘 날짜를 년-월-일 문자열로 생성한다.

        return str.replace("-", File.separator);    // 생성된 년-월-일 문자열의 형태를 년/월/일 로 변경한다. 이 때 /는 폴더 경로를 나타낸다
    }

    @PostMapping("/ajaxUpload")
    public void uploadAjax(MultipartFile[] uploadFile){
        log.info("post /ajaxUpload");
        String uploadPath = "/Users/jueon/Desktop/study/spring/upload/temp";

        // 폴더 객체 생성
        File uploadFolder = new File(uploadPath, getFolder());
        // 년/월/일 폴더 생성
        if (!uploadFolder.exists()) uploadFolder.mkdirs();


        for (MultipartFile file : uploadFile) {
            log.info("upload file name : " + file.getOriginalFilename());
            log.info("upload file size : " + file.getSize());

            File saveFile = new File(uploadFolder, file.getOriginalFilename());

            try {
                file.transferTo(saveFile);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }


    @GetMapping("/uploadForm")
    public void uploadForm() {
        log.info("get /uploadForm");
    }

    @GetMapping("/ajaxUpload")
    public void ajaxUpload(){
        log.info("get /ajaxUpload");
    }




    @PostMapping("/upload")
    public void upload(MultipartFile[] uploadFile, Model model) {
        String uploadPath = "/Users/jueon/Desktop/study/spring/upload/temp";

        for (MultipartFile file : uploadFile) {
            log.info("post /upload");
            log.info("upload file name : " + file.getOriginalFilename());
            log.info("upload file size : " + file.getSize());

            File saveFile = new File(uploadPath, file.getOriginalFilename());

            try {
                file.transferTo(saveFile);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }


}

중복 파일명 처리

  • 파일 저장 시 기존 파일명과 동일하면 기존파일이 삭제되고 나중에 저장되는 파일만 저장된다.
    • UUID를 사용하여 중복 가능성이 극히 낮은 문자열을 생성하여 이를 파일명과 결합한다.

    @PostMapping("/ajaxUpload")
    public void uploadAjax(MultipartFile[] uploadFile){
        log.info("post /ajaxUpload");
        String uploadPath = "/Users/jueon/Desktop/study/spring/upload/temp";

        File uploadFolder = new File(uploadPath, getFolder());

        if (!uploadFolder.exists()) uploadFolder.mkdirs();

        for (MultipartFile file : uploadFile) {
            String fileName = file.getOriginalFilename();
            UUID uuid = UUID.randomUUID();
            fileName = uuid.toString() + "_" + fileName;
            File saveFile = new File(uploadFolder, fileName);

            try {
                file.transferTo(saveFile);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
profile
학생 점심을 좀 차리시길 바랍니다

0개의 댓글