공부 기록

[FILE] 자바 + 스프링 부트 + Axios(rest api) 파일 업로드, 다운로드, 삭제 MIME 체크 등

타태 2022. 1. 17. 22:29

 

 

2022.01.01 - [실전 공부/Git] - [Git] 자주 사용하는 Git 명령어 요약

 

[Git] 자주 사용하는 Git 명령어 요약

2021.12.04 - [실전 공부/AWS&Docker&Linux] - [AWS_DB] AWS EC2에 올린 Docker Container DB에 DB 툴로 접속하기 [AWS_DB] AWS EC2에 올린 Docker Container DB에 DB 툴로 접속하기 2021.12.02 - [실전 공부] -..

ktae23.tistory.com

 

파일 업로드, 다운로드, 삭제, MIME 타입 체크, 파일 이름 찾기, 확장자 찾기 등

 

import

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.nio.file.DirectoryStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import javax.validation.constraints.NotBlank;
import org.apache.tika.Tika;
import org.springframework.core.io.InputStreamResource;
import org.springframework.core.io.Resource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;

 

파일 확장자 가져오기

    public String getFileType(String fileName) {
        String ext = fileName.substring(fileName.lastIndexOf(".") + 1);
        String[] imageExt = {"jpg", "jpeg", "png", "gif"};
        if (Arrays.asList(imageExt).contains(ext)) {
            return "IMAGE";
        }
        return "FILE";
    }

 

파일 이름에 일시 추가

public String getNewFileName(String fileName) {
    LocalDateTime currentDateTime = LocalDateTime.now();
    String now = currentDateTime.format(DateTimeFormatter.ofPattern("yyyyMMdd_HH:mm:ss"));

    StringBuilder sb = new StringBuilder();

    sb.append(now);
    sb.append("_");
    // 공백을 언더스코어로 변경 * 아래 있음
    sb.append(convertSpaceToUnderScore(fileName));
    return sb.toString();
}

 

공백을 언더스코어로 치환 + 소문자로 변경

public String convertSpaceToUnderScore(String value) {
    return value.replaceAll(" ", "_").toLowerCase();
}

 

멀티파트 파일 이름 가져오기 + MultipartHttpServletRequest에서 파일 꺼내기

public List<MultipartFile> getMultipartFileNames(MultipartHttpServletRequest request) {
    Iterator<String> fileNames = request.getFileNames();
    List<MultipartFile> multipartFiles = new ArrayList<>();

    while (fileNames.hasNext()) {
        String fileName = fileNames.next();
        multipartFiles.add(request.getFile(fileName));
    }
    return multipartFiles;
}

 

파일 가져오기

public File getFile(FileEntity fileEntity) throws IOException {
    String fileLocation = "";
    String fileName = "";
    Optional<File> file = Optional.empty();


    fileLocation = "/home/ubuntu/upload";
    fileName = fileEntity.getNewFileName();

    String filePath = fileLocation + File.separator + fileName;

    //디렉터리 생성 *아래에 있음
    makeDirectories(fileLocation);

    file = new File(filePath);

    // 파일 MIME 타입 구하기
    String mimeType = tika.detect(file.get());
    if (!mimeTypeChecker(mimeType)) {
        return null;
    }

    return file;
}

 

디렉터리 경로 생성하기

private void makeDirectories(String fileLocation) {

    File directory = new File(fileLocation);
    if (!directory.exists()) {
        directory.mkdirs();
    }
}

 

 

찾아온 파일 ResponseEntity로 반환하기 (컨트롤러)

public ResponseEntity<Resource> getFileDownload(File file, HttpServletRequest request) throws IOException {

    HttpHeaders header = new HttpHeaders();

    Resource resource = new InputStreamResource(new FileInputStream(file));

    // user - agent에 따른 인코딩 * 아래 나옴
    String encodedFileName = fileNameEncoder(file.getName(), request);

    // attachment는 다운로드, inline은 첨부파일
    header.add("Content-Disposition", "attachment; filename=" + "\"" + encodedFileName + "\"");
    header.add("Cache-Control", "no-cache, no-store, must-revalidate");
    header.add("Pragma", "no-cache");
    header.add("Expires", "0");

    return ResponseEntity.status(HttpStatus.OK)
            .headers(header)
            .contentLength(file.length())
            .contentType(MediaType.APPLICATION_OCTET_STREAM)
            .body(resource);
}

 

user - agent에 따른 파일 인코딩

