728x90
300x250

[JSP] 21. Jsp/Servlet(MVC) Maven 기반의 다중 업로드, 다운로드, 삭제 구현(1)


이 글의 소스코드를 수정해서 삭제 기능까지 추가해보도록 하겠다.


1. [JSP] 12. Jsp/Servlet(MVC) Maven 기반의 다중 파일 업로드, 다운로드 구현(1)

https://yyman.tistory.com/1414

2. [JSP] 12. Jsp/Servlet(MVC) Maven 기반의 다중 파일 업로드, 다운로드 구현(2)

https://yyman.tistory.com/1415



1. 결과


입력부터 다운로드, 삭제에 이르는 전 과정을 담아보았다.



그림 1. 다중 업로드 작업




그림 2. 다중 업로드된 모습





그림 3. 파일을 찾을 수 없을 때 반응




그림 4. 다운로드 모습




그림 5. 삭제 모습



그림 6. 파일이 다 지워질 경우에 폴더 삭제까지 하기



그림 7. 프로젝트 구성도(변경됨)




2. 변경 - Controller (FrontController.java)


package com.fileWeb.controller;


import java.io.IOException;

import java.util.HashMap;

import java.util.Map;


import javax.servlet.ServletConfig;

import javax.servlet.ServletException;

import javax.servlet.http.HttpServlet;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;

import javax.servlet.http.HttpSession;


import com.fileWeb.util.HttpUtil;


/**

 * Servlet implementation class HomeController

 */

public class FrontController extends HttpServlet {

private static final long serialVersionUID = 1L;

       

private String charset = null;

