본문 바로가기
자바

[java] multipart/form-data를 이용한 파일업로드/다운로드

by DarrenH 2022. 8. 19.
반응형

전체 소스

https://github.com/Darren4641/Semtle_web

 

GitHub - Darren4641/Semtle_web: www.semtle-cuk.site [컴퓨터정보공학부 학회 셈틀쟁이 공식 홈페이지]

www.semtle-cuk.site [컴퓨터정보공학부 학회 셈틀쟁이 공식 홈페이지]. Contribute to Darren4641/Semtle_web development by creating an account on GitHub.

github.com

 

안녕하세요 저번 포스팅에이해 이번에는 파일업로드/다운로드를 알아보려고하는데요.

 

저희 학회는 이제 스터디모임이있는데 스터디장이 스터디관련 파일을 올려주면, 학회원들은 해당 파일을 이용하여 과제 및 참고 자료로 이용할 수 있도록 제작하였습니다. 

 

저희 학회 사이트의 스터디룸 페이지인데요 저는 JAVA 스터디장을 맡았으므로 JAVA스터디장으로 가보겠습니다. 

 

 

(저기 위에 "꾸르꾸르꿀잼 C언어 스터디룸입니당" 저걸 수정안했네요,,ㅎㅎ)

파일이 있는경우 클립모양으로 지정해주었습니다. 

 

글을 작성하실때 View를 보여드리겠습니다.

 

이런식으로 되어있는데요. 코드를 살펴보겠습니다. 

	<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <link rel="shortcut icon" href="${pageContext.request.contextPath}/semtle/images/favicon.ico" type="image/x-icon" sizes="16X16">
	<link rel="icon" href="${pageContext.request.contextPath}/semtle/images/favicon.ico" type="image/x-icon" sizes="16X16">
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>SemtleWeb</title>

  </head>

  <body>
    <jsp:include page="${pageContext.request.contextPath}/semtle/header.jsp" flush="true" />

     <section class="board_text">
      <div class="board_textbox">
        <div class="board_text-input">
          <form action="study_text.doStudy" name="freeboard_text" accept-charset="utf-8" method="post"  enctype="multipart/form-data">
          <input type="hidden" name="userId" value="${ userInfo.userId }">
          <input type="hidden" name="userRole" value="${ userInfo.userRole }">
          <input type="hidden" name="nickName" value="${ userInfo.nickName }">
          <input type="hidden" name="userRole" value="${ userInfo.userRole }">
          <input style="margin-top:30px;" type="text" name="p_title" placeholder="제목을 입력해주세요">
            <textarea id="content" name="content" placeholder="내용을 입력해주세요" ></textarea>
            <input type="file" name="fileUrl">
            <div class="board_append">
              <label></label>
              <input type="submit" id="write_submit" value="완료" />
            </div>
          </form>
        </div>
      </div>
    </section>
    <jsp:include page="${pageContext.request.contextPath}/semtle/footer.jsp" flush="true" />
  </body>
</html>

여기서 중요한 부분이 <form> 태그입니다. 이 form태그를 살펴보시면 이전에 사용했던 form태그와 다른 부분이있습니다. 

enctype="multipart/form-data"

이 부분인데요 해당 코드를 작성해주셔야 파일 업로드가 가능합니다. 

 

해당 프로젝트는 MVC 2 Pattern이므로 Controller에가서 확인해보겠습니다. 

package com.Study;

import java.io.IOException;

import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import com.Study.StudyCommand;
import com.Study.StudyDeleteCommand;
import com.Study.StudyDownloadCommand;
import com.Study.StudyListCommand;
import com.Study.StudyModifyCommand;
import com.Study.StudyViewCommand;
import com.Study.StudyWriteCommand;

/**
 * Servlet implementation class StudyController
 */
@WebServlet("*.doStudy")
public class StudyController extends HttpServlet {
	StudyCommand command = null;
	private static final long serialVersionUID = 1L;
       
    /**
     * @see HttpServlet#HttpServlet()
     */
    public StudyController() {
        super();
        // TODO Auto-generated constructor stub
    }

