파일 업로드를 하기 위해서는 디펜던시를 추가해 주어야 한다.
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<c:set var="contextPath" value="${pageContext.request.contextPath}" />
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<style>
.ck.ck-editor {
max-width: 800px;
}
.ck-editor__editable {
min-height: 400px;
}
.ck-content {
font-size: 12px;
color: orange;
}
</style>
<script src="https://code.jquery.com/jquery-3.7.1.min.js" integrity="sha256-/JqT3SQfawRcv/BIHPThkBvs0OEvtFFmqPF/lYI/Cxo=" crossorigin="anonymous"></script>
<script src="https://cdn.ckeditor.com/ckeditor5/40.0.0/classic/ckeditor.js"></script>
<script>
$(function(){
fnFileCheck();
fnUpload();
fnCkeditor();
})
function fnFileCheck(){
$('.files').change(function(){
console.log(this.files);
$('#file_list').empty();
var maxSize = 1024 * 1024 * 100;
var maxSizePerFile = 1024 * 1024 * 10;
var totalSize = 0;
var files = this.files;
for(let i = 0; i < files.length; i++){
totalSize += files[i].size;
if(files[i].size > maxSizePerFile){
alert('각 첨부파일의 최대 크기는 10MB입니다.');
$(this).val('');
$('#file_list').empty();
return;
}
$('#file_list').append('<div>' + files[i].name + '</div>');
}
if(totalSize > maxSize){
alert('전체 첨부파일의 최대 크기는 100MB입니다.');
$(this).val('');
$('#file_list').empty();
return;
}
})
}
function fnUpload(){
$('#btn_upload').click(function(){
// ajax 파일 첨부는 FormData 객체를 생성해서 data로 전달한다.
var formData = new FormData();
var files = $('#files').prop('files'); // var files = $('#files')[0].files;
$.each(files, function(i, elem){
formData.append('files', elem);
})
// ajax
$.ajax({
// 요청
type: 'post',
url: '${contextPath}/ajax/upload.do',
data: formData,
contentType: false,
processData: false,
// 응답
dataType: 'json',
success: function(resData){ // resData === {"success":true}
if(resData.success){
alert('성공');
} else {
alert('실패');
}
}
})
})
}
function fnCkeditor(){
ClassicEditor
.create(document.getElementById('contents'), {
toolbar: {
items: [
'undo', 'redo',
'|', 'heading',
'|', 'fontfamily', 'fontsize', 'fontColor', 'fontBackgroundColor',
'|', 'bold', 'italic', 'strikethrough', 'subscript', 'superscript', 'code',
'|', 'link', 'uploadImage', 'blockQuote', 'codeBlock',
'|', 'bulletedList', 'numberedList', 'todoList', 'outdent', 'indent'
],
shouldNotGroupWhenFull: false
},
heading: {
options: [
{ model: 'paragraph', title: 'Paragraph', class: 'ck-heading_paragraph' },
{ model: 'heading1', view: 'h1', title: 'Heading 1', class: 'ck-heading_heading1' },
{ model: 'heading2', view: 'h2', title: 'Heading 2', class: 'ck-heading_heading2' },
{ model: 'heading3', view: 'h3', title: 'Heading 3', class: 'ck-heading_heading3' },
{ model: 'heading4', view: 'h4', title: 'Heading 4', class: 'ck-heading_heading4' },
{ model: 'heading5', view: 'h5', title: 'Heading 5', class: 'ck-heading_heading5' },
{ model: 'heading6', view: 'h6', title: 'Heading 6', class: 'ck-heading_heading6' }
]
},
ckfinder: {
// 업로드 경로
uploadUrl: '${contextPath}/ckeditor/upload.do'
}
})
.catch(err => {
console.log(err)
});
}
</script>
</head>
<body>
<div>
<h3>MVC 파일첨부</h3>
<form method="post" action="${contextPath}/upload.do" enctype="multipart/form-data">
<div>
<input type="file" name="files" class="files" multiple>
</div>
<div>
<button type="submit">업로드</button>
</div>
</form>
</div>
<hr>
<div id="file_list"></div>
<hr>
<div>
<h3>ajax 파일첨부</h3>
<div>
<input type="file" class="files" id="files" multiple>
</div>
<div>
<button type="button" id="btn_upload">업로드</button>
</div>
</div>
<hr>
<div>
<h3>CKEditor</h3>
<form method="post" action="${contextPath}/add.do">
<div>
<textarea id="contents" name="contents"></textarea>
</div>
<div>
<button type="submit">전송</button>
</div>
</form>
</div>
</body>
</html>
문서편집기능 - ckeditor사용
@RequiredArgsConstructor
@Service
public class FileServiceImpl implements FileService {
private final MyFileUtil myFileUtil;
@Override
public int upload(MultipartHttpServletRequest multipartRequest) {
//첨부된 파일들의 목록리스트
List<MultipartFile> files = multipartRequest.getFiles("files");
//순회
for(MultipartFile multipartFile : files) {
// 첨부 여부 확인
if(multipartFile != null && ! multipartFile.isEmpty()) {
try{
// 첨부 파일이 저장될 경로 가져오기
String path = myFileUtil.getPath();
// 저장된 경로의 디렉터리
File dir = new File(path);
if(!dir.exists()) {
dir.mkdirs();
}
// 첨부 파일의 원래 이름 알아내기
String originalName = multipartFile.getOriginalFilename();
// 첨부파일이 저장될 이름가져오기
String filesystemName = myFileUtil.getFilesystemName(originalName);
// 첨부파일의 file 객체
File file = new File(dir, filesystemName);
// 첨부파일 저장하기
multipartFile.transferTo(file);
} catch(Exception e) {
e.printStackTrace();
}
}
}
return 0; // 첨부한 파일의 개수
}
@Override
public Map<String, Object> ajaxUpload(MultipartHttpServletRequest multipartRequest) {
//첨부된 파일들의 목록리스트
List<MultipartFile> files = multipartRequest.getFiles("files");
//순회
for(MultipartFile multipartFile : files) {
// 첨부 여부 확인
if(multipartFile != null && ! multipartFile.isEmpty()) {
try{
// 첨부 파일이 저장될 경로 가져오기
String path = myFileUtil.getPath();
// 저장된 경로의 디렉터리
File dir = new File(path);
if(!dir.exists()) {
dir.mkdirs();
}
// 첨부 파일의 원래 이름 알아내기
String originalName = multipartFile.getOriginalFilename();
// 첨부파일이 저장될 이름가져오기
String filesystemName = myFileUtil.getFilesystemName(originalName);
// 첨부파일의 file 객체
File file = new File(dir, filesystemName);
// 첨부파일 저장하기
multipartFile.transferTo(file);
//
} catch(Exception e) {
e.printStackTrace();
}
}
}
return Map.of("success",true); // 첨부한 파일의 개수
}
@Override
public Map<String, Object> ckeditorUpload(MultipartFile upload, String contextPath) {
// 이미지 저장할 경로
String path = myFileUtil.getPath();
File dir = new File(path);
if(!dir.exists()) {
dir.mkdirs();
}
// 이미지 저장할 이름(원래 이름 + 저장할 이름)
String originalFilename = upload.getOriginalFilename();
String filesystemName = myFileUtil.getFilesystemName(originalFilename);
// 이미지 File 객체
File file = new File(dir, filesystemName);
// File 객체를 참고하여, MultipartFile upload 첨부 이미지 저장
try {
upload.transferTo(file);
} catch(Exception e) {
e.printStackTrace();
}
//CKeditor로 저장된 이미지를 확인할 수 있는 경로를 {url: http://localhost:8080/...} 방식으로 반환해야함
return Map.of("url", contextPath + path + "/" + filesystemName
, "uploaded", true);
}
}
@Component
public class MyFileUtil {
// 파일이 저장된 경로 반환하기
public String getPath() {
/* /storage/yyyy/MM/dd */
LocalDate today = LocalDate.now();
return "/storage/" + today.getYear() + "/" + String.format("%02d", today.getMonthValue()) + "/" +String.format("%02d", today.getDayOfMonth());
//return "/storage/" + DateTimeFormatter.ofPattern("yyyy/MM/dd").format(today);
}
// 파일이 저장될 이름 반환하기
public String getFilesystemName(String originalName) {
/* UUID.확장자 */
String extName = null;
if(originalName.endsWith("tar.gz")) { // 확장자에 마침표가 포함되는 예외 경우를 처리한다.
extName = "tar.gz";
} else {
String[] arr = originalName.split("\\."); // [.] 또는 \\.
extName = arr[arr.length - 1];
}
return UUID.randomUUID().toString().replace("-", "") + "." + extName;
}
}