    public FrontController() {

        super();

    }


protected void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {

doAction(req, res);

}

protected void doPost(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {

doAction(req, res);

}

// FrontController 패턴 & Command 패턴

protected void doAction(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {


ServletConfig sc = this.getServletConfig();

charset = sc.getInitParameter("charset");

req.setAttribute("charset", charset);

req.setCharacterEncoding(charset);

res.setContentType("text/html; charset=" + charset);

System.out.println(charset);

String uri = req.getRequestURI();

System.out.println("uri : " + uri);

String conPath = req.getContextPath();

System.out.println("conPath : " + conPath);

String command = uri.substring(conPath.length());

System.out.println("command : " + command);

Controller subController = null;

System.out.println("reqMapSize : " + req.getParameterMap().size());


if(command.equals("/board/insert.do")){

System.out.println("insert");

System.out.println("----------------");


    subController = new BoardInsertController();

    subController.execute(req, res);

   

}else if (command.equals("/board/insertResult.do")) {

System.out.println("insertResult");

System.out.println("----------------");


    subController = new BoardInsertResultController();

subController.execute(req, res);

}

else if(command.equals("/board/insertMultiResult.do")){

System.out.println("insertResult");

System.out.println("----------------");


    subController = new BoardInsertMultiResultController();

subController.execute(req, res);

}else if(command.equals("/board/download.do")) {

System.out.println("download");

System.out.println("----------------");

HttpUtil.fileDownload(req, res, null);

}else if(command.equals("/board/fileRemove.do")) {

System.out.println("remove");

System.out.println("----------------");

    HttpUtil.removeFile(req, res);

}

}

}



파일명: FrontController.java


[첨부(Attachments)]

FrontController-updated.zip





3. 변경 - Controller (BoardInsertMultiResultController.java)


변경 내용은 BoardInsertMultiResultController에 구현된 업로드 관련 소스를 HttpUtil로 이동시켰다.

파일명이 다소 너무 길어서 한 가지 이야기하면, 작명도 조금 연습하면 좋을 듯 싶다. (너무 긴 건 좋은 건 아니다.)


package com.fileWeb.controller;


import java.io.IOException;


import javax.servlet.ServletException;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;


import com.fileWeb.util.HttpUtil;


public class BoardInsertMultiResultController implements Controller {


@Override

public void execute(HttpServletRequest req, HttpServletResponse res) throws

ServletException, IOException {

HttpUtil.uploadFile(req, res);

HttpUtil.forward(req, res, "/WEB-INF/view/board/insertResult.jsp");

}

}


파일명: BoardInsertMultiResultController.java


[첨부(Attachments)]

BoardInsertMultiResultController-updated.zip






4. 변경 - Util 패키지로 변경 (HttpUtil.java)


다소 코드에 변화가 있었다.


변경 전: com.fileWeb.controller.HttpUtil.java

변경 후: com.fileWeb.util.HttpUtil.java




package com.fileWeb.util;


import java.io.File;

import java.io.FileInputStream;

import java.io.FileNotFoundException;

import java.io.IOException;

import java.io.InputStream;

import java.io.OutputStream;

import java.io.PrintWriter;

import java.util.HashMap;

import java.util.Iterator;

import java.util.List;

import java.util.Map;


import javax.servlet.RequestDispatcher;

import javax.servlet.ServletConfig;

import javax.servlet.ServletException;

import javax.servlet.http.HttpServlet;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;


import org.apache.commons.fileupload.FileItem;

import org.apache.commons.fileupload.disk.DiskFileItemFactory;

import org.apache.commons.fileupload.servlet.ServletFileUpload;



public class HttpUtil extends HttpServlet {

private static final long serialVersionUID = 1L;

private static String charset = null; // 문자열


private static int SIZETHRESHOLD = 4096;

private static String UPLOAD_FOLDER = "upload";

private static String UPLOAD_TMP_FOLDER = File.separator + "WEB-INF" + File.separator + "temp";

private static long MAX_UPLOAD_SIZE = 3 * 1024 * 1024;

private static Map<String, Object> reqMap = null; // req 정보(MultiRequest)

private static Map<Integer, Map<String, Object>> fileMap = null; // 다중 파일 지원

private static int num = 0;


public static void forward(HttpServletRequest req, HttpServletResponse res,

String path) throws ServletException, IOException {

try {

RequestDispatcher dispatcher = req.getRequestDispatcher(path);

dispatcher.forward(req, res);

}catch(Exception e) {

e.printStackTrace();

}

}

public static void uploadFile(HttpServletRequest req, HttpServletResponse res) throws

ServletException, IOException {


fileMap = new HashMap<Integer, Map<String, Object>>(); 

reqMap = new HashMap<>();

num = 1;

        

        PrintWriter out = res.getWriter();

        out.println("<HTML><HEAD><TITLE>Multipart Test</TITLE></HEAD><BODY>");

try {

            

            //디스크상의 프로젝트 실제 경로얻기

            //String contextRootPath = "c:" + File.separator + "upload";

String dirName = UPLOAD_FOLDER ; 

// String dirName = "upload"; 

String contextRootPath = req.getSession().getServletContext().getRealPath("/") + dirName;

            System.out.println("실제경로:" + contextRootPath);

            

            //1. 메모리나 파일로 업로드 파일 보관하는 FileItem의 Factory 설정

            DiskFileItemFactory diskFactory = new DiskFileItemFactory(); // 디스크 파일 아이템 공장

            diskFactory.setSizeThreshold(SIZETHRESHOLD); // 업로드시 사용할 임시 메모리

            // diskFactory.setSizeThreshold(4096); // 업로드시 사용할 임시 메모리

            diskFactory.setRepository(new File(contextRootPath + UPLOAD_TMP_FOLDER)); // 임시저장폴더

            // diskFactory.setRepository(new File(contextRootPath + UPLOAD_TMP_FOLDER)); // 임시저장폴더

            

            //2. 업로드 요청을 처리하는 ServletFileUpload생성

            ServletFileUpload upload = new ServletFileUpload(diskFactory);


            // upload.setSizeMax(3 * 1024 * 1024); //3MB : 전체 최대 업로드 파일 크기

            upload.setSizeMax(MAX_UPLOAD_SIZE); // 전체 최대 업로드 파일 크기

            

            //3. 업로드 요청파싱해서 FileItem 목록구함​​

            List<FileItem> items = upload.parseRequest(req); 


            Iterator<FileItem> iter = items.iterator(); //반복자(Iterator)로 받기​            

            while(iter.hasNext()) { //반목문으로 처리​    

                FileItem item = (FileItem) iter.next(); //아이템 얻기

                 //4. FileItem이 폼 입력 항목인지 여부에 따라 알맞은 처리

                

                if(item.isFormField()){ 

                //파일이 아닌경우

                    processFormField(out, item);

                    

                } else {

                //파일인 경우

                System.out.println("오류:" + item.getName());

               

                // 버그 개선 item 이름값 비어있을 때

                if ( item.getName() != "") {

                processUploadFile(out, item, contextRootPath);

                }

                System.out.println("오류2:");

                }

            }

            

        } catch(Exception e) {

            out.println("<PRE>");

            e.printStackTrace(out);

            out.println("</PRE>");

        }

out.println( "usrID(Map): " + reqMap.get("usrID") );

out.println( "usrPasswd(Map):" + reqMap.get("usrPasswd") );

        

        out.println("</BODY></HTML>");

// req.setAttribute("usrID", reqMap.get("usrID"));

// req.setAttribute("login", 1);//Object Type으로 넘어감

        req.setAttribute("reqMap", reqMap);

        req.setAttribute("fileMap", fileMap);

        

        // 방법3

        for( Integer key : fileMap.keySet() ){


            Map<String, Object> fileMapNode = fileMap.get(key);

            System.out.println( String.format("키 : %s, 값: %s", key, fileMapNode.get("fileName") ));

            

        }

        

// System.out.println("오류3:" + reqMap.get("usrID"));

}


//업로드한 정보가 파일인경우 처리

private static void processUploadFile(PrintWriter out, FileItem item, String contextRootPath)

throws Exception {

Map<String, Object> fileNode = new HashMap<String, Object>();


String dirName = UPLOAD_FOLDER ; 

String name = item.getFieldName(); // 파일의 필드 이름 얻기

String fileName = item.getName(); // 파일명 얻기

// 임시 - 실제 원본 이름 추출

File originalFile = new File(fileName);

String originalFileName = originalFile.getName();

System.out.println("임시:" + originalFileName );

String contentType = item.getContentType(); // 컨텐츠 타입 얻기

long fileSize = item.getSize(); // 파일의 크기 얻기

// 업로드 파일명을 현재시간으로 변경후 저장

String fileExt = fileName.substring(fileName.lastIndexOf("."));

String uploadedFileName = System.currentTimeMillis() + ""; 

System.out.println(fileExt);

System.out.println(uploadedFileName);

// 저장할 절대 경로로 파일 객체 생성

String realUploadFile = File.separator + dirName + File.separator + uploadedFileName;

System.out.println("실제 저장직전폴더:" + contextRootPath + realUploadFile);

File uploadedFile = new File(contextRootPath + realUploadFile);

item.write(uploadedFile); //파일 저장

//========== 뷰단에 출력 =========//

out.println("<P>");

out.println("파라미터 이름:" + name + "<BR>");

out.println("파일 이름:" + fileName + "<BR>");

out.println("콘텐츠 타입:" + contentType + "<BR>");

out.println("파일 사이즈:" + fileSize + "<BR>");

//확장자가 이미지인겨우 이미지 출력

if(".jpg.jpeg.bmp.png.gif".contains(fileExt.toLowerCase())) {

out.println("<IMG SRC='upload/" 

+ uploadedFileName 

+ "' width='300'><BR>");

}

out.println("</P>");

out.println("<HR>");

out.println("실제저장경로 : "+uploadedFile.getPath()+"<BR>");

out.println("<HR>");

// 파일 정보

fileNode.put("name", name);

fileNode.put("fileName", originalFileName);

fileNode.put("contentType", contentType);

fileNode.put("fileSize", fileSize);

fileNode.put("fileExt", fileExt);

fileNode.put("uploadedFileName", uploadedFileName);

fileNode.put("realName", uploadedFile.getName());

fileNode.put("realPath", uploadedFile.getPath());

fileMap.put(num, fileNode);

num++;

}

private static void processFormField(PrintWriter out, FileItem item) 

throws Exception{

String name = item.getFieldName(); //필드명 얻기

Object value = item.getString("UTF-8"); //UTF-8형식으로 필드에 대한 값읽기

// out.println(name + ":" + value + "<BR>"); //출력

reqMap.put(name, value);

}

/*

* 다운로드(Download)

*/

public static void fileDownload(HttpServletRequest req, HttpServletResponse res,

String path) throws ServletException, IOException {


charset = (String) req.getAttribute("charset");

System.out.println(charset);

PrintWriter out = res.getWriter();

// 파일 업로드된 경로

String root = req.getSession().getServletContext().getRealPath("/");

String savePath = root + UPLOAD_FOLDER + File.separator + UPLOAD_FOLDER ;

// String savePath = root + "upload" + File.separator + "upload";

// 서버에 실제 저장된 파일명

String filename = "1601561525229" ;

System.out.println("파일 실제 폴더경로:" + savePath);

System.out.println("실제 파일명:" + filename);

// 실제 내보낼 파일명

String orgfilename = "license한글.txt" ;

req.setCharacterEncoding(charset);

res.setCharacterEncoding(charset);

InputStream in = null;

OutputStream os = null;

File file = null;

boolean skip = false;

String client = "";

try{

    // 파일을 읽어 스트림에 담기

    try{

        file = new File(savePath, filename);

        in = new FileInputStream(file);

    }catch(FileNotFoundException fe){

        skip = true;

    }

    client = req.getHeader("User-Agent");

    // 파일 다운로드 헤더 지정

    res.reset() ;

    res.setContentType("application/octet-stream");

    res.setHeader("Content-Description", "JSP Generated Data");

    if(!skip){

        // IE

        if(client.indexOf("MSIE") != -1){

            res.setHeader ("Content-Disposition", "attachment; filename="+new String(orgfilename.getBytes("KSC5601"),"ISO8859_1"));

        }else{

            // 한글 파일명 처리

            orgfilename = new String(orgfilename.getBytes("KSC5601"),"iso-8859-1");

            res.setHeader("Content-Disposition", "attachment; filename=\"" + orgfilename + "\"");

            res.setHeader("Content-Type", "application/octet-stream; charset=utf-8");

        }  

        res.setHeader ("Content-Length", ""+file.length() );

        os = res.getOutputStream();

        

        byte b[] = new byte[(int)file.length()];

        int leng = 0;

        while( (leng = in.read(b)) > 0 ){

            os.write(b,0,leng);

        }

    }else{

    // 한글 깨짐 - 해결

    res.setContentType("text/html;charset=" + charset);

        out.println("<html><head>");

        out.println("<script language='javascript'>alert('파일을 찾을 수 없습니다.');history.back();</script>");

        out.println("</head><body></body></html>");

    }

    in.close();

    os.close();

}catch(Exception e){

e.printStackTrace();

}

    

}


/*

*  파일 삭제, 폴더 삭제

*/

public static void removeFile(HttpServletRequest req, HttpServletResponse res) 

throws ServletException, IOException{


// 파일 업로드된 경로

String root = req.getSession().getServletContext().getRealPath("/");

String savePath = root + UPLOAD_FOLDER + File.separator + UPLOAD_FOLDER ;

String filename = "1601561525229" ;


        File file = new File(savePath, filename);

        // 파일이 존재할 떄

        if (file.exists()) {

        file.delete();

        }

        

        removeDirectory(req, res, savePath);

}

/*

*/

private static boolean removeDirectory(HttpServletRequest req, HttpServletResponse res,

String path) throws ServletException, IOException {

boolean result = false;

File usrDir = new File(path);

if(!usrDir.exists()) {                  // 경로 존재 여부

            result = false;     

        }

else {

        File[] lowFiles = usrDir.listFiles();      // 경로 내의 파일 리스트

        

        // 폴더 삭제

        if ( usrDir.isDirectory() 

        && lowFiles.length == 0 ) {

   

        System.out.println("폴더 삭제처리 완료");

        usrDir.delete();

       

        return true;

       

    }else{

    result = false;

    }

        

        }

        

        return result;

}


}



파일명: HttpUtil.java


[첨부(Attachments)]

HttpUtil-updated.zip




* 맺음글(Conclusion)


파일 업로드, 다운로드, 삭제에 대해서 살펴보았다.

반응형
728x90
300x250

[JSP] 13. Jsp/Servlet(MVC) Maven 기반의 다중 파일 업로드, 다운로드 구현(2)


이전의 글에 이어서 작성하도록 하겠다.


이전의 글

- [JSP] 12. Jsp/Servlet(MVC) Maven 기반의 다중 파일 업로드, 다운로드 구현(1), 2020-09-24

   https://yyman.tistory.com/1414



14. Controller - BoardInsertMultiResultController 


고민을 나름대로 하여 만든 것이다. 인터넷 자료 등도 열심히 참고하고, 삽질을 많이 하였다.


package com.fileWeb.controller;


import java.io.File;

import java.io.IOException;

import java.io.PrintWriter;

import java.util.HashMap;

import java.util.Iterator;

import java.util.List;

import java.util.Map;


import javax.servlet.ServletException;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;


import org.apache.commons.fileupload.FileItem;

import org.apache.commons.fileupload.disk.DiskFileItemFactory;

import org.apache.commons.fileupload.servlet.ServletFileUpload;


public class BoardInsertMultiResultController implements Controller {


private Map<String, Object> reqMap = null; // req 정보(MultiRequest)

private Map<Integer, Map<String, Object>> fileMap = null; // 다중 파일 지원

private static int num = 0; 

@Override

public void execute(HttpServletRequest req, HttpServletResponse res) throws

ServletException, IOException {

fileMap = new HashMap<Integer, Map<String, Object>>(); 

reqMap = new HashMap<>();

num = 1;

        

        PrintWriter out = res.getWriter();

        out.println("<HTML><HEAD><TITLE>Multipart Test</TITLE></HEAD><BODY>");

try {

            

            //디스크상의 프로젝트 실제 경로얻기

            //String contextRootPath = "c:" + File.separator + "upload";

String dirName = "upload" ; 

String contextRootPath = req.getSession().getServletContext().getRealPath("/") + dirName;

            System.out.println("실제경로:" + contextRootPath);

            

            //1. 메모리나 파일로 업로드 파일 보관하는 FileItem의 Factory 설정

            DiskFileItemFactory diskFactory = new DiskFileItemFactory(); //디스크 파일 아이템 공장

            diskFactory.setSizeThreshold(4096); //업로드시 사용할 임시 메모리

            diskFactory.setRepository(new File(contextRootPath + "/WEB-INF/temp")); //임시저장폴더

            

            //2. 업로드 요청을 처리하는 ServletFileUpload생성

            ServletFileUpload upload = new ServletFileUpload(diskFactory);

            upload.setSizeMax(3 * 1024 * 1024); //3MB : 전체 최대 업로드 파일 크기

            

            //3. 업로드 요청파싱해서 FileItem 목록구함​​

            List<FileItem> items = upload.parseRequest(req); 


            Iterator<FileItem> iter = items.iterator(); //반복자(Iterator)로 받기​            

            while(iter.hasNext()) { //반목문으로 처리​    

                FileItem item = (FileItem) iter.next(); //아이템 얻기

                 //4. FileItem이 폼 입력 항목인지 여부에 따라 알맞은 처리

                

                if(item.isFormField()){ 

                //파일이 아닌경우

                    processFormField(out, item);

                    

                } else {

                //파일인 경우

                // System.out.println("오류:");

                    processUploadFile(out, item, contextRootPath);

                // System.out.println("오류2:");

                }

            }

            

        } catch(Exception e) {

            out.println("<PRE>");

            e.printStackTrace(out);

            out.println("</PRE>");

        }

out.println( "usrID(Map): " + reqMap.get("usrID") );

out.println( "usrPasswd(Map):" + reqMap.get("usrPasswd") );

        

        out.println("</BODY></HTML>");

// req.setAttribute("usrID", reqMap.get("usrID"));

// req.setAttribute("login", 1);//Object Type으로 넘어감

        req.setAttribute("reqMap", reqMap);

        req.setAttribute("fileMap", fileMap);

        

        // 방법3

        for( Integer key : fileMap.keySet() ){


            Map<String, Object> fileMapNode = fileMap.get(key);

            System.out.println( String.format("키 : %s, 값: %s", key, fileMapNode.get("fileName") ));

            

        }

        

// System.out.println("오류3:" + reqMap.get("usrID"));

HttpUtil.forward(req, res, "/WEB-INF/view/board/insertResult.jsp");

}


//업로드한 정보가 파일인경우 처리

private void processUploadFile( PrintWriter out, FileItem item, String contextRootPath)

throws Exception {

Map<String, Object> fileNode = new HashMap<String, Object>();

String name = item.getFieldName(); // 파일의 필드 이름 얻기

String fileName = item.getName(); // 파일명 얻기

// 임시 - 실제 원본 이름 추출

File originalFile = new File(fileName);

String originalFileName = originalFile.getName();

// System.out.println("임시:" + originalFileName );

String contentType = item.getContentType(); // 컨텐츠 타입 얻기

long fileSize = item.getSize(); // 파일의 크기 얻기

// 업로드 파일명을 현재시간으로 변경후 저장

String fileExt = fileName.substring(fileName.lastIndexOf("."));

String uploadedFileName = System.currentTimeMillis() + ""; 

System.out.println(fileExt);

System.out.println(uploadedFileName);

// 저장할 절대 경로로 파일 객체 생성

File uploadedFile = new File(contextRootPath + "/upload/" + uploadedFileName);

item.write(uploadedFile); //파일 저장

//========== 뷰단에 출력 =========//

out.println("<P>");

out.println("파라미터 이름:" + name + "<BR>");

out.println("파일 이름:" + fileName + "<BR>");

out.println("콘텐츠 타입:" + contentType + "<BR>");

out.println("파일 사이즈:" + fileSize + "<BR>");

//확장자가 이미지인겨우 이미지 출력

if(".jpg.jpeg.bmp.png.gif".contains(fileExt.toLowerCase())) {

out.println("<IMG SRC='upload/" 

+ uploadedFileName 

+ "' width='300'><BR>");

}

out.println("</P>");

out.println("<HR>");

out.println("실제저장경로 : "+uploadedFile.getPath()+"<BR>");

out.println("<HR>");

// 파일 정보

fileNode.put("name", name);

fileNode.put("fileName", originalFileName);

fileNode.put("contentType", contentType);

fileNode.put("fileSize", fileSize);

fileNode.put("fileExt", fileExt);

fileNode.put("uploadedFileName", uploadedFileName);

fileNode.put("realName", uploadedFile.getName());

fileNode.put("realPath", uploadedFile.getPath());

fileMap.put(num, fileNode);

num++;

}

private void processFormField(PrintWriter out, FileItem item) 

throws Exception{

String name = item.getFieldName(); //필드명 얻기

Object value = item.getString("UTF-8"); //UTF-8형식으로 필드에 대한 값읽기

// out.println(name + ":" + value + "<BR>"); //출력

reqMap.put(name, value);

}


}



파일명: BoardInsertMultiResultController.java


[첨부(Attachments)]

BoardInsertMultiResultController.zip



15. View - insertResult.jsp


사소해보이지만, jsp파일 내에서도 자료구조를 사용할 수 있다.


JSP: <%@ page import="java.util.*" %>

Java: import java.util.*;


<%@ page language="java" contentType="text/html; charset=UTF-8"

    pageEncoding="UTF-8"%>

<%@ page import="java.util.*" %>

<!DOCTYPE html>

<html>

<head>

<meta charset="UTF-8">

<title>업로드 - 결과</title>

</head>

<body>

<h3>업로드 결과</h3>

<%


//Object name = request.getAttribute("usrID");

//Object login = request.getAttribute("login");

Object obj = request.getAttribute("reqMap");

Map<String, Object> map = null;

if(obj != null){

map = (HashMap<String, Object>)obj;

}


%>


<table style="width:700px;border:1px solid #e2e2e2;">

<tr>

<td style="width:20%;">

<%

        out.println("name : " + map.get("usrID") + "<br />");

%>

</td>

<td style="width:20%;border-left:1px solid #e2e2e2;">

<%

out.println("login : " + map.get("usrPasswd") + "<br />");

%>

</td>

</tr>

<tr>

<td>

</td>

<td>

</td>

</tr>

</table>


</body>

</html>


파일명: insertResult.jsp


[첨부(Attachments)]

insertResult.zip



16. 라이브러리(Libraries)


pom.xml으로 자동 생성된 라이브러리이긴 한데 수동으로 추출해보았다.





17. 맺음글(Conclusion)


실질적으로 다중 파일 업로드와 다운로드 구현에 대해서 자세히 살펴보았다.



* 참고자료(References)


1. [서블릿/JSP] Apache Commons FileUpload를 이용한 파일업로드 구현하기, https://dololak.tistory.com/720?category=636501, Accessed by 2020-09-24, Last Modified 2019-07-24.


2. FrontController & Command Pattern - 프론트 컨트롤러와 커맨드 패턴, https://dailyworker.github.io/servlet-advened/, Accessed by 2020-09-24, Last Modified 2019-07-09.


3. 파일전송/업로드(Multipart) , https://gunbin91.github.io/jsp/2019/05/28/jsp_11_file.html, Accessed by 2020-09-24, Last Modified 2019-05-28.

-> Multipart 방식으로 소개되어 있는데, 현재 서블릿 버전에서는 충돌 발생함. (www.servlets.com)


4. Servlets.com | com.oreilly.servlet, http://www.servlets.com/cos/, Accessed by 2020-09-24, Last Modified 2002-03-01.

-> 비추천: 2002년에서 멈춰버린 프로젝트


5. .[jsp] form 타입이 enctype="multipart/form-data" 일때 request.getParameter() 불가 값이 null로 오는 문제, http://blog.naver.com/PostView.nhn?blogId=software705&logNo=220551397421&parentCategoryNo=&categoryNo=&viewDate=&isShowPopularPosts=false&from=postView, Accessed by 2020-09-24, Last Modified 2015-11-27.

-> 비추천: oreilly.servlet jar 파일로 구현한 MultipartRequest인데, 서블릿 충돌을 경험하게 된다.


6. [jsp/servlet] commons-fileupload 를 이용한 파일업로드 (서블릿), https://m.blog.naver.com/javaking75/220056175936, Accessed by 2020-09-24, Last Modified 2014-07-10.

-> 추천(70점): 오래된 것처럼 보이지만, 현실적으로 돌아가고 commons-io, commons-fileupload에 대해서 자세히 잘 소개하고 있음.


7. [Day49][JSP] HashMap을 사용하여 전체 회원 목록 조회 / 상세 회원 목록 조회 / JSTL, https://clapdev.tistory.com/49, Accessed by 2020-09-24, Last Modified 2019-11-05.

-> 추천(30점): c태그(JSTL)에 대해서 잘 소개하고 있음.


8. JSP 기본 실습 : 데이터 전송 받기2 [개인정보입력폼] -request-, https://whdvy777.tistory.com/entry/JSP-%EA%B8%B0%EB%B3%B8-%EC%8B%A4%EC%8A%B5-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EC%A0%84%EC%86%A1-%EB%B0%9B%EA%B8%B02-%EA%B0%9C%EC%9D%B8%EC%A0%95%EB%B3%B4%EC%9E%85%EB%A0%A5%ED%8F%BC-request, Accessed by 2020-09-24, Last Modified 2013-03-08.

-> 추천(35점): 오래되어 보이지만, 기본에 충실하고 있음.


9. Java Map 반복(Iteration)시키는 3가지 방법, https://stove99.tistory.com/96, Accessed by 2020-09-24, Last Modified 2011-11-13.


-> 자주 까먹을 수 있는 방법이라고 본다. 다중 프로그래밍을 다루고 있어서 까먹는 일이 많아서 적어본다.


Map<String, String> map = new HashMap<String, String>();

         

map.put("키1", "값1");

map.put("키2", "값2");

map.put("키3", "값3");

map.put("키4", "값4");

map.put("키5", "값5");

map.put("키6", "값6");

         

         

        // 방법1

        Iterator<String> keys = map.keySet().iterator();

        while( keys.hasNext() ){

            String key = keys.next();

            System.out.println( String.format("키 : %s, 값 : %s", key, map.get(key)) );

        }

         

        // 방법2

        for( Map.Entry<String, String> elem : map.entrySet() ){

            System.out.println( String.format("키 : %s, 값 : %s", elem.getKey(), elem.getValue()) );

        }

         

        // 방법3

        for( String key : map.keySet() ){

            System.out.println( String.format("키 : %s, 값 : %s", key, map.get(key)) );

        }


- https://stove99.tistory.com/96 [스토브 훌로구]


반응형
728x90
300x250

[JSP] 12. Jsp/Servlet(MVC) Maven 기반의 다중 파일 업로드, 다운로드 구현(1)


조금 실질적으로 도움이 되는 프로젝트라고 주장한다.

다중 파일 업로드, 다운로드 프로젝트를 조금 실용적으로 유용하게 사용할 수 있도록 특화하여 따로 만들어보게 되었다.


사용하는 것은 쉽지만, 구현하는 것은 다소 많은 시간이 소요된다고 본다.

현재 시중에 판매되는 교제 등의 내용이 공개 비판하면 그렇지만, 품질이 매우 낮다는 점이다.


최신 버전에 가까운 프로젝트를 위주로 직접 태스트를 엄선해서 수차례 검증하고, 작성하였다.


* apache-tomcat-9.0.37-windows-x64

* Maven - 3.6.3/1.16.0.20200610-1735   - http://maven.apache.org/download.cgi

  (pom.xml)

   - javax.servlet-api 4.0.1

   - commons-io 2.8.0 (apache project)   -  http://commons.apache.org/

   - commons-fileupload 1.4 (apache project)   - http://commons.apache.org/


적용한 모델은 MVC2 모델을 적용하였으니 참고하면 도움이 되겠다.



1. 결과


다운로드, 업로드 기능에 많은 시간이 소요된 프로젝트였다.



그림 1. 결과 



그림 2. 결과 




그림 3. 결과


그림 4. 결과




그림 5. 결과




2. 프로젝트 구성


작업을 해야하는 양이 조금 된다. 하나 잘 만들어놓으면 활용하거나 개선을 해서 사용해보는 것도 좋은 방법인 거 같다.



그림 6. 프로젝트 구성도



3. 프로젝트 초기 설정


새 프로젝트 만들기를 누른다.



그림 7. 새 프로젝트 만들기


Maven의 Maven Project를 클릭한다.

Next를 누른다.



그림 8. New Maven Project


"org.apache.maven.archetypes   | maven-archetype-webapp"를 선택하고 Next를 누른다.




그림 9. New Maven Project(2)


패키지명과 Artifact Id를 입력하고, Finish를 누른다.



그림 10. New Maven Project(3)


톰캣 서버 설정 등을 완료한다. 그리고 빌드를 시도하면, 이런 화면을 볼 수 있다.



4. 프로젝트 속성 - Java Build Path, Project Facets


14버전으로 변경해준다.



그림 11. Java Build Path -> Libraries 탭의 JRE System Library 버전


버전을 깔맞춤해줘야 한다.

14버전이면, 14로 일관성있게 설정 해준다.



그림 12. Project Facets -> Java 버전확인


마찬가지로 버전을 14로 맞춰준다.


비고: 대략 초기 Maven Project를 생성하면, 1.6? 1.7 버전으로 셋팅되어 있음.



5. Controller 생성하기(Servlet 만들기)


프로젝트에서 오른쪽 버튼을 클릭해서 New->Servlet을 클릭해서 하나 만들어준다.



그림 13. Servlet 만들기


패키지: com.fileWeb.controller

클래스명: FrontController(java)



6. pom.xml 설정하기(MvnRepository.com)


pom.xml 설정으로 필요한 라이브러리를 구비해준다.



<?xml version="1.0" encoding="UTF-8"?>


<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

  <modelVersion>4.0.0</modelVersion>


  <groupId>com.mavenUpload</groupId>

  <artifactId>web</artifactId>

  <version>0.0.1-SNAPSHOT</version>

  <packaging>war</packaging>


  <name>File - Multi Web Upload Project</name>

  <!-- FIXME change it to the project's website -->

  <url>http://www.example.com</url>


  <properties>

    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>

    <maven.compiler.source>1.7</maven.compiler.source>

    <maven.compiler.target>1.7</maven.compiler.target>

  </properties>


  <dependencies>

    <dependency>

      <groupId>junit</groupId>

      <artifactId>junit</artifactId>

      <version>4.11</version>

      <scope>test</scope>

    </dependency>

    <!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api -->

<dependency>

    <groupId>javax.servlet</groupId>

    <artifactId>javax.servlet-api</artifactId>

    <version>4.0.1</version>

    <scope>provided</scope>

</dependency>

<!-- https://mvnrepository.com/artifact/commons-io/commons-io -->

<dependency>

    <groupId>commons-io</groupId>

    <artifactId>commons-io</artifactId>

    <version>2.8.0</version>

</dependency>

<!-- https://mvnrepository.com/artifact/commons-fileupload/commons-fileupload -->

<dependency>

    <groupId>commons-fileupload</groupId>

    <artifactId>commons-fileupload</artifactId>

    <version>1.4</version>

</dependency>

  </dependencies>


  <build>

    <finalName>web</finalName>

    <pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->

      <plugins>

        <plugin>

          <artifactId>maven-clean-plugin</artifactId>

          <version>3.1.0</version>

        </plugin>

        <!-- see http://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_war_packaging -->

        <plugin>

          <artifactId>maven-resources-plugin</artifactId>

          <version>3.0.2</version>

        </plugin>

        <plugin>

          <artifactId>maven-compiler-plugin</artifactId>

          <version>3.8.0</version>

        </plugin>

        <plugin>

          <artifactId>maven-surefire-plugin</artifactId>

          <version>2.22.1</version>

        </plugin>

        <plugin>

          <artifactId>maven-war-plugin</artifactId>

          <version>3.2.2</version>

        </plugin>

        <plugin>

          <artifactId>maven-install-plugin</artifactId>

          <version>2.5.2</version>

        </plugin>

        <plugin>

          <artifactId>maven-deploy-plugin</artifactId>

          <version>2.8.2</version>

        </plugin>

      </plugins>

    </pluginManagement>

  </build>

</project>



파일명: pom.xml


[첨부(Attachments)]

pom.zip



7. web.xml 설정하기


web.xml 설정에 관한 것이다.

jsp가 아닌 *.do로 처리하는 프로젝트에 대한 내용으로 작성되었다.

UTF-8 한글 언어셋도 정의하였다.


<?xml version="1.0" encoding="UTF-8"?>


<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 

xmlns="http://java.sun.com/xml/ns/javaee" 

xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" 

id="WebApp_ID" version="3.0">


  <display-name>Archetype Created Web Application</display-name>

  

  <welcome-file-list>


    <welcome-file>index.html</welcome-file>

    <welcome-file>index.htm</welcome-file>

    <welcome-file>index.do</welcome-file>

    <welcome-file>default.html</welcome-file>

    <welcome-file>default.htm</welcome-file>

    <welcome-file>default.do</welcome-file>

  </welcome-file-list>

  

  <servlet>

  <servlet-name>FrontController</servlet-name>

  <servlet-class>com.fileWeb.controller.FrontController</servlet-class>

  <init-param>

<param-name>charset</param-name>

<param-value>UTF-8</param-value>

</init-param>

  </servlet>

  <servlet-mapping>

  <servlet-name>FrontController</servlet-name>

  <url-pattern>*.do</url-pattern>

  </servlet-mapping>

</web-app>



파일명: web.xml


[첨부(Attachments)]

web.zip



8. Controller - HttpUtil.java


패키지명: com.fileWeb.controller

클래스명: HttpUtil (java)



package com.fileWeb.controller;


import java.io.File;

import java.io.FileInputStream;

import java.io.FileNotFoundException;

import java.io.IOException;

import java.io.InputStream;

import java.io.OutputStream;

import java.io.PrintWriter;


import javax.servlet.RequestDispatcher;

import javax.servlet.ServletConfig;

import javax.servlet.ServletException;

import javax.servlet.http.HttpServlet;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;



public class HttpUtil extends HttpServlet {

private static final long serialVersionUID = 1L;

private static String charset = null;


public static void forward(HttpServletRequest req, HttpServletResponse res,

String path) throws ServletException, IOException {

try {

RequestDispatcher dispatcher = req.getRequestDispatcher(path);

dispatcher.forward(req, res);

}catch(Exception e) {

e.printStackTrace();

}

}

public static void fileUpload(HttpServletRequest req, HttpServletResponse res,

String path) throws ServletException, IOException {


charset = (String) req.getAttribute("charset");

System.out.println(charset);

PrintWriter out = res.getWriter();

// 파일 업로드된 경로

String root = req.getSession().getServletContext().getRealPath("/");

String savePath = root + "upload" + File.separator + "upload";

// 서버에 실제 저장된 파일명

String filename = "1600955663095" ;

System.out.println("파일 실제 폴더경로:" + savePath);

// 실제 내보낼 파일명

String orgfilename = "license한글.txt" ;

req.setCharacterEncoding(charset);

res.setCharacterEncoding(charset);

InputStream in = null;

OutputStream os = null;

File file = null;

boolean skip = false;

String client = "";

try{

    // 파일을 읽어 스트림에 담기

    try{

        file = new File(savePath, filename);

        in = new FileInputStream(file);

    }catch(FileNotFoundException fe){

        skip = true;

    }

    client = req.getHeader("User-Agent");

    // 파일 다운로드 헤더 지정

    res.reset() ;

    res.setContentType("application/octet-stream");

    res.setHeader("Content-Description", "JSP Generated Data");

    if(!skip){

        // IE

        if(client.indexOf("MSIE") != -1){

            res.setHeader ("Content-Disposition", "attachment; filename="+new String(orgfilename.getBytes("KSC5601"),"ISO8859_1"));

        }else{

            // 한글 파일명 처리

            orgfilename = new String(orgfilename.getBytes("KSC5601"),"iso-8859-1");

            res.setHeader("Content-Disposition", "attachment; filename=\"" + orgfilename + "\"");

            res.setHeader("Content-Type", "application/octet-stream; charset=utf-8");

        }  

        res.setHeader ("Content-Length", ""+file.length() );

        os = res.getOutputStream();

        

        byte b[] = new byte[(int)file.length()];

        int leng = 0;

        while( (leng = in.read(b)) > 0 ){

            os.write(b,0,leng);

        }

    }else{

    // 한글 깨짐 - 해결

    res.setContentType("text/html;charset=" + charset);

        out.println("<html><head>");

        out.println("<script language='javascript'>alert('파일을 찾을 수 없습니다.');history.back();</script>");

        out.println("</head><body></body></html>");

    }

    in.close();

    os.close();

}catch(Exception e){

e.printStackTrace();

}

    

}


}



파일명: HttpUtil.java


[첨부(Attachments)]

HttpUtil.zip



9. Controller의 인터페이스 (Controller.java)


Controller.java 파일이다.

인터페이스로 설계되었다.


package com.fileWeb.controller;


import java.io.IOException;


import javax.servlet.ServletException;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;


public interface Controller {

public void execute(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException;

}



파일명: Controller.java


[첨부(Attachments)]

Controller.zip




10. Controller - FrontController와 Command 패턴 적용으로 구성함. 


설명도 중요하지만, 코드로 살펴보도록 하겠다.


package com.fileWeb.controller;


import java.io.IOException;

import java.util.HashMap;

import java.util.Map;


import javax.servlet.ServletConfig;

import javax.servlet.ServletException;

import javax.servlet.http.HttpServlet;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;

import javax.servlet.http.HttpSession;


/**

 * Servlet implementation class HomeController

 */

public class FrontController extends HttpServlet {

private static final long serialVersionUID = 1L;

       

private String charset = null;

    public FrontController() {

        super();

    }


protected void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {

doAction(req, res);

}

protected void doPost(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {

doAction(req, res);

}

// FrontController 패턴 & Command 패턴

protected void doAction(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {


ServletConfig sc = this.getServletConfig();

charset = sc.getInitParameter("charset");

req.setAttribute("charset", charset);

req.setCharacterEncoding(charset);

res.setContentType("text/html; charset=" + charset);

System.out.println(charset);

String uri = req.getRequestURI();

System.out.println("uri : " + uri);

String conPath = req.getContextPath();

System.out.println("conPath : " + conPath);

String command = uri.substring(conPath.length());

System.out.println("command : " + command);

Controller subController = null;

System.out.println("reqMapSize : " + req.getParameterMap().size());


if(command.equals("/board/insert.do")){

System.out.println("insert");

System.out.println("----------------");


    subController = new BoardInsertController();

    subController.execute(req, res);

   

}else if (command.equals("/board/insertResult.do")) {

System.out.println("insertResult");

System.out.println("----------------");


    subController = new BoardInsertResultController();

subController.execute(req, res);

}

else if(command.equals("/board/insertMultiResult.do")){

System.out.println("insertResult");

System.out.println("----------------");


    subController = new BoardInsertMultiResultController();

subController.execute(req, res);

}else if(command.equals("/board/download.do")) {

System.out.println("download");

System.out.println("----------------");

    HttpUtil.fileUpload(req, res, null);

}else if(command.equals("/board/update.do")){

System.out.println("update");

System.out.println("----------------");

}else if(command.equals("/board/select.do")){

System.out.println("select");

System.out.println("----------------");

}else if(command.equals("/board/delete.do")){

System.out.println("delete");

System.out.println("----------------");

}

}

}



파일명: FrontController.java


[첨부(Attachments)]

FrontController.zip



11. Controller - BoardInsertController.java


BoardInsertController의 핵심 내용은 "insert.jsp" 파일을 불러오는 데 있다.

없어도 된다고 생각했는데, 기능이 많아질 경우를 대비한다면, 있는 것이 낫다고 생각했다.


package com.fileWeb.controller;


import java.io.IOException;

import java.util.HashMap;

import java.util.Map;


import javax.servlet.ServletException;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;


public class BoardInsertController implements Controller {


@Override

public void execute(HttpServletRequest req, HttpServletResponse res) throws

ServletException, IOException {

HttpUtil.forward(req, res, "/WEB-INF/view/board/insert.jsp");

}


}



파일명: BoardInsertController.java


[첨부(Attachments)]

BoardInsertController.zip



12. View - board/insert.jsp


삽입 페이지에 대한 것이다.

구현 목표는 두 가지에 대한 실험이다.


하나는 순수한 POST 처리 방식이고, 하나는 POST기반의 multipart/form-data 방식에 관한 것이다.


실험 결과를 먼저 소개하면, 

1. multipart/form-data를 정의해버리면, 기본 정의된 request로는 parameter를 전송받을 수가 없다.


* com.oreilly.servlet   (결과: 오래된 프로젝트 / 서블릿 버전 충돌 발생)

  - http://www.servlets.com/cos/


다양한 검색을 통해서 MultipartRequest 정의를 활용해서 이 문제를 oreilly.Servlet으로 해결할 수 있다는 글을 봐서 적용하였으나 
서블릿 충돌 문제가 발생하였다. (사용할 수 없는 오래된 라이브러리)

2. Apache Common-io, Apache Common-FileUpload를 적용하면, 해결할 수 있다. (2020-09-24일 기준)



<%@ page language="java" contentType="text/html; charset=UTF-8"

    pageEncoding="UTF-8"%>

<!DOCTYPE html>

<html>

<head>

<meta charset="UTF-8">

<title>삽입</title>

</head>

<body>

<h3>삽입</h3>


<!-- 파일 업로드 화면 -->

<!-- 일반적으로 Multipart form으로 전송된 데이터는

      일반 request 메서드로 받아올 수 없습니다.  enctype="multipart/form-data" -->

<form method="post" action="insertResult.do" >

<table style="width:700px;border:1px solid #e2e2e2;">

<tr>

<td style="width:20%">

파일명

</td>

<td>

<input type="text" name="usrID" size="10">

<input type="file" name="uploadFile" multiple>

</td>

</tr>

<tr>

<td colspan="2">

<input type="submit" value="전송">

</td>

</tr>

</table>

</form>



<!-- 멀티파트/데이터 전송 -->

<form method="post" action="insertMultiResult.do" enctype="multipart/form-data" >

<table style="width:700px;border:1px solid #e2e2e2;">

<tr>

<td style="width:20%">

파일명

</td>

<td>

<input type="text" name="usrID" size="10">

<input type="password" name="usrPasswd" size="10">

<input type="file" name="uploadFile" multiple>

<input type="file" name="uploadFile" multiple>

<input type="file" name="uploadFile" multiple>

</td>

</tr>

<tr>

<td colspan="2">

<input type="submit" value="전송">

</td>

</tr>

</table>

</form>


</body>

</html>


파일명: insert.jsp


[첨부(Attachments)]

insert.zip




13. Controller - BoardInsertResultController


multipart/form-data를 미지원하는 타입으로 결과를 출력하는 방법에 대하여 작성하였다.


package com.fileWeb.controller;


import java.io.IOException;

import java.util.HashMap;

import java.util.Map;


import javax.servlet.ServletException;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;


public class BoardInsertResultController implements Controller {


@Override

public void execute(HttpServletRequest req, HttpServletResponse res) throws

ServletException, IOException {


Map<String, Object> reqMap = new HashMap<String, Object>();

reqMap.put("usrID", req.getParameter("usrID"));

reqMap.put("usrPasswd", req.getParameter("filename"));


            req.setAttribute("reqMap", reqMap);

        

HttpUtil.forward(req, res, "/WEB-INF/view/board/insertResult.jsp");

}


}


파일명: BoardInsertResultController.jsp


[첨부(Attachments)]

BoardInsertResultController.zip



2부에서 만나요.


양이 조금 많아서 2부에서 글을 이어서 소개하려고 한다.


- [JSP] 12. Jsp/Servlet(MVC) Maven 기반의 다중 파일 업로드, 다운로드 구현(2), 2020-09-24

   https://yyman.tistory.com/1415


반응형

+ Recent posts