첨부파일 자체의 처리는 AJax를 통해서 이루어진다. 즉 게시물의 등록 시점에는 현재 서버에 업로드된 퍼일들에 정보를 등록하려는 게시물의 정보와 같이 전송된다.
이때 게시물의 등록버튼을 클릭했을때 현재 서버에 업로드된 파일의 정보를
<input type = 'hidden'>으로 만들어서 한번에 전송하는 방식을 사용하겠다.
등록 화면인 http://localhost:8080/board/register 의 화면을 담당하는 register.jsp 파일에 첨부파일 업로드할 수 있도록
<div>태그를 추가한다.
<div class="row">
<div class="col-lg-12">
<div class="panel panel-default">
<div class="panel-heading">File Attach</div>
<!-- /.panel-heading -->
<div class="panel-body">
<div class="form-group uploadDiv">
<input type="file" name='uploadFile' multiple>
</div>
<div class='uploadResult'>
<ul>
</ul>
</div>
</div>
<!-- end panel-body -->
</div>
<!-- end panel-body -->
</div>
<!-- end panel -->
</div>
<!-- /.row -->
그럼 어렵지 않게 첨부파일을 놓을 수 있는 란이 생겼다.
JS처리
복잡한 부분은 파일을 선택하거나 Submit Button을 클릭했을 때의 JS처리이다.
가장 먼저 Submit Button을 클릭했을때 첨부파일 관련된 처리를 할 수 있도록 기본 동작을 막는 작업부터 하자
$(document).ready(function(e){
var formObj = $("form[role='form']");
$("button[type='submit']").on("click", function(e){
e.preventDefault();
console.log("submit clicked");
});
});
var regex = new RegExp("(.*?)\.(exe|sh|zip|alz)$");
var maxSize = 5242880; //5MB
function checkExtension(fileName, fileSize){
if(fileSize >= maxSize){
alert("파일 사이즈 초과");
return false;
}
if(regex.test(fileName)){
alert("해당 종류의 파일은 업로드할 수 없습니다.");
return false;
}
return true;
}
$("input[type='file']").change(function(e){
var formData = new FormData();
var inputFile = $("input[name='uploadFile']");
var files = inputFile[0].files;
for(var i = 0; i < files.length; i++){
if(!checkExtension(files[i].name, files[i].size) ){
return false;
}
formData.append("uploadFile", files[i]);
}
$.ajax({
url: '/uploadAjaxAction',
processData: false,
contentType: false,data:
formData,type: 'POST',
dataType:'json',
success: function(result){
console.log(result);
showUploadResult(result); //업로드 결과 처리 함수
}
}); //$.ajax
});
사실 전에 내가 작성했었던 JS를 복붙한것이다.
그리고 첨부파일을 추가해서 submit을 한경우 결과는 콘솔창에 나타난다.
이제 업로드가 된다는 것이니 화면에 섬네일등을 만들어 처리할 부분은 별도의 showUploadResult()함수를 제작하고 결과를 반영한다.
function showUploadResult(uploadResultArr){
if(!uploadResultArr || uploadResultArr.length == 0){ return; }
var uploadUL = $(".uploadResult ul");
var str ="";
$(uploadResultArr).each(function(i, obj){
if(obj.image){
var fileCallPath = encodeURIComponent( obj.uploadPath+ "/thumb_"+obj.uuid +"_"+obj.fileName);
str += "<li><div>";
str += "<span> "+ obj.fileName+"</span>";
str += "<button type='button' data-file=\'"+fileCallPath+"\' data-type='image' class='btn btn-warning btn-circle'><i class='fa fa-times'></i></button><br>";
str += "<img src='/display?fileName="+fileCallPath+"'>";
str += "</div>";
str +"</li>";
}else{
var fileCallPath = encodeURIComponent( obj.uploadPath+"/"+ obj.uuid +"_"+obj.fileName);
var fileLink = fileCallPath.replace(new RegExp(/\\/g),"/");
str += "<li><div>";
str += "<span> "+ obj.fileName+"</span>";
str += "<button type='button' data-file=\'"+fileCallPath+"\' data-type='file' class='btn btn-warning btn-circle'><i class='fa fa-times'></i></button><br>";
str += "<img src='/resources/img/attach.png'></a>";
str += "</div>";
str +"</li>";
}
});
uploadUL.append(str);
}
이때 가장주의할 점은
파일의 업로드는 별도의 업로드 버튼을 두지않기에, <input type='file'>의 내용이 변경되는 것을 감지해서 처리되어야 하기때문에 $(documnet).ready() 내에 넣는 것이다..
반영이 잘된다.
그리고 이제 엑스 버튼을 누르면 지워지도록 하는 JS 도 추가하자
$(".uploadResult").on("click", "button", function(e){
console.log("delete file");
var targetFile = $(this).data("file");
var type = $(this).data("type");
var targetLi = $(this).closest("li");
$.ajax({
url: '/deleteFile',
data: {fileName: targetFile, type:type},
dataType:'text',
type: 'POST',
success: function(result){
targetLi.remove();
}
}); //$.ajax
});
이제 파일을 삭제하면 delete file 이라고 뜨는것을 확인 할 수 있다.
그리고 실제 파일도 삭제되는것을 알 수있다.
게시물 등록과 첨부파일의 DB처리
게시물의 등록과정에서는 첨부파일의 상세조회는 의미가 없고, 단순히 새로운 첨부파일을 추가하거나 삭제해서 자신이 원하는 파일을 첨부하는 것이 중요하다.
Ajax를 이용하는 경우 이미 어떤 파일을 첨부로 처리 할 것인지는 완료된 상태이므로, 남은 작업은 게시물이
등록될때 첨부파일과 관련된 자료를 같이 전송하고, 이를 DB에 등록하는 것이다.
이미 업로드된 첨부파일의 정보는 별도의 <input type='hidden'>태그를 생성해서 처리한다.
이를 위해서 첨부파일 정보를 태그로 생성할 때 첨부파일과 관련된 정보(data-uuid, data-filename, data-type)을 추가한다.
$(uploadResultArr).each(function(i, obj){
if(obj.image){
var fileCallPath = encodeURIComponent( obj.uploadPath+ "/thumb_"+obj.uuid +"_"+obj.fileName);
str += "<li data-path='"+obj.uploadPath+"'";
str +=" data-uuid='"+obj.uuid+"' data-filename='"+obj.fileName+"' data-type='"+obj.image+"'"
str +" ><div>";
str += "<span> "+ obj.fileName+"</span>";
str += "<button type='button' data-file=\'"+fileCallPath+"\' "
str += "data-type='image' class='btn btn-warning btn-circle'><i class='fa fa-times'></i></button><br>";
str += "<img src='/display?fileName="+fileCallPath+"'>";
str += "</div>";
str +"</li>";
}else{
var fileCallPath = encodeURIComponent( obj.uploadPath+"/"+ obj.uuid +"_"+obj.fileName);
var fileLink = fileCallPath.replace(new RegExp(/\\/g),"/");
str += "<li "
str += "data-path='"+obj.uploadPath+"' data-uuid='"+obj.uuid+"' data-filename='"+obj.fileName+"' data-type='"+obj.image+"' ><div>";
str += "<span> "+ obj.fileName+"</span>";
str += "<button type='button' data-file=\'"+fileCallPath+"\' data-type='file' "
str += "class='btn btn-warning btn-circle'><i class='fa fa-times'></i></button><br>";
str += "<img src='/resources/img/attach.png'></a>";
str += "</div>";
str +"</li>";
}
});
uploadUL.append(str);
}
로 수정하자
이렇게하면 <input type=hidden>으로 처리된 첨부파일의 정보는 BoardVo로 수집된다.
파일정보는 Json으로 처리된다.
그리고 기존의 <form>태그를 전송하는 부분을 아래와 같이 수정한다.
var formObj = $("form[role='form']");
$("button[type='submit']").on("click", function(e){
e.preventDefault();
console.log("submit clicked");
var str = "";
$(".uploadResult ul li").each(function(i, obj){
var jobj = $(obj);
console.dir(jobj);
console.log("-------------------------");
console.log(jobj.data("filename"));
str += "<input type='hidden' name='attachList["+i+"].fileName' value='"+jobj.data("filename")+"'>";
str += "<input type='hidden' name='attachList["+i+"].uuid' value='"+jobj.data("uuid")+"'>";
str += "<input type='hidden' name='attachList["+i+"].uploadPath' value='"+jobj.data("path")+"'>";
str += "<input type='hidden' name='attachList["+i+"].fileType' value='"+ jobj.data("type")+"'>";
});
console.log(str);
formObj.append(str).submit();
});
});
브라우저에서 게시물 등록을 선택하면 이미 업로드된 항목들을 내부적으로 <input type= 'hidden'>태그들로 만들어서 <form>태그가 submit될때 같이 전송되도록 한다.
BoardController, BoardService의 처리
파라미터를 수집하는 BoardController는 별도의 처리 없이 전송되는 데이터가 제대로 수집되었는지 먼저 확인하자
기존의 BoardController 의 register 메서드를 수정하자
@PostMapping("/register")
public String register(BoardVO board, RedirectAttributes rttr) {
log.info("==========================");
log.info("register: " + board);
if (board.getAttachList() != null) {
board.getAttachList().forEach(attach -> log.info(attach));
}
log.info("==========================");
service.register(board);
rttr.addFlashAttribute("result", board.getBno());
return "redirect:/board/list";
}
BoardController의 register()는 BoardService를 호출하기 전에 log를 이용해서 확인하는
작업을 먼저진행시키도록 했다.
브라우저에서 첨부파일을 추가하고 게시물을 등록하면 서버에서는
해당 로그들이 출력된다.
로그들이 정상적으로 나온다.
BoardServiceImpl를 수정해 주어야한다.
BoardServiceImpl에서 두개의 Mapper인터페이스 타입을 주입하고, 이를 호출하는 일을
Mapper를 주입받아야 하기때문에 자동주입대신 Setter매서드를 이용해서 수정하자
@Setter(onMethod_ = @Autowired)
private BoardMapper mapper;
@Setter(onMethod_ = @Autowired)
private BoardAttachMapper attachMapper;
게시물의 등록작업은 tbl_board 테이블과 tbl_attach테이블 양쪽 모두 insert가 진행되어야 하기때문에 트랜잭션 처리가 필요하다
일번적인 경우라면 오라클의 시퀀스를 이용해서 nextval, currval을 이용해서 처리하겠지만,
myBatis의 selectKey를 이용해서 별도의 currval을 매번 호출할 필요가 없다.
@Transactional
@Override
public void register(BoardVO board) {
log.info("register......" + board);
mapper.insertSelectKey(board);
if (board.getAttachList() == null || board.getAttachList().size() <= 0) {
return;
}
board.getAttachList().forEach(attach -> {
attach.setBno(board.getBno());
attachMapper.insert(attach);
});
}
register메서드를 위와같이 수정해주자
BOardServiceImpl 의 register()는 트랜잭션하에 tbl_board에 먼저 게시물을 등록하고,
각 첨부파일은 생성된 게시물 번호를 세팅한 후 tbl_attach테이블에 데이터를 추가한다.
myBatis쪽에 문제가 없다면 DB의 tbl_attach테이블에 첨부파일이 여러개 등록되었을 때 아래와 같은 모습으로 출력된다.
'Spring공부 > 3-파일업로드' 카테고리의 다른 글
프로젝트(4)-게시물 삭제 (0) | 2021.10.15 |
---|---|
프로젝트(3)-게시물 조회 (0) | 2021.10.15 |
프로젝트(1)-첨부파일등록 (0) | 2021.10.14 |
파일업로드(6)-첨부파일삭제 (0) | 2021.10.14 |
파일업로드(5)-원본 이미지 보여주기 (0) | 2021.10.14 |
댓글