※ 많은 레퍼런스들을 참고하여 고민하며 구현한 코드지만 저 스스로도 취업준비생인 초보 개발자이기 때문에 코드 상의 문제 혹은 더 바람직한 우수사례들이 있을 수 있습니다. 코드 작성 시 참고 정도로 활용해주시고 문제나 개선 가능한 부분 발견 시 공유해주신다면 감사하겠습니다.
현재 진행중인 프로젝트에서 화면 이동 없이 modal 창을 띄워 글을 작성하고 수정할 수 있는 기능이 필요했습니다
화면을 다시 받아오지 않고 비동기식으로 파일을 업로드할 수 있는 제이쿼리를 이용한 AJAX 구현 코드입니다
JSP
<div id="modal-question" class="modal">
<form id="question-form" action="" method="POST" enctype="multipart/form-data">
<input type="hidden" id="bno" name="bno">
<input type="text" id="title" name="title" onchange="test()">
<%-- Editor로 사용할 태그 --%>
<textarea name="content" id="content" style="width: 100%; height: 100%;"></textarea>
<input type="text" id="writer" name="writer" placeholder="">
<div id="form-footer">
<span id="upload-button">Image</span>
<button type="submit" id="submit-button" class="btn">Submit</button>
</div>
<%-- 이미지 업로드 input 태그(숨김) --%>
<input type="file" multiple="multiple" name="image" id="image" style="display: none;" accept="image/*">
</form>
</div>
form-footer 태그 안에 upload-button의 id를 가진 span이 인풋을 작동시키는 버튼 역할을 합니다
form 태그의 맨 아랫줄에 이미지 업로드용 input이 있습니다 화면에 나타나지 않도록 숨김처리 하였습니다
JS
// 이미지 업로드 버튼 클릭 시 image input 태그 클릭 작동
$("#upload-button").click(function () {
$("#image").trigger("click")
})
먼저 form 태그의 upload-button을 누르면 input 태그를 작동시키도록 합니다
(해당 코드는 꼭 필요한 부분은 아닙니다 input 태그를 숨기지 않고 그대로 진행하셔도 됩니다)
// 이미지 업로드 함수. image input 변경 시 실행됨
$("#image").on("change", function () {
// input 태그의 파일 데이터
const files = this.files
// ajax submit용 form data
const formData = new FormData()
// input 태그 파일을 데이터 순회하며 form data에 추가
// 첫번째 스트링 인자('image')는 서버에서 multipart file의 파라미터명으로 쓰이므로 주의
for (let i=0; i<files.length; i++) {
formData.append('image', files[i])
}
// ajax 호출
$.ajax({
// 요청 URL
url: "/file/upload",
// 파일 전송 시
enctype: "multipart/form-data",
// data의 스트링화(stringify) 방지
processData: false,
// contentType header의 default 값 설정 방지
contentType: false,
// 전송할 데이터
data: formData,
// 데이터 전송 방식
type: "POST",
// ajax 요청 성공 시 콜백함수
success: function (imgs) {
// 데이터를 반환받아 실행할 코드
}
})
})
위에서 작동한 input 태그에 변경사항이 있을 시 실행되는 코드입니다
form data를 생성해 파일 데이터를 넣어 ajax 방식으로 특정 url에 요청하는 과정입니다
ajax 요청 시 properties를 주의해주세요 (processData와 contentType 꼭 코드와 같이 설정해주세요)
참고하실 부분은 FormData는 console.log로 출력 시 아무것도 출력되지 않습니다
필요하시다면 링크(https://stackoverflow.com/questions/17066875/how-to-inspect-formdata)를 참고해주세요
ImageController
@RestController
@RequestMapping("/file/")
public class ImageController {
@Autowired
FileService fileService;
@PostMapping(value="/upload")
public ResponseEntity<List<ImageFileDTO>> uploadFile(MultipartHttpServletRequest mtfRequest) throws Exception {
// 파일(들)을 지정된 경로에 저장 및 Image DTO 리스트 반환
List<ImageFileDTO> attachedImgs = UploadFileUtils.uploadFile(mtfRequest);
// Image DTO 리스트와 함께 OK status response code 반환
return new ResponseEntity<>(attachedImgs, HttpStatus.OK);
}
}
image 관련 컨트롤러 입니다
인자로 MultipartHttpServletRequest를 전달받아 multipart file을 다룰 수 있습니다
DTO 리스트를 반환받아 http status code와 함께 클라이언트에게 전달합니다
ImageFileDTO
@Getter
@Setter
public class ImageFileDTO {
private String imageName;
private String uploadPath;
}
업로드된 image 파일의 이름과 업로드 경로를 담을 간단한 DTO 입니다
UploadFileUtils
public class UploadFileUtils {
public static List<ImageFileDTO> uploadFile(MultipartHttpServletRequest mtfRequest) {
// multipartFile 리스트
List<MultipartFile> mtfs = mtfRequest.getFiles("image");
//
List<ImageFileDTO> fileList = new ArrayList<>();
for (MultipartFile mtf : mtfs) {
ImageFileDTO imageFileDTO = new ImageFileDTO();
// 파일 이름
String originalFileName = mtf.getOriginalFilename();
// 공백문자를 언더스코어로 교체하기
originalFileName = originalFileName.replace(' ', '_');
imageFileDTO.setImageName(originalFileName);
// 업로드 경로
String uploadPath = mtfRequest.getSession().getServletContext().getRealPath(File.separator + "WEB-INF" + File.separator + "uploadedImages" + File.separator);
// 조회시 과부하를 막기 위한 경로 구분용 현재 날짜를 yyyyMMdd 형태로 반환받기
String date = getTodayDate();
// 파일 이름 중복문제를 해결하기 위한 UUID
String uuid = UUID.randomUUID().toString();
// 데이터베이스에 기록될 경로 형태
// 현재날짜 + UUID + "_" + 파일
String dbFile = date + uuid + "_" + originalFileName;
imageFileDTO.setUploadPath(dbFile);
// 파일 저장 경로
String saveFile = uploadPath + dbFile;
// 위에서 생성했던 리스트에 첨부 이미지 데이터 담기
fileList.add(imageFileDTO);
try {
// 파일 경로에 저장하기
mtf.transferTo(new File(saveFile));
} catch (IOException e) {
e.printStackTrace();
}
}
return fileList; // 파일 리스트 반환
}
}
다른 포스팅에서 구현했던 UploadFileUtils를 약간 수정하여 multipart file들을 지정된 경로에 저장하고
파일 이름과 저장경로를 DTO에 담아 list에 넣어 반환하게끔 했습니다
AJAX 코드의 success 콜백함수 부분
// ajax 요청 성공 시 콜백함수
success: function (imgs) {
// 데이터를 반환받아 실행할 코드
}
다시 ajax 코드로 돌아와 반환 받은 DTO 리스트를 가지고 콜백함수를 구현합니다
저같은 경우는 업로드한 이미지를 tinymce WYSWYG editor에 출력하므로 아래와 같이 작성 했습니다
// ajax 요청 성공 시 콜백함수
success: function (imgs) {
// 읽어온 이미지를 에디터 Content에 img 태그로 추가
for (const img of imgs) {
tinymce.activeEditor.insertContent('<img alt="photo" src="/uploadedImages/' + img.uploadPath + '" style="width: 800px; height: 500px;"/>')
}
}
특정한 태그안에 업로드된 이미지를 삽입하고 싶으신 분들은 링크(https://stackoverflow.com/questions/44131193/show-image-from-ajax-response)를 참고해주세요
실행 결과 클라이언트측과 서버측 모두 잘 작동하고 있습니다
'Languages > HTML & CSS & JS' 카테고리의 다른 글
Tinymce editor 이미지 업로드 & 삽입하기 (0) | 2022.02.20 |
---|