	/**
	 * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
	 */
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		// TODO Auto-generated method stub
		System.out.println("doGet");
		doAction(request, response);
	}

	/**
	 * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
	 */
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		// TODO Auto-generated method stub
		System.out.println("doPost");
		doAction(request, response);
	}
	
	private synchronized void setCommand(StudyCommand command) {
		this.command = command;
	}
	private synchronized void doAction(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		System.out.println("do PostController");
		HttpSession session = request.getSession();
		request.setCharacterEncoding("UTF-8");
		response.setCharacterEncoding("UTF-8");
		response.setContentType("text/html; charset=UTF-8");
		String title = null;
		String viewPage = null;
		String boardId = null;
		String requestUri = request.getRequestURI();
		String contextPath = request.getContextPath();
		String commandName =  null;
		
		viewPage = requestUri;
		commandName = viewPage.substring(contextPath.length());

		title = (String) session.getAttribute("title");
		
		try {
			if(commandName.equals("/study_write.doStudy")) {
				viewPage = "/semtle/Study/study_board_text.jsp";
				boardId = (String) request.getAttribute("boardId");
			}else if(commandName.equals("/study_text.doStudy")) {
				viewPage = "/semtle/Study/study.jsp";
				boardId = (String) request.getAttribute("boardId");
				setCommand(new StudyWriteCommand());
				command.execute(request, response);
			}else if(commandName.equals("/study_view.doStudy")) {
				viewPage = "/semtle/Study/study_board_room.jsp";
				setCommand(new StudyViewCommand());
				command.execute(request, response);
			}else if(commandName.equals("/study_download.doStudy")) {
				setCommand(new StudyDownloadCommand());
				int postId = command.execute(request, response);
				viewPage = "study_view.doStudy?postId="+postId;
			}else if(commandName.equals("/study_setting.doStudy")) {
				viewPage = "/semtle/Study/study_setting.jsp";
				setCommand(new StudyViewCommand());
				command.execute(request, response);
			}else if(commandName.equals("/study_update.doStudy")) {
				setCommand(new StudyModifyCommand());
				command.execute(request, response);
				viewPage = "/semtle/Study/study.jsp";
			}else if(commandName.equals("/study_delete.doStudy")) {
				viewPage = "/semtle/Study/study.jsp";
				setCommand(new StudyDeleteCommand());
				command.execute(request, response);
			}else if(commandName.equals("/board_list_Study.doStudy")) {
				viewPage = "/semtle/Study/study_board.jsp";
				title = request.getParameter("title");
				boardId = title;
				session.setAttribute("title", title);
				session.setAttribute("boardId", boardId);
				setCommand(new StudyListCommand());
				command.execute(request, response);
			} else if(commandName.equals("/Study.doStudy")) {
				viewPage = "/semtle/Study/study.jsp";
				title = "스터디룸";
				session.setAttribute("title", title);
			}else if(commandName.equals("/study_room.doStudy")) {
				viewPage = "/semtle/Study/study_board_room.jsp";
			}else {
				System.out.println("해당 Command가 없습니다.");
				viewPage = "notCommand.jsp";
			}
			
			System.out.println("commandName : " + commandName);
			System.out.println("boardId = " + boardId);
			RequestDispatcher dispatcher = request.getRequestDispatcher(viewPage);
			dispatcher.forward(request, response);
		}catch(IllegalStateException e) {
			System.out.println("Illegal");
		}
		
	}
}

보시면 사용자가 form 작성완료후 submit을 하게 되면 "study_textdoStudy"로 가게 되는데요 이를 StudyController에서 살펴보면

else if(commandName.equals("/study_text.doStudy")) {
				viewPage = "/semtle/Study/study.jsp";
				boardId = (String) request.getAttribute("boardId");
				setCommand(new StudyWriteCommand());
				command.execute(request, response);
			}

이 부분 입니다. 이때 StudyWriteCommand클래스의 execute메서드를 실행하네요.

그럼 이제 StudyWriteCommand 클래스를 살펴보겠습니다.

 

package com.Study;

import java.io.IOException;
import java.io.PrintWriter;
import java.sql.Timestamp;
import java.text.SimpleDateFormat;
import java.util.Enumeration;
import java.util.TimeZone;

import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import com.Post.postDAO;
import com.oreilly.servlet.MultipartRequest;
import com.oreilly.servlet.multipart.DefaultFileRenamePolicy;

public class StudyWriteCommand implements StudyCommand {

	@Override
	public int execute(HttpServletRequest request, HttpServletResponse response) throws IOException {
		// TODO Auto-generated method stub
		SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
		sdf.setTimeZone(TimeZone.getTimeZone("Asia/Seoul"));
		String realFolder = "";
		String filename = "null";
		String saveFolder = "/upload";
		String encType = "utf-8";
		int maxSize = 20*1024*1024;
		MultipartRequest multi = null;
		
		ServletContext context = request.getSession().getServletContext();
		realFolder = context.getRealPath(saveFolder);
		System.out.println("realFolder = " + realFolder + "\nsaveFolder = " + saveFolder);
		
		HttpSession session = request.getSession();
		String title = (String)session.getAttribute("title");
		
		String boardId = (String)session.getAttribute("boardId");
		//int sizeLimit = 20 * 1024 * 1024;
		//String savePath = request.getSession().getServletContext().getRealPath("/upload");
		//System.out.println("SavePath = " + savePath);

		try {
			multi = new MultipartRequest(request, realFolder, maxSize, encType, new DefaultFileRenamePolicy());
			
			Enumeration files = multi.getFileNames();
			
			while(files.hasMoreElements()) {
				String name = (String)files.nextElement();
				filename = multi.getFilesystemName(name);
			}
			
		}catch(Exception e) {
			e.printStackTrace();
		}
		
		String nickName = multi.getParameter("nickName");
		String userId = multi.getParameter("userId");
		String content = multi.getParameter("content");
		String createAt = sdf.format(new Timestamp(System.currentTimeMillis()));
		int lookUp = 0;
		String updateAt = sdf.format(new Timestamp(System.currentTimeMillis()));
		String status = "1";
		String p_title = multi.getParameter("p_title");
		String userRole = multi.getParameter("userRole");
		String fileUrl = filename;
		postDAO dao = postDAO.getInstance();
		dao.writePost(boardId, p_title, userRole, nickName, userId, content, fileUrl, lookUp, createAt, updateAt, status);
		PrintWriter writer = response.getWriter();
		
		writer.println("<script>alert('게시글이 작성되었습니다.'); location.href='board_list_Study.doStudy?title="+boardId+"';</script>");
		writer.close();
		return 1;
		
	}
}

 