public String fileNameEncoder(String fileName, HttpServletRequest request) throws UnsupportedEncodingException {

    String requestHeader = request.getHeader("User-Agent");
    String encodedFileName = "";
    String originalFileName = getOriginalFileName(URLDecoder.decode(fileName, "UTF-8"));

    if (requestHeader.contains("Edge")) {
        encodedFileName = URLEncoder.encode(originalFileName, "UTF-8").replaceAll("\\+", "%20");
    } else if (requestHeader.contains("MSIE") || requestHeader.contains("Trident")) {
        encodedFileName = URLEncoder.encode(originalFileName, "UTF-8").replaceAll("\\+", "%20");
    } else if (requestHeader.contains("Chrome")) {
        encodedFileName = new String(originalFileName.getBytes("UTF-8"), "ISO-8859-1");
    } else if (requestHeader.contains("Opera")) {
        encodedFileName = new String(originalFileName.getBytes("UTF-8"), "ISO-8859-1");
    } else if (requestHeader.contains("Firefox")) {
        encodedFileName = new String(originalFileName.getBytes("UTF-8"), "ISO-8859-1");
    }
    return encodedFileName;
}

 

멀티파트 파일 저장하기

public void saveFiles(Object fileDto, List<MultipartFile> multipartFiles, List<String> newFileNameList) throws IOException {

    String fileLocation = "/home/ubuntu/save";
    makeDirectories(fileLocation);

    for (int i = 0; i < multipartFiles.size(); i++) {
        InputStream is = multipartFiles.get(i).getInputStream();
        String mimeType = tika.detect(is);
		
        // MIME타입 체크 * 아래 있음
        if (mimeTypeChecker(mimeType)) {
            String filePath = fileLocation + File.separator + convertSpaceToUnderScore(newFileNameList.get(i));
            multipartFiles.get(i).transferTo(Paths.get(filePath));

        }
    }
}

 

멀티파트 파일 저장하기 (transferTo 대신 사용 가능)

public File convertMultiPartToFile(MultipartFile multipartFile) throws IOException {
    File file = new File(multipartFile.getOriginalFilename());
    file.createNewFile();
    FileOutputStream fos = new FileOutputStream(file);
    fos.write(multipartFile.getBytes());
    fos.close();
    return file;
}

 

 

파일 제거하기

public void deleteFiles(List<FileEntity> fileEntityList) throws IOException {

    String fileLocation = "";
    String fileName = "";
    for (FileEntity entity : fileEntityList) {
        fileLocation = "/home/ubuntu/upload";
        fileName = entity.getNewFileName();

        String filePath = fileLocation + File.separator + fileName;

        File targetFile = new File(filePath);

        targetFile.delete();

    }
}

 

파일명에서 확장자 제거한 이름 가져오기

public String getOriginalFileName(String fileName) {
    // 인덱스 찾기 * 아래 나옴
    List<Integer> indexes = findIndexes(fileName);
    return fileName.substring(indexes.get(1) + 1);
}

 

인덱스 찾기 (구분자 지정)

public List<Integer> findIndexes(String fileName) {
    List<Integer> indexList = new ArrayList<Integer>();
    int index = fileName.indexOf("_");
    while (index != -1) {
        indexList.add(index);
        //     (ex : 2022-01-17_13:20:14_파일이름.jpg의 경우 두 번째 '_' 의 위치를 반환)
        index = fileName.indexOf("_", index + 1);
    }
    return indexList;
}

 

 


 

AXIOS 파일 업로드

파일 업로드 엑시오스 부분
     const url = "http://localhost:8080/file/upload";
     const frm = new FormData()
     let files = document.getElementById("files");

     for(let i = 0; i < files.files.length; i++){
       frm.append(`files${i}`, files.files[i]);
     }
     axios.post(url, frm)
     .then(response => {
 		// 성공 로직
     })
     .catch(error => {
        // 에러 로직
     })

html 부분
<input type="file" name="files" id="files"  multiple/>

controller에서는 MultipartHttpServletRequest로 받음 (위에 리퀘스트에서 파일 꺼내는 예제 있음)

 

AXIOS 파일 다운로드

const url = `http://localhost:8080/download/${fileId}`;
 axios.get(url, { responseType: "blob"})
 .then(response => {
     const name = response.headers["content-disposition"]
     .split("filename=")[1]
     .replace(/"/g, "");
     const url = window.URL.createObjectURL(new Blob([response.data]));
     const link = document.createElement("a");
     link.href = url;
     link.setAttribute("download", name);
     link.style.cssText = "display:none";
     document.body.appendChild(link);
     link.click();
     link.remove();
 })
 .catch(error => {
// 예외 처리
 })

** p.s.

 content-disposition 헤더가 서버에서는 나가지면 프론트에서 못받는 경우가 생길 수 있다.

반드시 적용해야하는 필수 헤더가 아니니 이럴 경우 헤더 사용을 고집하지 말고 const name = response.data.fileName 으로 다운로드할 파일의 이름을 지정해주자.

반응형