이미지 업로드, 이미지겟 부분 보기 디비에 올리는게 아니라, 톰캣서버에 올려놓고
디비에는 url만 저장 select 날릴 떄 쿼리문을 가져와서 출력하는 방식으로 진행할 예정
출력을 하려면 PrintWriter out = res.getWriter로 받아온다.
이번 테스트 케이스는 서블릿에서 PrintWriter라는 IO를 만들어서 화면에 뿌리고 싶다. (단, List를 Json으로 변환 후 화면에 뿌리고 싶다.)
[초기]
else if("jsonNoticeList2".equals(upmu[1])) {//select
logger.info("jsonNoticeList");
List<Map<String ,Object>> nList = null;
hmb.bind(pMap);
nList = nLogic.noticeList(pMap);
req.setAttribute("nList", nList);
//실제 플젝에서는 이렇게 하지 않는다(서블릿단에서 직접 내보낸다) 1-2버전에서는 개선해 본다
isRedirect = false;//false이면 forward처리됨
}
현재 문제는 404 에러가 발생한다. 어떻게 처리할까?
이렇게 되면 /notice/를 지워야된다. 어떤 방식으로 지울 수 있을까?
[수정 후]
else if("jsonNoticeList2".equals(upmu[1])) {//select
logger.info("jsonNoticeList");
List<Map<String ,Object>> nList = null;
hmb.bind(pMap);
nList = nLogic.noticeList(pMap);
Gson g = new Gson();
String temp = g.toJson(nList);
res.setCharacterEncoding("utf-8");
res.setContentType("application/json");
PrintWriter out = res.getWriter();
//out.print(nList);//List-> [], Map -> {deptno=10, dname=영업부}
out.print(temp);//[{"deptno":10, "dname":"영업무"}]
//응답결과가 페이지가 아닌 경우가 존재한다
//예를들면) json이거나 quill사용시 이미지 이름 일때도 포함된다.
//path의 값을 null처리하거나 문자열이 나가는 경우를 고려해야 한다
int end = path.toString().length();// -> notice/
path.delete(0, end);
path.append(temp);//url이 전달되는게 아니라 json형식 즉 문자열이 전달됨
}
[FrontMVC 수정전]
//이 지점은 java와 오라클 서버를 경유한 뒤 시점이다.
if(af !=null) {
if(af.isRedirect()) {
res.sendRedirect(af.getPath());
}
else{
RequestDispatcher view = req.getRequestDispatcher(af.getPath());
view.forward(req, res);
}
}
근데 왜 저렇게 작성한 것일까? 오체분시해보자.
다음은 path의 값을 공백처리 하기 위한 코드이다.
int end = path.toString().length();
path.delete(0,end);
path.append(temp);
notice/jsonNoticeList2.gd로 경로를 잡아줬다. 그렇게 되면
FrontMVC -> NoticeController -> HashMapBinder -> Logic 순서로 작동하는데
jsp로 뿌려주는게 아니라 서블릿파일 내 PrintWriter로 out.print로 뿌려준다.
path.append해서 path경로를 따로 잡아줄 필요가 없다.
그렇다면 /notice/를 제거해줘야된다.
이것을 제거하는 이유는 404에러가 발생하기 떄문이다.
실질적으로 url로 보여지는 페이지는 무엇일까?
localhost:8000이다.
path를 공백으로 처리해야된다.
int end = path.toString().length();
로 해서 path의 길이를 구한다. 이 떄는 /notice/이기 떄문에 이 길이를 구하면
그리고 그 경로를 삭제하기 위해서 path.delete 메소드
를 사용하여 경로을 삭제한다. path.delete(0,end)로 경로를 잡아주면 /notice/ 전체를 삭제한다.
path.append를 하게 되면 아무것도 저장되어 있지 않은 path에 temp(json)를 저장한다.
path.append : [{"N_TITLE":"겨울방학이벤트\n","N_WRITER":"관리자","N_CONTENT":"2년 33만원 겨울방학이벤트\n","N_NO":2},{"N_TITLE":"휴관일","N_WRITER":"관리자","N_CONTENT":"이번주 일요일은 휴관일입니다.","N_NO":1},{"N_TITLE":"겨울방학이벤트\n","N_WRITER":"관리자","N_CONTENT":"1년 33만원 겨울방학이벤트\n","N_NO":0}]
이후엔 path에 이렇게 저장되어있다. url이 전달되는게 아니라 json형식이 전달된 것이다.
즉, path에 Json이 전달될 떄를 고려 했다.
데이터 형식 관점으로 바라보면, nList가 List이기 때문에 [{}] 로 출력된다.
List<Map<String, Object>> 이다.
그래서 결과가
정리하면
전체적인 로직으로 파악해보자.
FrontMVC(Url: http://localhost:8000/notice/jsonNoticeList2.gd - Notice)
NoticeController(jsonNoticeList2)
HashMapBinder(공통코드)
Logic(noticeList) -> 결과값 받아옴(nList)
NoticeController에서 nList 받은 값을 가지고
json으로 변환(Gson g = new Gson())
PrintWriter out = res(내보냄).getWriter : 화면에 찍으려고 사용
String temp = g.toJson(nList) : 이 부분이 nList를 Json으로 변환하는 스팟
화면 url : http://localhost:8000/notice/jsonNoticeList2.gd 유지한 채로 path 수정 들어감(why? - url에 Json이 들어갈 때 + Json이 들어갈 떄를 학습하기 위해서 이 떄는 FrontMVC NULL 및 공백처리를 하지 않았음)
그렇게 하려면 기존의 url에서 /notice/값을 지워야한다. (처음 디폴트로 /notice/가 설정되어있다. 이 부분을 지우지 않았다고 가정해보자.
이 /notice/를 path가 가지고 있고, isRedirect : false(forward) 처리하게 되면 해당 경로는 아무것도 가지고 있지 않은 page를 출력한다. 이 부분이 .gd로 끝나는가?
af = path : /notice/ + isRedirect : false(forward)
서버 처리는 /notice/로 나오게된다.
/notice/를 지워주면 path문자열에서 delete 하게 되면 처리되지 않을까?
그렇게 처리하기 위해서
int end = path.toString().length();를 하고
path.delete(0, end);를 진행했다.
근데 이렇게 하게 되면 Json을 url에 포함하지 못한다.
url에 Json에 포함하는 이유는 다른 리액트같은 이종간 연결을 할 떄, 우리는 JSON 포맷으로 응답이 나가도록 처리해야된다.
그래서 path.append(temp)를 하는 것이다.
이런식으로 잘 나오는 것을 확인할 수 있다.
즉, url : http://localhost:8000/notice/jsonNoticeList2.gd 이렇게 입력하면
응답값으로 Json을 보내는 것이다.
[{"N_TITLE":"겨울방학이벤트\n","N_WRITER":"관리자","N_CONTENT":"2년 33만원 겨울방학이벤트\n","N_NO":2},{"N_TITLE":"휴관일","N_WRITER":"관리자","N_CONTENT":"이번주 일요일은 휴관일입니다.","N_NO":1},{"N_TITLE":"겨울방학이벤트\n","N_WRITER":"관리자","N_CONTENT":"1년 33만원 겨울방학이벤트\n","N_NO":0}]
리액트랑 붙여보자.
export const noticeListDB = (notice) => {
//?gubun=n_title&keyword=휴관
return new Promise((resolve, reject) => {
try {
console.log(notice);
const response = axios({
method: "get",
url: "http://localhost:8000/notice/jsonNoticeList2.gd",
params: notice,
});
resolve(response);
} catch (error) {
reject(error);
}
});
};
리액트에서 이 부분을 비동기적으로 처리할 떄, url응답 페이지로 Json을 보낸다.
Json으로 마임타입으로 결정한 뒤 서블릿에서 페이지를 보여준다.
리액트에서
공지사항을 누르는 순간 디비에서 서버에 url를 요청 한다.
url: "http://localhost:8000/notice/jsonNoticeList2.gd",
이렇게 되면 응답은 이렇게 나온다.
notice/jsonNoticeList2를 서블릿에서 가로채고(.gd) -FrontMVC
NoticeController에서 jsonNoticeLIst2 조건에 걸린다.
NoticeController에서 마임타입을 res.setContentType("application/json");
으로 지정하면 응답으로 나가는 마임타입
이 json이다.
path : 공백(end로 다 지웠다.) 나중에 path.append(temp);
하여 Json을 추가한다. +
isRedirct는 : false(forward)
if(path !=null) {//응답페이지가 존재하는 경우만 처리할것
af.setPath(path.toString());//이 대로 두면 NullPointerException대상임
}else {
af.setPath(null);//이 대로 두면 NullPointerException대상임
}
af.setRedirect(isRedirect);//true-> ActionForward - isRedirect - false->true
return af;
af.setPath(path.toString())
를 타게되고,
af.setPath
: json데이터가 들어간다.
그리고 FrontMVC에 와서 경로를 나눈다.
if(af !=null) {
if(af.isRedirect()) {//true라는 건 sendRedirect인 경우임
if(af.getPath() == null) {
return;//해당메소드 탈출
}else {
res.sendRedirect(af.getPath());// -> notice/noticeList.jsp
}
}
else{//forward인 경우임 - url안바뀜, 화면은 바뀜, 유지됨. a페이지에서 쥐고 있는 정보를 b페이지에서도 사용가능함
if(af.getPath().contains("/")) {
RequestDispatcher view = req.getRequestDispatcher(af.getPath());
view.forward(req, res);
}
else if(af.getPath() == null) {//파일 업로드 처리시 ActionForward를 통해서 값을 리턴 받을때 문제가 발생됨. 이부분에 대한 해결 프로세스 추가하였다.
logger.info("path가 null일때");
}
else {
logger.info("슬래쉬가 미포함인 경우 ===> " + af.getPath());
res.setCharacterEncoding("utf-8");
res.setContentType("text/plain;utf-8");
PrintWriter out = res.getWriter();
out.print(af.getPath());
return;
}
}
}
af가 null이 아니기 떄문에 조건에 걸리게되고,
else {
logger.info("슬래쉬가 미포함인 경우 ===> " + af.getPath());
res.setCharacterEncoding("utf-8");
res.setContentType("text/plain;utf-8");
PrintWriter out = res.getWriter();
out.print(af.getPath());
return;
}
이 때, sendRedirect, Forward를 진행하는가? --> 페이지 처리 ❌
res.setCharacterEncoding("utf-8");
res.setContentType("text/plain;utf-8");
out.print(af.getPath())를 담게되면 안에 nList를 Json으로 변환한 것을 응답페이지로 내보낸다.
만약에 FrontMVC af 분기 else 부분을 주석처리 하게 된다면, out.print가 어떻게 출력되는지 확인해보려고 하는 사례
FrontMVC에서 (out.print & PrintWriter 사용 안 했을 때
else 주석 부분을 해제하면,(FrontMVC else 주석처리)
이렇게 나온다.
이렇게 화면에 page가 변경되어 나타내는 주된 이유는 PrintWriter로 인해 출력이 저렇게 바뀌어 출력된다.
만약에 마임타입을 res를 주석처리하게 되면 어떻게 처리될까?
else {
logger.info("슬래쉬가 미포함인 경우 ===> " + af.getPath());
//res.setCharacterEncoding("utf-8");
//res.setContentType("text/plain;utf-8");
PrintWriter out = res.getWriter();
out.print(af.getPath());
return;
}
한글이 다 꺠져서 나온다.
근데 path에 저장된건 json인데(NoticeController) 왜 마임타입을 application/json이 아니라 text/plain;utf-8를(FrontMVC) 사용했을까?
NoticeController에서 else if("jsonNoticeList2".equals(upmu[1]))
if문 분기 되었을 떄,
res.setContentType("application/json");
res.setCharacterEncoding("utf-8");
정리하면
PrintWriter
이다. PrintWriter
이다. 여기서는 setContentType("application/json")으로 처리하지 않아도 괜찮을까? yes
-> Path에 저장된것을 봤을 떄, path.append(temp)
를 했다. json이 path에 저장되었다.
-> 이것을 out.print(af.getPath())
;이렇게 출력한다.
path 관점에서 보자. IsRedirect로 전체를 나누는데, SendRedirect에선 ('/'), 문자열, 공백 처리는 왜 안해주는가?
null처리는 되어있다. 왜 그렇게 했을까?
SendRedirect는 (req,res)에 대한 연결이 끊어진다. 단순히 경로를 이동한다. 그렇다면 path가 null인 경우만 제외하고 나머지는 통일이 가능하다.(값을 뿌리진 않으니깐)
Forward(else) 처리는 왜 ('/'), null일떄, 문자열&공백 일 떄 3가지를 나누어서 작성했는데 왜 이렇게 했을까?
forward를 통해서 값을 뿌려야 되기 떄문이다. forward를 사용한다면 요청객체와 응답객체가 끊어지지않고 관리된다.
이 말은 하나의 원본으로 전체를 경유한다는 얘기인데, 만약에 null일떄, 문자열&공백 일 때를 나누지 않는다면
null일 때는 이미지업로드 및 첨부파일을 업로드 할 떄 사용한다.
문자열 & 공백 :
페이지처리를 원함 ('/') (forward)
url입력 시 Json이 아니라, 이미지를 넣으면 된다.
이미지 업로드(UPLOAD) , 이미지(GET) 부분 디비에 올리는게 아니라, 톰캣서버에 올려놓고 디비에는 url만 저장한다.
select 날릴 떄 쿼리문을 가져와서 출력하는 방식으로 진행할 예정이다.
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<%@include file="/common/quill_common.jsp" %>
</head>
<body>
<!-- Create the editor container -->
<div id="editor22"></div>
<!-- Initialize Quill editor
브라우저가 DOM Tree그린다
CSS 포함하는 DOM Tree 그린다
출력됨
-->
<script>
const toolbarOptions = [
['bold', 'italic', 'underline'], // toggled buttons
['blockquote', 'code-block'],
[{ 'list': 'ordered'}, { 'list': 'bullet' }],
[{ 'indent': '-1'}, { 'indent': '+1' }], // outdent/indent
[{ 'header': [1, 2, 3, 4, 5, 6, false] }],
[{ 'color': [] }, { 'background': [] }], // dropdown with defaults from theme
[{ 'font': [] }],
[{ 'align': [] }],
['link', 'image']
];
const quill = new Quill('#editor22', {
modules: { toolbar: toolbarOptions },
theme: 'snow',
placeholder: '글 작성하기',
});
const selectLocalImage = (event) => {
console.log('selectLocalImage');
//e.preventDefault();이벤트 버블링 방어 코드 작성 - submit이슈, <button type=submit|button>
//quill에디터에서 이미지를 클릭했을 때 실제 화면에서는 <input type=file>생성해 주자
//이미지를 선택하면 선택하자마자 백엔드에 요청을 post방식으로 넘긴다 - 8000번 서버의 pds폴더에 선택한 이미지를 업로드 처리함
//파일을 (바이너리코드) 전송할땐 무조건 post방식으로 해야 함
const fileInput = document.createElement('input');//<input> - DOM API의 createElement를 사용하여 태그 생성하는 코드
fileInput.setAttribute('type', 'file');//<input type=file>
console.log("input.type " + fileInput.type);
//이미지 파일만 선택가능하도록 제한
fileInput.setAttribute("accept", "image/*");//* -> image/png , image/gif, image/jpg
fileInput.setAttribute("name", "image");//req.getParameter("image"); -> <input name="image"/>
fileInput.click();
fileInput.addEventListener("change", function () { // change 이벤트로 input 값이 바뀌면 실행
const formData = new FormData();
const file = fileInput.files[0];
formData.append('image', file);
$.ajax({//<form method=post enctype=multipart/form-data/> 이럴경우 req.getParameter사용이 불가함 -> cos.jar
type: 'post',
enctype: 'multipart/form-data',
url: '/notice/imageUpload.gd',
data: formData,
processData: false,
contentType: false,
success: function (response) {
console.log('avatar.png'+response);//avartar.png
const url = "http://localhost:8000/notice/imageGet.gd?imageName="+response;//
const range = quill.getSelection().index;
quill.setSelection(range, 1);
quill.clipboard.dangerouslyPasteHTML(range,
'<img src='+url+' style="width:100%;height: auto;" alt="image" />');
},
error: function (err) {
console.log(err);
}
});//////////////////// end of ajax
});////////////////////// end of onchange 이벤트 핸들러
}////////////////////////// end of selectLocalImage
//html 가져오기
const html = quill.root.innerHTML;
console.log(html);
quill.on('text-change', (delta, oldDelta, source) => {
console.log('글자가 입력될때 마다 호출');
console.log(quill.root.innerHTML);
//console.log(source);//user
//console.log(delta);
//console.log(oldDelta);
}); ////////////// end of onchage - 텍스트 내용이 변경되었을 때 발동
quill.getModule('toolbar').addHandler('image', () => {
console.log('image가 변경되었을때');
selectLocalImage();
})
</script>
</body>
</html>
else if("imageUpload".equals(upmu[1])) {//delete
//quill editor에서 이미지를 선택하면 해당 요청을 호출함 - 비동기처리
//post이면서 enctype 바이너리인 경우 전송이 안됨
MultipartRequest multi = null;
String realFolder = "C:\\Program Files\\workspace_jsp\\nae2Gym\\src\\main\\webapp\\pds";
String encType = "utf-8";
int maxSize = 5*1024*1024;
try {
multi = new MultipartRequest(req, realFolder, maxSize, encType, new DefaultFileRenamePolicy());
} catch (Exception e) {
logger.info(e.toString());
}
Map<String, Object> rmap = nLogic.imageUpload(multi, realFolder);
String temp = null;
if(rmap !=null) {
temp = rmap.get("bs_file").toString();
}
int end = path.toString().length();// -> notice/
path.delete(0, end);
path.append(temp);//url이 전달되는게 아니라 json형식 즉 문자열이 전달됨
}// end of imageUpload
//http://localhost:8000/notice/imageGet.gd?imageName=avatar25.png
else if("imageGet".equals(upmu[1])) {//첨부파일을 처리할 때 다운로드 처리하는 화면에서 사용할 코드 소개함
String b_file = req.getParameter("imageName");// avartar.png
logger.info("111 => " +b_file);
String filePath = "C:\\Program Files\\workspace_jsp\\nae2Gym\\src\\main\\webapp\\pds" ;
File file = new File(filePath, b_file.trim());
logger.info("222 => " + file );
String mimeType = req.getServletContext().getMimeType(file.toString());
if(mimeType == null) {
res.setContentType("application/octet-stream");
}
String downName = null;
FileInputStream fis = null;
ServletOutputStream sos = null;
try {
if(req.getHeader("user-agent").indexOf("MSIE")==-1) {
downName = new String(b_file.getBytes("UTF-8"), "8859_1");//국제 표준규격- 다국어지원
}else {
downName = new String(b_file.getBytes("EUC-KR"), "8859_1"); //한국 표준 규격
}
res.setHeader("Content-Disposition", "attachment;filename="+downName);
logger.info("333");
fis = new FileInputStream(file);
logger.info(fis);
sos = res.getOutputStream();
byte b[] = new byte[1024*10];
int data = 0;
logger.info("444");
while((data=(fis.read(b,0, b.length)))!=-1) {
sos.write(b,0,data);
}
sos.flush();//FileInputStream을 사용해서 file객체를 읽음- 메모리에 쌓인 정보를 비우는 메소드 호출
isRedirect = true;//null처리를 해둠
logger.info(path);
int end = path.toString().length();// -> notice/
path.delete(0, end);
path = null;
} catch (Exception e) {
logger.info(e.toString());
} finally {
try {
if(sos !=null) sos.close();
if(fis !=null) fis.close();
} catch (Exception e2) {
// TODO: handle exception
}
}
}// end of imageGet
//파일 업로드 시와 마찬가지로 파일 정보를 얻어올때도 출력페이지를 내보낼 필요가 없는 경우임
if(path !=null) {//응답페이지가 존재하는 경우만 처리할것
af.setPath(path.toString());//이 대로 두면 NullPointerException대상임
}else {
af.setPath(null);//이 대로 두면 NullPointerException대상임
}
af.setRedirect(isRedirect);//true-> ActionForward - isRedirect - false->true
return af;
}
}
public Map<String, Object> imageUpload(MultipartRequest multi, String realFolder) {
logger.info("imageUpload");
Map<String,Object> pMap = new HashMap<>();
Enumeration<String> files = multi.getFileNames();
String fullPah = null;//파일 정보에 대한 전체경로
String filename = null;//파일이름
//첨부파일이 있다면?
if(files !=null) {
//파일 이름을 클래스로 정의하는 객체 - 파일객체가 생성되었다고 해서 그 안에 내용까지 포함하진 않음
//파일 크기를 계산해주는 메소드 지원함
File file = null;
while(files.hasMoreElements()) {
String fname = files.nextElement();
filename = multi.getFilesystemName(fname);
pMap.put("bs_file", filename);//avartar.png
//File객체 생성하기
file = new File(realFolder+"\\"+filename);
}
//첨부파일의 크기를 담기
double size = 0;
if(file !=null) {
size = file.length();
size = size/(1024);
pMap.put("bs_size", size);
}
}
return pMap;
//return pMap;
}
}
if(af !=null) {
if(af.isRedirect()) {//true라는 건 sendRedirect인 경우임
//첨부파일을 업로드 하는 것은 페이지 이동과 전혀 무관하다
//첨부파일이 처리된 경우에는 path에 null을 반환하게 한다
if(af.getPath() == null) {
return;//해당메소드 탈출
}else {
//파일업로드가 아닌 경우 응답으로 나갈 페이지 url이 담기는 변수가 path이다.
//이런 부분들을 스프링에서는 XXXXViewResolver라는 클래스가 지원하는 부분
res.sendRedirect(af.getPath());// -> notice/noticeList.jsp
}
}
else{//forward인 경우임 - url안바뀜, 화면은 바뀜, 유지됨. a페이지에서 쥐고 있는 정보를 b페이지에서도 사용가능함
//슬래쉬가 포함된 경우
if(af.getPath().contains("/")) {
RequestDispatcher view = req.getRequestDispatcher(af.getPath());
view.forward(req, res);
}
else if(af.getPath() == null) {//파일 업로드 처리시 ActionForward를 통해서 값을 리턴 받을때 문제가 발생됨. 이부분에 대한 해결 프로세스 추가하였다.
logger.info("path가 null일때");
}
//슬래쉬가 미포함인 경우
//-> 슬래쉬가 포함되었다는건 응답으로 나가는 마임타입이 html이다. path.append(notice/noticeList.jsp)
//1. json 포맷이라면 당연히 없음
//2. 문자열 형식일때 - ReactJS와 같이 이종간의 언어가 뷰계층을 담당할 때 필수템
//3. null일때 - 이미지 업로드 처리시나 첨부파일 처리시에는 리턴으로 나갈 값이 필요없다.
else {
logger.info("슬래쉬가 미포함인 경우 ===> " + af.getPath());
res.setCharacterEncoding("utf-8");
res.setContentType("text/plain;utf-8");
PrintWriter out = res.getWriter();
out.print(af.getPath());
return;
}
}
const selectLocalImage = (event) => {
console.log('selectLocalImage');
//e.preventDefault();이벤트 버블링 방어 코드 작성 - submit이슈, <button type=submit|button>
//quill에디터에서 이미지를 클릭했을 때 실제 화면에서는 <input type=file>생성해 주자
//이미지를 선택하면 선택하자마자 백엔드에 요청을 post방식으로 넘긴다 - 8000번 서버의 pds폴더에 선택한 이미지를 업로드 처리함
//파일을 (바이너리코드) 전송할땐 무조건 post방식으로 해야 함
const fileInput = document.createElement('input');//<input> - DOM API의 createElement를 사용하여 태그 생성하는 코드
fileInput.setAttribute('type', 'file');//<input type=file>
console.log("input.type " + fileInput.type);
//이미지 파일만 선택가능하도록 제한
fileInput.setAttribute("accept", "image/*");//* -> image/png , image/gif, image/jpg
fileInput.setAttribute("name", "image");//req.getParameter("image"); -> <input name="image"/>
fileInput.click();
input
엘리먼트를 만든다. <input>
fileInput.setAttribute('type','file')
: <input type = file>
setAttribute를 통해서 태그 속성값을 부여하고 있다.
fileInput.setAttribute("name", "image"); <input type = file name = image>
로 한다.
fileInput.addEventListener("change", function () { // change 이벤트로 input 값이 바뀌면 실행
const formData = new FormData();
const file = fileInput.files[0];
formData.append('image', file);
$.ajax({//<form method=post enctype=multipart/form-data/> 이럴경우 req.getParameter사용이 불가함 -> cos.jar
type: 'post',
enctype: 'multipart/form-data',
url: '/notice/imageUpload.gd',
data: formData,
processData: false,
contentType: false,
success: function (response) {
console.log('avatar.png'+response);//avartar.png
const url = "http://localhost:8000/notice/imageGet.gd?imageName="+response;//
const range = quill.getSelection().index;
quill.setSelection(range, 1);
quill.clipboard.dangerouslyPasteHTML(range,
'<img src='+url+' style="width:100%;height: auto;" alt="image" />');
}
이 부분은 비동기적으로 처리가 되는데 이 ajax가 요청을 보내게되면,
quill.clipboard.dangerouslyPasteHTML
Input 값이 바뀔 떄 마다 이벤트가 바뀐다.
formData 참고
formData.append(name, value) – name과 value를 가진 폼 필드를 추가한다.
formData.append('image', file);
- image 이름에 file 폼 필드를 추가
- 내부동작은 어떻게 동작하는가?
<form method=post enctype=multipart/form-data/> 이럴경우 req.getParameter사용이 불가함 -> cos.jar를 사용한다.
req.getParameter은 서블릿에 넘겨주기 때문에, 이 부분이 중요하다.
원래는 바이너리 타입으로 받으면 req.getParameter를 못해서
그래서 cos.jar를 이용하여 작성했다.
multipart/form-data : 바이너리 파일을 보낼 때 사용한다. (enctype=multipart/form-data를 사용하면 req.getParameter 사용하지 못한다.(null값만 찍힘))
성공시에 url 값을 리턴하는데, const url = "http://localhost:8000/notice/imageGet.gd?imageName="+response;
이런 형식을 띈다.
http://localhost:8000/notice/imageGet.gd?imageName=JSP.png
포스트맨 Test Case) url로 서버에 요청을 보내보자.
url 바로 접속 case
이렇게 사진을 리턴하는것을 볼 수 있다.
url에 /
슬래시가 들어가면 마임타입이 HTML이다. 앞서 얘기했던 것 처럼 url에
지금 예제는 이미지일 경우이다. 리턴형식으로 url로 이미지를 리턴하지 않는가?
다르게 단위테스트를 진행해보겠다
http://localhost:8000/notice/imageGet.gd?imageName=오라클.png
잘 실행되는것을 알 수 있다.
이 부분이 비동기적으로 처리되는 부분이다.(페이지에서 사진을 노출시킬 떄 사용)
$.ajax({//<form method=post enctype=multipart/form-data/> 이럴경우 req.getParameter사용이 불가함 -> cos.jar
type: 'post',
enctype: 'multipart/form-data',
url: '/notice/imageUpload.gd',
data: formData,
processData: false,
contentType: false,
success: function (response) {
console.log('avatar.png'+response);//avartar.png
const url = "http://localhost:8000/notice/imageGet.gd?imageName="+response;//
const range = quill.getSelection().index;
quill.setSelection(range, 1);
quill.clipboard.dangerouslyPasteHTML(range,
'<img src='+url+' style="width:100%;height: auto;" alt="image" />');
}
성공시 처리 부분을 보면 직관적으로 알 수 있다.
quill.getSelection().
: API 참고하자. 간단하게 요약하면,
quill.clipboard.dangerouslyPasteHTML
: 이 부분에서 이미지 삽입이 일어난다.const html = quill.root.innerHTML;
console.log(html);
quill.on('text-change', (delta, oldDelta, source) => {
console.log('글자가 입력될때 마다 호출');
console.log(quill.root.innerHTML);
//console.log(source);//user
//console.log(delta);
//console.log(oldDelta);
}); ////////////// end of onchage - 텍스트 내용이 변경되었을 때 발동
quill.getModule('toolbar').addHandler('image', () => {
console.log('image가 변경되었을때');
selectLocalImage();
})
정리하면,
2. 이 응답을 누가받는가? .gd가 아니기 떄문에 서블릿을 경유하지않는다.
3. 그렇다면 어떻게 처리하는가?
이미지 업로드 시 ajax를 보면 요청 url이
[JS]
/notice/imageUpload.gd를 요청한다. 즉. 이 말은 "이미지 업로드 시" 에만 서블릿을 경유한다는 얘기다.
사용자가 이미지를 업로드 했을 떄, 이 url( /notice/imageUpload.gd)이 요청된다.
[자바처리]
FrontMvc에서 (~~.gd) url를 가로챈다.
upmu[0 : notice] + upmu[1 : imageUpload] -> NoticeController로 간다.
NoticeController에서(if(imageUpload))로 가고 -> nlogic를 탄 다음 Map 객체를 리턴받는다.
FrontMVC에 돌아와서 forward 실시 -> 슬래시도 아니고 null도 아니기 떄문에 마지막 else문 실행한다.
[JS]
success: function (response) {
console.log('avatar.png'+response);//avartar.png
const url = "http://localhost:8000/notice/imageGet.gd?imageName="+response;//
const range = quill.getSelection().index;
quill.setSelection(range, 1);
quill.clipboard.dangerouslyPasteHTML(range,
'<img src='+url+' style="width:100%;height: auto;" alt="image" />');
}
res.setCharacterEncoding("utf-8");
res.setContentType("text/plain;utf-8");
PrintWriter out = res.getWriter();
out.print(af.getPath());
PrintWriter
으로 화면에 값을 뿌려준다. 즉, 이 부분이 응답페이지로 나간다는 얘기다.그러면 response는 = JSP7.png 이 되고, 이 값을 다시 url에 넣고
그 값을 quill라이브러리 메소드를 통해 이미지 값을 넣는다.
추가적으로 마임타입까지 jsp와 서블릿이 동일하다는 것을 볼 수 있다.
contentType="text/html; charset=UTF-8"
== res.setContentType("text/plain;utf-8");
밑에 NoticeController 에서 이미지Get를 호출하는 코드는 JS에서 찾아볼수가 없는데 어디서 호출하는거야?
/notice/imageUpload.gd에 POST 요청을 보내고, 성공적인 응답을 받으면 응답으로부터 이미지 URL을 추출하여 Quill 에디터에 이미지를 삽입한다.
이미지 업로드 후에 성공적인 응답을 받은 경우, 서버는 이미지를 다시 가져오기 위해 /notice/imageGet.gd에 대한 요청을 처리한다.
이 부분이 위에서 const url로 요청url를 가져온뒤,
이미지 업로드 후의 성공적인 처리에서는 /notice/imageGet.gd로의 요청이 발생하게 되고, 서블릿에서는 해당 요청에 대한 처리를 else if("imageGet".equals(upmu[1])) 부분에서 수행하게된다.
quill.clipboard.dangerouslyPasteHTML(range,
'<img src='+url+' style="width:100%;height: auto;" alt="image" />');
},
else if("imageUpload".equals(upmu[1])) {//delete
//quill editor에서 이미지를 선택하면 해당 요청을 호출함 - 비동기처리
//post이면서 enctype 바이너리인 경우 전송이 안됨
MultipartRequest multi = null;
String realFolder = "C:\\Program Files\\workspace_jsp\\nae2Gym\\src\\main\\webapp\\pds";
String encType = "utf-8";
int maxSize = 5*1024*1024;
try {
multi = new MultipartRequest(req, realFolder, maxSize, encType, new DefaultFileRenamePolicy());
} catch (Exception e) {
logger.info(e.toString());
}
Map<String, Object> rmap = nLogic.imageUpload(multi, realFolder);
String temp = null;
if(rmap !=null) {
temp = rmap.get("bs_file").toString();
}
int end = path.toString().length();// -> notice/
path.delete(0, end);
path.append(temp);//url이 전달되는게 아니라 json형식 즉 문자열이 전달됨
}// end of imageUpload
//http://localhost:8000/notice/imageGet.gd?imageName=avatar25.png
else if("imageGet".equals(upmu[1])) {//첨부파일을 처리할 때 다운로드 처리하는 화면에서 사용할 코드 소개함
String b_file = req.getParameter("imageName");// avartar.png
logger.info("111 => " +b_file);
String filePath = "C:\\Program Files\\workspace_jsp\\nae2Gym\\src\\main\\webapp\\pds" ;
File file = new File(filePath, b_file.trim());
logger.info("222 => " + file );
String mimeType = req.getServletContext().getMimeType(file.toString());
if(mimeType == null) {
res.setContentType("application/octet-stream");
}
String downName = null;
FileInputStream fis = null;
ServletOutputStream sos = null;
try {
if(req.getHeader("user-agent").indexOf("MSIE")==-1) {
downName = new String(b_file.getBytes("UTF-8"), "8859_1");//국제 표준규격- 다국어지원
}else {
downName = new String(b_file.getBytes("EUC-KR"), "8859_1"); //한국 표준 규격
}
res.setHeader("Content-Disposition", "attachment;filename="+downName);
logger.info("333");
fis = new FileInputStream(file);
logger.info(fis);
sos = res.getOutputStream();
byte b[] = new byte[1024*10];
int data = 0;
logger.info("444");
while((data=(fis.read(b,0, b.length)))!=-1) {
sos.write(b,0,data);
}
sos.flush();//FileInputStream을 사용해서 file객체를 읽음- 메모리에 쌓인 정보를 비우는 메소드 호출
isRedirect = true;//null처리를 해둠
logger.info(path);
int end = path.toString().length();// -> notice/
path.delete(0, end);
path = null;
} catch (Exception e) {
logger.info(e.toString());
} finally {
try {
if(sos !=null) sos.close();
if(fis !=null) fis.close();
} catch (Exception e2) {
// TODO: handle exception
}
}
}// end of imageGet
//파일 업로드 시와 마찬가지로 파일 정보를 얻어올때도 출력페이지를 내보낼 필요가 없는 경우임
if(path !=null) {//응답페이지가 존재하는 경우만 처리할것
af.setPath(path.toString());//이 대로 두면 NullPointerException대상임
}else {
af.setPath(null);//이 대로 두면 NullPointerException대상임
}
af.setRedirect(isRedirect);//true-> ActionForward - isRedirect - false->true
return af;
}
}
else if("imageUpload".equals(upmu[1])) {//delete
//quill editor에서 이미지를 선택하면 해당 요청을 호출함 - 비동기처리
//post이면서 enctype 바이너리인 경우 전송이 안됨
MultipartRequest multi = null;
String realFolder = "C:\\Program Files\\workspace_jsp\\nae2Gym\\src\\main\\webapp\\pds";
String encType = "utf-8";
int maxSize = 5*1024*1024;
try {
multi = new MultipartRequest(req, realFolder, maxSize, encType, new DefaultFileRenamePolicy());
} catch (Exception e) {
logger.info(e.toString());
}
Map<String, Object> rmap = nLogic.imageUpload(multi, realFolder);
String temp = null;
if(rmap !=null) {
temp = rmap.get("bs_file").toString();
}
int end = path.toString().length();// -> notice/
path.delete(0, end);
path.append(temp);//url이 전달되는게 아니라 json형식 즉 문자열이 전달됨
}
MultipartRequest클래스는 무엇일까?
// ↓ request 객체, ↓ 저장될 서버 경로, ↓ 파일 최대 크기, ↓ 인코딩 방식, ↓ 같은 이름의 파일명 방지 처리
// (HttpServletRequest request, String saveDirectory, int maxPostSize, String encoding, FileRenamePolicy policy)
// 아래와 같이 MultipartRequest를 생성만 해주면 파일이 업로드 된다.(파일 자체의 업로드 완료)
MultipartRequest multi = new MultipartRequest(request, savePath, sizeLimit, "utf-8", new DefaultFileRenamePolicy());
req : request 객체
realFolder : 저장될 서버 경로를 의미한다.
maxSize : 파일 최대 크기
encType : 인코딩방식
DefaultFileRenamePolicy : 중복제거(같은 이름의 파일명 방지 처리)
MultipartRequest를 "생성"만 해주면 파일이 업로드 된다.(파일 자체의 업로드 완료)
그리고 rmap객체를 생성하는데, nLogic.imageUpload로 부터 처리된 값을 rmap이 받는다.
nLogic.imageUpload 파라미터엔 2개의 인자가 들어가며
multi : 파일 클래스를 참조하는 참조변수(세팅완료)
realFolder : 파일 절대경로
왜 2가지 인자를 넣는것일까?
public Map<String, Object> imageUpload(MultipartRequest multi, String realFolder) {
logger.info("imageUpload");
Map<String,Object> pMap = new HashMap<>();
Enumeration<String> files = multi.getFileNames();
String fullPah = null;//파일 정보에 대한 전체경로
String filename = null;//파일이름
//첨부파일이 있다면?
if(files !=null) {
//파일 이름을 클래스로 정의하는 객체 - 파일객체가 생성되었다고 해서 그 안에 내용까지 포함하진 않음
//파일 크기를 계산해주는 메소드 지원함
File file = null;
while(files.hasMoreElements()) {
String fname = files.nextElement();
filename = multi.getFilesystemName(fname);
pMap.put("bs_file", filename);//avartar.png
//File객체 생성하기
file = new File(realFolder+"\\"+filename);
}
//첨부파일의 크기를 담기
double size = 0;
if(file !=null) {
size = file.length();
size = size/(1024);
pMap.put("bs_size", size);
}
}
return pMap;
//return pMap;
}
Enumeration<String> files = multi.getFileNames();
<input type = "file" name = "uploadFile">
태그가 폼 요소에 있다면,
getFileNames() 메소드는 uploadFile이란
파라미터의 이름을 저장한
Enumeration 객체를 반환한다.
fileInput.setAttribute("name", "image");
name = image가 들어가기 때문에
이 원리를 그대로 적용해보면, files는 name속성의 값(image)들을 읽어들이고 그 값을 Enumeration 객체타입으로 반환한다.
if(files !=null) {
//파일 이름을 클래스로 정의하는 객체 - 파일객체가 생성되었다고 해서 그 안에 내용까지 포함하진 않음
//파일 크기를 계산해주는 메소드 지원함
File file = null;
while(files.hasMoreElements()) {
String fname = files.nextElement();
filename = multi.getFilesystemName(fname);
pMap.put("bs_file", filename);//avartar.png
//File객체 생성하기
file = new File(realFolder+"\\"+filename);
}
//첨부파일의 크기를 담기
double size = 0;
if(file !=null) {
size = file.length();
size = size/(1024);
pMap.put("bs_size", size);
}
}
다중 파일 업로드시 getFileNames()
를 통해 모든 파일의 이름을 얻어온다.
getFilesystemName(name)
: 을 사용하여 실제파일명을 얻어온다.
files는 Enumeration 객체타입이다. 즉(input의 name을 가지고 있다.) 쉽게 말하면, image이다.
File file = null 로 선언함으로서
while문을 반복한다.
files.nextElement()
Key값이 들어간다(이미지명).하나 눈여겨 볼 점은 파라미터로 받은 multi를 기준으로 getFileSystemName,getFileNames메소드를 사용했다.
읽어온 값을 pMap.put("bs_file", filename)
으로 저장한다.
file = new File(realFolder[절대경로]+"\\" + filename);
만약에 filename : avatar.png
realFolder : C:\Program Files 라고 가정했을 때,
File(C:\Program Files\avatar.png) 이렇게 들어간다.
그리고 파일 사이즈를 구한다. 이 부분은 while문 안쪽에서 작업할 필요가 없다.
최종적으로 나온 FIle의 사이즈를 구하는게 더 합리적이기 때문에 바깥에 위치시켰다.
그리고 최종적으로 pMap을 리턴한다
pMap엔 뭐가 담겨있을까?
{"bs_file" : filename, "bs_size" : size } 이렇게 담겨있다. 이렇게 들어간 값을 리턴한다.
다시 Controller로 돌아와서
Map<String, Object> rmap = nLogic.imageUpload(multi, realFolder);
String temp = null;
if(rmap !=null) {
temp = rmap.get("bs_file").toString();
}
int end = path.toString().length();// -> notice/
path.delete(0, end);
path.append(temp);//url이 전달되는게 아니라 json형식 즉 문자열이 전달됨
}
리턴받은 rmap에 {"bs_file" : filename, "bs_size" : size } 를 저장한다.
rmap에 값이 들어 있다면, 파일의 이름을 알아야하는데, 그 방법을 temp = rmap.get("bs_file").toString()
으로 구했다.
그래서 이 url를 제거해야된다.
path.delete(0,end)
하고 path.append(temp(이미지이름)); 를 붙였다.
path.append(temp(이미지이름)) 포인트 부분이다.
path : 이미지이름
하지만 forward 방식이기 때문에 url문제가 없음
업로드 시 pds폴더에 잘 저장되는것을 확인했다.
같은 이미지 이름을 첨부할 시 다른 이름으로 저장 되는 부분을 확인 해 볼 수 있다. DefaultFileRenamePolicy()
MultipartRequest(req, realFolder, maxSize, encType, new DefaultFileRenamePolicy());
지금까지 업로드 하는 코드를 살펴봤다. 이번엔 첨부파일을 Get하는 메소드를 설계해보자.
image src = "서블릿태움" 해당 주소로 서블릿 태울 수 있다.
요청 시기
업로드완료 후 JS에서 Ajax로 성공 시 const url = 값을 넣는다.
이 url를 이미지에 넣는데, 이미지경로도 서블릿을 경유한다.(요청한다) 그 떄 NoiceController가 받아서 처리과정
이런식으로 어떻게 구현할까?
else if("imageGet".equals(upmu[1])) {//첨부파일을 처리할 때 다운로드 처리하는 화면에서 사용할 코드 소개함
String b_file = req.getParameter("imageName");// avartar.png
logger.info("111 => " +b_file);
String filePath = "C:\\Program Files\\workspace_jsp\\nae2Gym\\src\\main\\webapp\\pds" ;
File file = new File(filePath, b_file.trim());
logger.info("222 => " + file );
String mimeType = req.getServletContext().getMimeType(file.toString());
if(mimeType == null) {
res.setContentType("application/octet-stream");
}
String downName = null;
FileInputStream fis = null;
ServletOutputStream sos = null;
try {
if(req.getHeader("user-agent").indexOf("MSIE")==-1) {
downName = new String(b_file.getBytes("UTF-8"), "8859_1");//국제 표준규격- 다국어지원
}else {
downName = new String(b_file.getBytes("EUC-KR"), "8859_1"); //한국 표준 규격
}
res.setHeader("Content-Disposition", "attachment;filename="+downName);
logger.info("333");
fis = new FileInputStream(file);
logger.info(fis);
sos = res.getOutputStream();
byte b[] = new byte[1024*10];
int data = 0;
logger.info("444");
while((data=(fis.read(b,0, b.length)))!=-1) {
sos.write(b,0,data);
}
sos.flush();//FileInputStream을 사용해서 file객체를 읽음- 메모리에 쌓인 정보를 비우는 메소드 호출
isRedirect = true;//null처리를 해둠
logger.info(path);
int end = path.toString().length();// -> notice/
path.delete(0, end);
path = null;
} catch (Exception e) {
logger.info(e.toString());
} finally {
try {
if(sos !=null) sos.close();
if(fis !=null) fis.close();
} catch (Exception e2) {
// TODO: handle exception
}
}
}// end of imageGet
내가 몰랐던 부분
필자는 먼저 Jsp에서 서로 값을 받을 때, setAttribute, getAttribute로 받아야 되는 줄 알았다. (마치 불문율처럼..ㅋㅋ)
근데 JS에서 비동기처리로 보내는 Ajax url 성공시 url 이미지 넣을 떄 src로 imageName을 req.getParameter
받을 수 있다니.. 이런 생각을 못했다.
url로 쿼리스트링 사용자에대한 입력 값이 나오는데 이 부분을 getParameter로 받을 수 있다
정확히 말하면, url를 이미지태그에 삽입 시 src = "url"이 요청 되는데 이 때, "호출"이 일어난다. 그래서 그 호출 시 imageName의 avatar.png를 가져오는 것이다.✅
String b_file = req.getParameter("imageName"); 이면
b_file = response가 저장된다.
request 객체의 getParameter() 메서드로 사용자가 입력한 데이터를 가져올수있다
<from> 데이터를 살펴보면
<input type = "text" name = "id">
<input type = "password" name = "pass">
위와 같이 데이터를 입력받고 submit으로 서블릿을 호출할때
request.getParameter("name값") 으로 가져올 수 있다.
GET방식과 POST방식 둘다 request.Parameter()를 사용할 수 있다.
내가 헷갈린 부분은 enctype=multipart/form-data 이 부분이 때문이다.
form 태그 속성에 enctype=multipart/form-data 바이너리 속성이 들어가면 req.getParameter() 사용하면 null이 나온다.
지금은 폼태그가 아니라 ajax로 보내는 부분인데 혼동했다.
String b_file = req.getParameter("imageName");// avartar.png
logger.info("111 => " +b_file);
String filePath = "C:\\Program Files\\workspace_jsp\\nae2Gym\\src\\main\\webapp\\pds" ;
File file = new File(filePath, b_file.trim());
logger.info("222 => " + file );
String mimeType = req.getServletContext().getMimeType(file.toString());
if(mimeType == null) {
res.setContentType("application/octet-stream");
}
mimeType = req.getServletContext().getMimeType(file.toString());
getServletContext(): 페이지에 대한 서블릿 실행 환경 정보를 담고 있는 application 내장 객체를 리턴한다.
서블릿 컨텍스트를 통해 MIME 타입을 가져오는 코드
res.setContentType("application/octet-stream");
MIME의 개별 타입 중 application에 속하는 타입인디, 8비트 단위의 binary data라는 뜻
"특별히 표현할 수 있는 프로그램이 존재하지 않는 데이터의 경우 기본값으로 octet-stream을 사용한다." = 브라우저가 보통 자동으로 실행하지 않거나 실행할지 묻기도 하는 타입이다
String downName = null;
FileInputStream fis = null;
ServletOutputStream sos = null;
try {
if(req.getHeader("user-agent").indexOf("MSIE")==-1) {
downName = new String(b_file.getBytes("UTF-8"), "8859_1");//국제 표준규격- 다국어지원
}else {
downName = new String(b_file.getBytes("EUC-KR"), "8859_1"); //한국 표준 규격
}
res.setHeader("Content-Disposition", "attachment;filename="+downName);
logger.info("333");
fis = new FileInputStream(file);
logger.info(fis);
sos = res.getOutputStream();
byte b[] = new byte[1024*10];
int data = 0;
logger.info("444");
while((data=(fis.read(b,0, b.length)))!=-1) {
sos.write(b,0,data);
}
ServletOutputSteam : 파일을 읽어올 때에는 FileInputStream으로 읽어온 뒤 브라우저에 출력할 때에는 ServletOutputStream을 사용한다.
FileInputStream: 파일을 읽기 위한 스트림이다.
파일을 바이너리 형태로 읽어와서 메모리에 저장한다.
파일을 읽어와서 바이트 배열에 저장한다.
ServletOutputStream: 서블릿에서 클라이언트에게 데이터를 출력하기 위한 스트림이다.
바이트 배열을 클라이언트에게 전송한다.
서블릿 컨테이너는 이 출력 스트림을 통해 데이터를 클라이언트에게 보낸다.
(req.getHeader("user-agent").indexOf("MSIE")==-1
브라우저의 종류를 체크한다.
MSIE"는 Internet Explorer의 User-Agent 문자열에 해당한다.
"MSIE"가 없으면(즉, IE가 아니면), 다른 브라우저로 간주한다.
downName = new String(b_file.getBytes("UTF-8"), "8859_1")
b_file.getBytes("UTF-8")은 파일 이름을 UTF-8 문자열로 "인코딩"한다.
그리고 다시 new String(..., "8859_1") 은 이 UTF-8 문자열을 ISO-8859-1(또는 Latin-1)로 디코딩하여 새로운 문자열을 생성한다.
b_file : 이미지이름
res.setHeader("Content-Disposition", "attachment;filename="+downName):
(HTTP 응답 헤더를 설정)
File file = new File(filePath, b_file.trim());
fis = new FileInputStream(file);
- java.io.FileInputStream@166edcf2
while((data=(fis.read(b,0, b.length)))!=-1) {
sos.write(b,0,data);
}
sos.flush();
- 최대 b.length 바이트만큼 데이터를 읽어온다.
- 읽어온 데이터는 b 바이트 배열에 저장되고, 읽은 바이트 수가 data 변수에 대입된다.
- 만약 더 이상 읽을 데이터가 없다면 -1을 반환한다
- 읽어온 데이터를 ServletOutputStream
- (sos)을 통해 클라이언트로 전송한다.
- write(b, 0, data)는 배열 b의 처음부터 읽은 바이트 수 data까지를 출력한다.
isRedirect = true;//null처리를 해둠
logger.info(path);
int end = path.toString().length();// -> notice/
path.delete(0, end);
path = null;
} catch (Exception e) {
logger.info(e.toString());
} finally {
try {
if(sos !=null) sos.close();
if(fis !=null) fis.close();
} catch (Exception e2) {
// TODO: handle exception
}
}
}// end of imageGet
path를 null 처리 했기 때문에, isRedirect 상관이없다.
왜 null 처리를 했을까?
앞에서 얘기했던 것 처럼 파일 업로드 시
와 마찬가지로 파일 정보를 얻어올 때도
출력페이지를 내보낼 필요가 없다.
af.setPath(path.toString());
af.setRedirect(isRedirect);
이전에는 이 코드였지만, path = null를 넣었기 때문에 null 처리를 진행해야된다.
if(path !=null) {//응답페이지가 존재하는 경우만 처리할것
af.setPath(path.toString());//이 대로 두면 NullPointerException대상임
}else {
af.setPath(null);//이 대로 두면 NullPointerException대상임
}
af.setRedirect(isRedirect);//true-> ActionForward - isRedirect - false->true
return af;
}
if(af !=null) {
if(af.isRedirect()) {//true라는 건 sendRedirect인 경우임
//첨부파일을 업로드 하는 것은 페이지 이동과 전혀 무관하다
//첨부파일이 처리된 경우에는 path에 null을 반환하게 한다
if(af.getPath() == null) {
return;//해당메소드 탈출
}else {
//파일업로드가 아닌 경우 응답으로 나갈 페이지 url이 담기는 변수가 path이다.
//이런 부분들을 스프링에서는 XXXXViewResolver라는 클래스가 지원하는 부분
res.sendRedirect(af.getPath());// -> notice/noticeList.jsp
}
}
else{//forward인 경우임 - url안바뀜, 화면은 바뀜, 유지됨. a페이지에서 쥐고 있는 정보를 b페이지에서도 사용가능함
//슬래쉬가 포함된 경우
if(af.getPath().contains("/")) {
RequestDispatcher view = req.getRequestDispatcher(af.getPath());
view.forward(req, res);
}
else if(af.getPath() == null) {//파일 업로드 처리시 ActionForward를 통해서 값을 리턴 받을때 문제가 발생됨. 이부분에 대한 해결 프로세스 추가하였다.
logger.info("path가 null일때");
}
//슬래쉬가 미포함인 경우
//-> 슬래쉬가 포함되었다는건 응답으로 나가는 마임타입이 html이다. path.append(notice/noticeList.jsp)
//1. json 포맷이라면 당연히 없음
//2. 문자열 형식일때 - ReactJS와 같이 이종간의 언어가 뷰계층을 담당할 때 필수템
//3. null일때 - 이미지 업로드 처리시나 첨부파일 처리시에는 리턴으로 나갈 값이 필요없다.
else {
logger.info("슬래쉬가 미포함인 경우 ===> " + af.getPath());
res.setCharacterEncoding("utf-8");
res.setContentType("text/plain;utf-8");
PrintWriter out = res.getWriter();
out.print(af.getPath());
return;
}
}
}/
- SendRedirect인 경우 (af.isRedirect())
첨부파일이 처리된 경우에는 path에 null를 반환하게 된다.
============파일 업로드인 경우==========
if(af.getPath() == null) {
return;//해당메소드 탈출
}
파일 업로드가 아닌경우
else {
//파일업로드가 아닌 경우 응답으로 나갈 페이지 url이 담기는 변수가 path이다.
//이런 부분들을 스프링에서는 XXXXViewResolver라는 클래스가 지원하는 부분
res.sendRedirect(af.getPath());// -> notice/noticeList.jsp
}
- Forward인 경우 (af.isRedirect())
if(af.getPath().contains("/")) {
RequestDispatcher view = req.getRequestDispatcher(af.getPath());
view.forward(req, res);
}
슬래시가 포함되어 있다는건, 응답으로 나가는 마임타입이 HTML이다.
슬래쉬가 미포함인 경우
Json 포맷이라면 당연히 없음
문자열 형식일때 - ReactJS와 같이 이종간의 언어가 뷰계층을 담당할 때 필수템
null일때 - 이미지 업로드 처리시나 첨부파일 처리시에는 리턴으로 나갈 값이 필요없다.
[null 일 때,] :
else if(af.getPath() == null) {//파일 업로드 처리시 ActionForward를 통해서 값을 리턴 받을때 문제가 발생됨. 이부분에 대한 해결 프로세스 추가하였다.
logger.info("path가 null일때");
}
[슬래시가 미포함인경우] Json, 문자열
else {
logger.info("슬래쉬가 미포함인 경우 ===> " + af.getPath());
res.setCharacterEncoding("utf-8");
res.setContentType("text/plain;utf-8");
PrintWriter out = res.getWriter();
out.print(af.getPath());
return;
}