먼저 파일이 저장되는 실제 경로를 지정해줍니다. 저희가 프로젝트를 디렉토리를 보는것과는 위치가 조금 다릅니다. 저희가 Tomcat을 이용해서 서버를 켜기때문에 Tomcat에 저장돼있는 경로에 폴더를 지정해주셔야합니다. 그 경로를 구해주는 코드는 다음과 같습니다. 

ServletContext context = request.getSession().getServletContext();

try구문의 new MultiPartRequest에 대해 설명 드리겠습니다.

MultiPartRequest를 객체를 생성하려면 총 5개의 파라미터가 필요합니다.

1. request

2. 업로드 될 경로

3. 파일의 최대 크기

4. 파일 encoding처리

5. 파일이름이 같을시 처리에 대한 객체

     DefaultFileRenamePolicy  - 중복된 파일이름일 경우 파일 이름 뒤에 숫자를 붙임

 

이제 파일을 업로드가 되었으며, 이제 파일 이름을 DB에 저장해야합니다. 

마찬가지로 postDAO를 통해 디비에 저장해주겠습니다. 파일을 찾을때 DB로부터 파일명을 가지고 온 뒤 경로/파일명 으로 찾게 됩니다. 

 

그럼 이제 파일업로드는 끝이 났습니다. 

 

 

그럼 이제 파일다운로드를 진행하도록 하겠습니다. 

<span id="studyboard-foot-attach"><a href="study_download.doStudy?postId=${ post.postId }">${ post.fileUrl }</a></span>

jsp파일에서 파일을 클릭하게되면 위와 같이 "study_download.doStudy?postId="로 요청합니다. 

그럼 StudyController에서 다음과 같이 처리합니다. 

 

else if(commandName.equals("/study_download.doStudy")) {
				setCommand(new StudyDownloadCommand());
				int postId = command.execute(request, response);
				viewPage = "study_view.doStudy?postId="+postId;
			}

이번에는 StudyDownloadCommand를 살펴보겠습니다. 

 

package com.Study;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;

import javax.servlet.ServletContext;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.Post.postDAO;
import com.Post.postDTO;

public class StudyDownloadCommand implements StudyCommand{
	
	@Override
	public int execute(HttpServletRequest request, HttpServletResponse response) throws IOException {
		// TODO Auto-generated method stub
		
		int postId = Integer.parseInt(request.getParameter("postId"));
		postDAO dao = postDAO.getInstance();
		postDTO dto = dao.viewPost(postId);
		
		String fileName = dto.getFileUrl();
		
		ServletContext context = request.getSession().getServletContext();
		String downaloadPath = context.getRealPath("upload");
		String filePath = downaloadPath + "/" + fileName;
		File file = new File(filePath);
		
		System.out.println("경로는  : " + filePath);
		String mimeType = context.getMimeType(filePath);
		if(mimeType == null)
			mimeType = "application/octet-stream";
		response.setContentType(mimeType);
		
		String encoding = new String(fileName.getBytes("utf-8"), "8859_1");
		response.setHeader("Content-Disposition", "attachment; filename=" + encoding);
		response.setContentType("text/html; charset=utf-8");
		response.setHeader("Chche-Control", "no-cache");
		
		FileInputStream in = new FileInputStream(file);
		ServletOutputStream outStream = response.getOutputStream();
		
		byte b[] = new byte[4096];
		int data = 0;
		while ((data = in.read(b, 0, b.length)) != -1) {
			outStream.write(b, 0, data);
		}

		outStream.flush();
		outStream.close();
		in.close();
		
		return postId;
	}
	
}

 

마찬가지로 서버에올라간 realPath 경로를 구해주고 해당 파일을 찾게 됩니다. 

일반적으로 application/octet-stream으로도 다운로드가 가능하지만 MS office같은 파일은 오류가 발생할 수 있기때문에 MimeType을 지정해줍니다. 

 

그리고나서 OutPutStream을 이용해서 파일을 가져옵니다.

이때, byte 단위로 전송이됩니다. 

 

이로써 파일이 다운로드 된것을 볼 수 있습니다. 

 

 

 

 

 

 

 

 

 

 

반응형