공부 기록
[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 으로 다운로드할 파일의 이름을 지정해주자.
반응형