ajax를 이용하여 파일 전송하기
주요 특징
1. 비동기 통신
- 페이지 새로고침 없이 통신: AJAX를 사용하면 파일 업로드를 포함한 서버와의 통신을 비동기적으로 처리할 수 있습니다. 이를 통해 사용자 경험이 향상되고, 페이지 새로고침 없이 데이터를 전송하고 받을 수 있습니다.
2. 사용자 인터페이스 향상
- 실시간 응답: 파일 업로드 진행 상황을 실시간으로 사용자에게 보여줄 수 있어, 대기 시간 동안의 피드백을 제공할 수 있습니다.
- 다양한 알림: 업로드 성공, 실패, 진행 상황 등 다양한 상태를 사용자에게 즉시 알릴 수 있습니다.
3. 보안
- CSRF 보호: AJAX 요청은 일반적으로 CSRF 토큰을 포함하여 전송되므로, 보안 위협으로부터 데이터를 보호할 수 있습니다.
- HTTPS 사용 권장: 민감한 데이터 전송 시 HTTPS를 사용하여 데이터의 무결성과 기밀성을 보장할 수 있습니다.
ajax 예시 :
html :
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Document</title>
</head>
<body>
<h1>사진 파일 전송</h1>
<hr>
<form>
<input type="text" id="username"><br>
<input type="file" id="img"><br>
</form>
<script>
let imgInput = document.querySelector("#img");
imgInput.addEventListener("change", (e) => {
let file = imgInput.files[0];
//console.log("file", file);
let reader = new FileReader();
reader.onload = () => {
let username = document.querySelector("#username").value;
let base64String = reader.result;
//console.log(base64String);
myUpload(username, base64String);
}
reader.readAsDataURL(file);
});
async function myUpload(username, img) {
let user = {
username: username,
img: img
}
let requestBody = JSON.stringify(user);
//console.log(requestBody);
let response = await fetch("v2/upload", {
method: "post",
body: requestBody,
headers: {
"Content-Type": "application/json; charset=utf-8"
}
});
let responseBody = await response.json();
if (responseBody.success) {
location.href = "/";
}
}
</script>
</body>
</html>
JavaScript 코드 설명 :
- 파일 입력 필드 이벤트 리스너:
imgInput
변수는 파일 입력 필드(input[type="file"]
)를 선택합니다.imgInput.addEventListener("change", (e) => { ... })
: 파일 입력 필드의 변경 이벤트를 감지합니다.- 파일이 선택되면
file
변수에 파일 객체를 저장합니다. FileReader
객체를 사용하여 파일을 읽습니다.- 파일이 읽히면
onload
이벤트 핸들러가 호출되어 다음 작업을 수행합니다: - 사용자 이름과 파일의 Base64 인코딩 문자열을 추출합니다.
myUpload
함수를 호출하여 파일을 서버에 업로드합니다.
- 비동기 파일 업로드 함수:
myUpload
함수는 사용자 이름과 Base64 인코딩된 파일을 매개변수로 받습니다.- 사용자 이름과 이미지를 포함하는 객체를 생성하여 JSON 문자열로 변환합니다.
fetch
API를 사용하여 서버에 비동기적으로 POST 요청을 전송합니다.- 요청 본문에는 JSON 문자열이 포함됩니다.
- 헤더에는
Content-Type
을application/json; charset=utf-8
로 설정합니다. - 서버 응답을 JSON 형식으로 파싱하고, 응답이 성공적일 경우 메인 페이지로 리디렉션합니다.
Controller :
@PostMapping("/v2/upload")
public ResponseEntity<?> v2Upload(@RequestBody UploadRequest.V2DTO v2DTO) {
uploadService.v2사진저장(v2DTO);
Resp resp = new Resp(true, "성공", null);
return ResponseEntity.ok(resp);
}
변경점
- 메서드 서명:
public ResponseEntity<?> v2Upload(@RequestBody UploadRequest.V2DTO v2DTO)
- 업로드 서비스 호출:
uploadService.v2사진저장(v2DTO);
uploadService
의 v2사진저장
메서드를 호출하여 v2DTO
를 통해 전달된 데이터를 처리합니다.- 응답 객체 생성:
Resp resp = new Resp(true, "성공", null);
Resp
객체를 생성하여 클라이언트로 보낼 응답 데이터를 구성합니다.Resp
객체의 필드:success
필드: 업로드가 성공했음을 나타내는true
값.message
필드: 응답 메시지로 "성공"을 설정.data
필드: 추가적인 데이터가 없는 경우null
로 설정.
- 응답 반환:
return ResponseEntity.ok(resp);
ResponseEntity.ok(resp)
를 호출하여 HTTP 200 OK 상태와 함께 Resp
객체를 응답 본문으로 반환합니다.Service :
@Transactional
public void v2사진저장(UploadRequest.V2DTO v2DTO) {
String profileUrl = FileUtil.fileSave(v2DTO.getImg());
uploadRepository.save(v2DTO.toEntity(profileUrl));
}
Service에서 사용한 fileSave 메서드 :
public static String fileSave(String base64) {
String mimeType = base64.substring(5, base64.indexOf(";base64,"));
String result = mimeType.split("/")[1];
String imgName = UUID.randomUUID().toString() + result;
String profileUrl = "images/"+imgName;
String dbUrl = "/upload/"+imgName;
String base64Data = base64.split(",")[1];
byte[] decodedBytes = Base64.getDecoder().decode(base64Data);
try {
Path path = Paths.get(profileUrl);
Files.write(path, decodedBytes);
return dbUrl;
} catch (IOException e) {
throw new RuntimeException(e.getMessage());
}
}
- base64 문자열을 받아오기 위해 오버로딩으로 메서드 구현
메서드 세부 설명
- Mime Type 추출:
base64.substring(5, base64.indexOf(";base64,"))
: Base64 문자열의 처음 부분에서 MIME 타입을 추출합니다.mimeType.split("/")[1]
: MIME 타입에서 파일 확장자를 얻습니다.
String mimeType = base64.substring(5, base64.indexOf(";base64,"));
String result = mimeType.split("/")[1];
- 파일 이름 생성:
UUID.randomUUID().toString()
: 고유한 파일 이름을 생성하기 위해 UUID를 사용합니다.profileUrl
: 서버의 파일 시스템에 저장될 파일의 경로입니다.dbUrl
: 데이터베이스에 저장될 파일의 URL 경로입니다.
String imgName = UUID.randomUUID().toString() + result;
String profileUrl = "images/"+imgName;
String dbUrl = "/upload/"+imgName;
- Base64 데이터 추출 및 디코딩:
base64.split(",")[1]
: Base64 인코딩된 이미지 데이터 부분을 추출합니다.Base64.getDecoder().decode(base64Data)
: Base64 인코딩된 데이터를 디코딩하여 바이트 배열로 변환합니다.
String base64Data = base64.split(",")[1];
byte[] decodedBytes = Base64.getDecoder().decode(base64Data);
- 파일 쓰기 및 반환 경로:
Paths.get(profileUrl)
: 파일 경로를 생성합니다.Files.write(path, decodedBytes)
: 디코딩된 바이트 데이터를 해당 경로에 파일로 씁니다.- 파일 쓰기에 성공하면
dbUrl
을 반환합니다. - 예외 발생 시
RuntimeException
을 발생시킵니다.
try {
Path path = Paths.get(profileUrl);
Files.write(path, decodedBytes);
return dbUrl;
} catch (IOException e) {
throw new RuntimeException(e.getMessage());
}
Repository : 변경점 없음
Share article