국비교육(22-23)

32일차(3)/jsp(15) : 자료실, 파일 업로드/다운로드 기능 구현

서리/Seori 2022. 11. 22. 22:24

32일차(3)/jsp(15) : 자료실, 파일 업로드/다운로드 기능 구현

 

 

**DB에 새 테이블 생성

- 게시판에 업로드된 파일의 정보를 저장하는 테이블

- orgFileName : 원본 파일명
- saveFileName : 서버에 저장된 파일명
 (같은 이름의 파일이 있는 경우, 업로드되면서 이름이 바뀐다.)
- fileSize : 해당 파일의 크기. 몇바이트인지
- regdate : 파일 업로드 일자

 

 

<index> 수정 : 자료실 링크 추가

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>    
<%
	//session scope에 id라는 키값으로 저장된 값이 있는지 읽어와 본다.(없으면 null)
	String id=(String)session.getAttribute("id");
	
%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>/index.jsp</title>
</head>
<body>
	<div class="container">
		<%if(id != null) {%>
			<p>
				<a href="${pageContext.request.contextPath }/users/private/info.jsp"><%=id %></a>님 로그인중....
				<a href="${pageContext.request.contextPath }/users/logout.jsp">로그아웃</a>
			</p>
		<%}else{ %>
			<a href="${pageContext.request.contextPath }/users/loginform.jsp">로그인</a>
		<%} %>
		<h1>인덱스 페이지입니다.</h1>
		<ul>
			<li><a href="${pageContext.request.contextPath }/users/signup_form.jsp">회원가입</a></li>		
			<li><a href="${pageContext.request.contextPath }/private/study.jsp">회원전용공간(공부)</a></li>
			<li><a href="${pageContext.request.contextPath }/private/game.jsp">회원전용공간(게임)</a></li>
			<li><a href="file/list.jsp">자료실</a></li>
		</ul>
	</div>
</body>
</html>

 

 

/file/ <list.jsp>

<%@page import="test.file.dao.FileDao"%>
<%@page import="java.util.List"%>
<%@page import="test.file.dto.FileDto"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%
	//파일 목록을 얻어와서
	List<FileDto> list=FileDao.getInstance().getList();
	//응답하기
%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>/file/private/list.jsp</title>
</head>
<body>
	<div class="container">
		<a href="${pageContext.request.contextPath }/file/private/upload_form.jsp">업로드 하기</a>
		<h3>자료실 목록 보기</h3>
		<table>
			<thead>
				<tr>
					<th>번호</th>
					<th>작성자</th>
					<th>제목</th>
					<th>파일명</th>
					<th>크기</th>
					<th>등록일</th>
				</tr>
			</thead>
			<tbody>
			<%for(FileDto tmp:list) {%>
				<tr>
					<td><%=tmp.getNum() %></td>
					<td><%=tmp.getWriter() %></td>
					<td><%=tmp.getTitle() %></td>
					<td>
						<a href="download.jsp?num=<%=tmp.getNum() %>"><%=tmp.getOrgFileName() %></a>
					</td>
					<td><%=tmp.getFileSize() %></td>
					<td><%=tmp.getRegdate() %></td>
				</tr>				
			<%} %>
			</tbody>
		</table>
	</div>
</body>
</html>

- 게시판을 테이블 형태로 작성하기!

- 파일에는 다운로드 링크를 함께 걸어준다.

 

 

- test.file.dto / test.file.dao 안에 DTO, DAO 생성

 

<FileDto>

package test.file.dto;

public class FileDto {
	private int num;
	private String writer;
	private String title;
	private String orgFileName;
	private String saveFileName;
	private long fileSize;
	private String regdate;
	
	public FileDto() {}

	public FileDto(int num, String writer, String title, String orgFileName, String saveFileName, long fileSize,
			String regdate) {
		super();
		this.num = num;
		this.writer = writer;
		this.title = title;
		this.orgFileName = orgFileName;
		this.saveFileName = saveFileName;
		this.fileSize = fileSize;
		this.regdate = regdate;
	}

	public int getNum() {
		return num;
	}

	public void setNum(int num) {
		this.num = num;
	}

	public String getWriter() {
		return writer;
	}

	public void setWriter(String writer) {
		this.writer = writer;
	}

	public String getTitle() {
		return title;
	}

	public void setTitle(String title) {
		this.title = title;
	}

	public String getOrgFileName() {
		return orgFileName;
	}

	public void setOrgFileName(String orgFileName) {
		this.orgFileName = orgFileName;
	}

	public String getSaveFileName() {
		return saveFileName;
	}

	public void setSaveFileName(String saveFileName) {
		this.saveFileName = saveFileName;
	}

	public long getFileSize() {
		return fileSize;
	}

	public void setFileSize(long fileSize) {
		this.fileSize = fileSize;
	}

	public String getRegdate() {
		return regdate;
	}

	public void setRegdate(String regdate) {
		this.regdate = regdate;
	}
	
}

- 필드명은 가급적 오라클의 테이블명과 동일하게

 

 

<FileDao>

package test.file.dao;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.List;

import test.file.dto.FileDto;
import test.util.DbcpBean;

public class FileDao {
	//static 필드
	private static FileDao dao;
	//외부에서 객체 생성하지 못하도록 생성자를 private로
	private FileDao() {}
	//자신의 참조값을 리턴해주는 메소드
	public static FileDao getInstance() {
		if(dao==null) {
			dao=new FileDao();
		}
		return dao;
	}
	
	//파일 하나의 정보를 리턴하는 메소드	
	public FileDto getData(int num) {		
		FileDto dto=null;
		//필요한 객체를 담을 지역변수를 미리 만들어둔다.
		Connection conn = null;
		PreparedStatement pstmt = null;
		ResultSet rs = null;
		try {
			//Connection Pool에서 Connection 객체를 하나 얻어온다.
			conn = new DbcpBean().getConn();
			//실행할 sql문의 뼈대 구성하기
			String sql = "SELECT writer, title, orgFileName, saveFileName, fileSize, regdate"
					+ " From board_file"
					+ " WHERE num=?";			
			//sql문의 ?에 바인딩할게 있으면 한다.			
			pstmt = conn.prepareStatement(sql);
			pstmt.setInt(1, num);
			//SELECT문을 수행하고 결과값을 받아온다.
			rs = pstmt.executeQuery();
			//반복문 돌면서 resultSet에서 필요한 값을 얻어낸다.
			if (rs.next()) {
				dto=new FileDto();				
				dto.setNum(num);
				dto.setWriter(rs.getString("writer"));
				dto.setTitle(rs.getString("title"));
				dto.setOrgFileName(rs.getString("orgFileName"));
				dto.setSaveFileName(rs.getString("saveFileName"));
				dto.setFileSize(rs.getLong("fileSize"));
				dto.setRegdate(rs.getString("regdate"));				
			}
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			try {
				if (rs != null)
					rs.close();
				if (pstmt != null)
					pstmt.close();
				if (conn != null)
					conn.close(); //Connection Pool에 Connection 반납하기

			} catch (Exception e2) {
			}
		}
		return dto;
	}

	//업로드된 파일 정보를 DB 에 저장하는 메소드	
	public boolean insert(FileDto dto) {
		Connection conn = null;
		PreparedStatement pstmt = null;
		int rowCount = 0;
		try {
			conn = new DbcpBean().getConn();
			String sql = "INSERT INTO board_file"
					+ " (num, writer, title, orgFileName, saveFileName, fileSize, regdate)"
					+ " VALUES(board_file_seq.NEXTVAL, ?, ?, ?, ?, ?, SYSDATE)";
			pstmt = conn.prepareStatement(sql);
			//?에 바인딩
			pstmt.setString(1, dto.getWriter());
			pstmt.setString(2, dto.getTitle());
			pstmt.setString(3, dto.getOrgFileName());
			pstmt.setString(4, dto.getSaveFileName());
			pstmt.setLong(5, dto.getFileSize());			
			//INSERT or UPDATE or DELETE문을 수행하고 수정,삭제,추가된 row의 개수 리턴
			rowCount = pstmt.executeUpdate();
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			try {
				if (pstmt != null)
					pstmt.close();
				if (conn != null)
					conn.close();
			} catch (Exception e2) {
			}
		}

		if (rowCount > 0) {
			return true;
		} else {
			return false;
		}
	}
	
	//파일 목록을 리턴해주는 메소드	
	public List<FileDto> getList() {
		//파일 목록을 담을  ArrayList 객체 생성
		List<FileDto> list=new ArrayList<FileDto>();
		//필요한 객체를 담을 지역변수를 미리 만들어둔다.
		Connection conn = null;
		PreparedStatement pstmt = null;
		ResultSet rs = null;		
		try {
			//Connection Pool에서 Connection 객체를 하나 얻어온다.
			conn = new DbcpBean().getConn();
			//실행할 sql문의 뼈대 구성하기
			String sql = "SELECT num, writer, title, orgFileName, fileSize, TO_CHAR(regdate, 'YYYY.MM.DD HH24:MI') regdate"
					+ " FROM board_file"
					+ " ORDER BY num DESC";
			//sql문의 ?에 바인딩할게 있으면 한다.

			pstmt = conn.prepareStatement(sql);
			//SELECT문을 수행하고 결과값을 받아온다.
			rs = pstmt.executeQuery();
			//반복문 돌면서 resultSet에서 필요한 값을 얻어낸다.
			while (rs.next()) {
				//FileDto 객체에 select된 row하나의 정보를 담고
				FileDto dto=new FileDto();				
				dto.setNum(rs.getInt("num"));
				dto.setWriter(rs.getString("writer"));
				dto.setTitle(rs.getString("title"));
				dto.setOrgFileName(rs.getString("orgFileName"));								
				dto.setFileSize(rs.getLong("fileSize"));
				dto.setRegdate(rs.getString("regdate"));
				//ArrayList 객체에 누적시킨다.
				list.add(dto);
			}
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			try {
				if (rs != null)
					rs.close();
				if (pstmt != null)
					pstmt.close();
				if (conn != null)
					conn.close(); //Connection Pool에 Connection 반납하기

			} catch (Exception e2) {
			}
		}
		return list;
	}
}

 

 

- 테이블 전체를 보여줄 getList 메소드 만들기 (SELECT * )

- 반복문을 돌면서 rs(resultset)에 담겨있는 내용을 하나씩 list에 담기!

- ResultSet에서 빼내서 dto의 setter메소드를 사용해서 담는다.

 

- FileSize 는 long 타입이기 때문에 .getLong 을 써준다.

 

 

/file/private/ <upload_form.jsp>

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>/file/private/upload_form.jsp</title>
</head>
<body>
	<div class="container">
		<h3>파일 업로드 폼입니다.</h3>
		<!--
			파일 업로드 폼 작성방법
			1. method="post"
			2. enctype="multipart/form-data"
			3.<input type="file" />
			- enctype-"multipart/form-data"가 설정된 폼을 전송하면
			폼전송된 내용을 추출할때 HttpServletRequest 객체로 추출할 수 없다.
			MultipartRequest 객체를 이용해서 추출해야 한다.		
		-->
		<form action="upload.jsp" method="post" enctype="multipart/form-data">
			<div>
				<label for="title">제목</label>
				<input type="text" name="title" id="title" />
			</div>
			<div>
				<label for="myFile">첨부파일</label>
				<input type="file" name="myFile" id="myFile" />
			</div>
			<button type="submit">업로드</button>
		</form>
	</div>
</body>
</html>

 

 

 

<form action="upload.jsp" method="post" enctype="multipart/form-data">

- 특정 파일을 폼 전송으로 보낼 수 있다.

 

- input type="file" 을 사용해서 파일을 업로드할 수 있는 버튼을 만든다.

 

 

@WebFilter(urlPatterns = {"/private/*", "/users/private/*", " /file/private/* "})

- 회원만 업로드할 수 있는 게시판으로 만들고 싶다면, 로그인 필터에 위 내용 추가하기

- private 하위에 있는 모든 웹페이지에 접근할 때 회원 로그인이 필요하다.

 


 

/file/private/ <upload.jsp>

<%@page import="test.file.dao.FileDao"%>
<%@page import="test.file.dto.FileDto"%>
<%@page import="com.oreilly.servlet.multipart.DefaultFileRenamePolicy"%>
<%@page import="com.oreilly.servlet.MultipartRequest"%>
<%@page import="java.io.File"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%
	//파일 시스템 상에서 webapp의 upload 폴더까지의 절대경로를 얻어낸다.
	String realPath=application.getRealPath("/upload");
	//해당 경로를 access할 수 있는 파일 객체 생성
	File f=new File(realPath);
	if(!f.exists()){ //만일 폴더가 존재하지 않으면
		f.mkdir();//upload 폴더 만들기
	}
	//cos.jar에 있는 MultipartRequest 클래스로 객체 생성하기
	MultipartRequest mr=new MultipartRequest(request,//내부적으로 필요한 HttpServletRequest 객체 전달 
			realPath, 	//업로드된 파일을 저장할 경로 
			1024*1024*100, 	//최대 업로드 사이즈 제한
			"utf-8", 	//한글 파일명 깨지지 않도록 
			new DefaultFileRenamePolicy()); //동일한 파일이 존재하면 자동으로 파일dmf rename해서 저장하도록
	/*
		위의 MultipartRequest 객체가 예외가 발생하지 않고 잘 생성되었다면 파일 업로드가 성공한 것이다.
		따라서 mr에 들어있는 참조값을 이용해서 폼 전송된 값을 추출해서 저장만 잘 하면 된다.	
	*/
	//2. 폼 전송되는 title을 읽어온다.
	String title=mr.getParameter("title");
	//3. 파일의 작성자(업로더)는 HttpSession 객체에서 읽어온다.
	String writer=(String)session.getAttribute("id");
	//4.추가로 원본 파일명, 저장된 파일명, 파일 사이즈도 얻어내서 FileDto 객체에 담아서
	String orgFileName=mr.getOriginalFileName("myFile");
	String saveFileName=mr.getFilesystemName("myFile");
	long fileSize=mr.getFile("myFile").length();	
	//업로드된 파일의 정보를 FileDto에 담고
	FileDto dto=new FileDto();
	dto.setWriter(writer);
	dto.setTitle(title);
	dto.setOrgFileName(orgFileName);
	dto.setSaveFileName(saveFileName);
	dto.setFileSize(fileSize);
	//DB에 저장하고 응답하기
	boolean isSuccess=FileDao.getInstance().insert(dto);	
%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>/file/private/upload.jsp</title>
</head>
<body>
	<%if(isSuccess){ %>
		<p>
			<%=writer %>님이 업로드한 <%=orgFileName %>파일을 저장했습니다.
			<a href="${pageContext.request.contextPath }/file/list.jsp">목록보기</a>
		</p>
		<p><%=realPath %></p>
	<%}else{ %>
		<p>
			업로드 실패!
			<a href="upload_form.jsp">목록보기</a>
		</p>
	<%} %>
</body>
</html>

 

<% String title=request.getParameter("title"); %>

- 위와 같이 작성할 경우, 파일데이터를 읽어오지 못한다.

- 파일 업로드는 일반 jsp 페이지를 읽어오는 것과는 다르다.

- request.getParameter를 쓰는 일반적인 방식으로는 추출할 수 없고, 어떤 라이브러리가 필요하다.

 

 

** 서블릿츠 닷컴 : https://servlets.com/

- 링크에서 cos 압축파일 다운로드: http://servlets.com/cos/

 

- cos 라이브러리에 들어있는 클래스들! 위의 기능을 사용해보려고 한다.

 

- 압축 해제해서 lib 폴더에 들어가면 cos.jar 파일이 있다.

 

- 파일 업로드에 필요한 여러 클래스를 제공해주는 라이브러리이다.

- WEB-INF의 lib 폴더 안에 넣어주기!

 

- webapp에 클라이언트가 접근할 수 있는 upload 폴더 생성

 

MultipartRequest mr=new MultipartRequest(request, realPath, 1024*1024, 
"utf-8", new DefaultFileRenamePolicy());

- MultipartRequest 객체 생성. 5개의 인자를 가진다.

 (요청, 실제 경로, 최대 크기, 인코딩, 이름을 변경하는 메소드 객체)

 

- 기본객체인 application 객체를 사용해서 업로드 폴더까지의 절대경로를 얻어낸다.

 

- cos.jar에 들어있는 객체들

 

- MultipartRequest 객체가 예외가 발생하지 않고 잘 생성되었다면 파일 업로드에 성공한 것이다.
- 따라서 mr에 들어있는 참조값을 이용해서 폼에 전송된 값을 추출해서 저장만 잘 하면 된다.

 

mr.getParameter("title"); : 업로드한 파일의 제목 값을 얻어낼 수 있다.

 

- MultipartRequest 객체에 어떤 메소드가 있는지 살펴보기.

 

 

- 파일을 저장한 경로를 담은 realPath가 함께 나오도록 했다.

 

- 똑같은 파일을 업로드하면 뒤에 자동으로 숫자가 붙는다.

 

- 파일명은 업로드 시 달라질 수 있기 때문에, 변경전/변경후 파일명이 모두 관리되어야 할 필요가 있다.

 (현재 업로드한 파일의 이름이 같은 경우 자동으로 변경하게 하는 메소드를 사용함)

- orgFileName : 원본 파일명은 파일 목록을 보여주는 데에 필요

  saveFileName : 저장된 파일명은 파일을 다운로드하는 데에 필요

 

- 해당 경로(realPath)를 복사해서 탐색기에 넣어보면 파일이 들어가있는 것을 볼 수 있다.

 


 

- 업로드한 파일을 다운로드 해보기

 

/file/download.jsp 생성

<%@page import="java.io.BufferedOutputStream"%>
<%@page import="java.net.URLEncoder"%>
<%@page import="java.io.File"%>
<%@page import="java.io.FileInputStream"%>
<%@page import="test.file.dto.FileDto"%>
<%@page import="test.file.dao.FileDao"%>
<%@ page language="java" contentType="application/octet-stream; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%
	// 1. get 방식 파라미터로 전달되는 다운로드시켜줄 파일의 번호를 읽어온다.
	int num=Integer.parseInt(request.getParameter("num"));
	/*
		2. DB에서 다운로드시켜줄 파일의 정보를 읽어온다.
			어떤 파일명으로 upload 폴더에 저장되어 있는지 => saveFileName
			업로드 당시 원본 파일명은 무엇인지 => orgFileName
			파일의 사이즈는 어떻게 되는지 => fileSize
		- 다운로드 시켜주기 위해서는 위의 세가지 정보가 필요하다.
	*/
	FileDto dto=FileDao.getInstance().getData(num);

	//3. 서버의 파일시스템(upload) 에 저장된 파일에서 바이트 알갱이를 읽어서 출력한다(다운로드)
	String orgFileName=dto.getOrgFileName();
	String saveFileName=dto.getSaveFileName();
	//다운로드 시켜줄 파일의 실제 경로 구성하기 
	String path=application.getRealPath("/upload") + File.separator+saveFileName;
	//다운로드할 파일에서 읽어들일 스트림 객체 생성하기
	FileInputStream fis=new FileInputStream(path);
	//다운로드 시켜주는 작업을 한다. (실제 파일 데이터와 원본파일명을 보내줘야한다.)
	//다운로드 시켜주는 작업을 한다. 
	String encodedName=null;
	
	//한글 파일명 세부처리 
	if(request.getHeader("User-Agent").contains("Firefox")){
	   //벤더사가 파이어 폭스인경우 
	   encodedName=new String
	      (orgFileName.getBytes("utf-8"),"ISO-8859-1");
	}else{ //그외 다른 벤더사 
	   encodedName=URLEncoder.encode(orgFileName, "utf-8");
	   //파일명에 공백이있는 경우 처리 
	   encodedName=encodedName.replaceAll("\\+"," ");
	}
	
	//응답 헤더 정보 설정
	response.setHeader("Content-Disposition","attachment;filename="+encodedName);
	response.setHeader("Content-Transfer-Encoding", "binary");
	
	//다운로드할 파일의 크기 읽어와서 다운로드할 파일의 크기 설정
	response.setContentLengthLong(dto.getFileSize());
	
	//클라이언트에게 출력할수 있는 스트림 객체 얻어오기
	BufferedOutputStream bos=
	   new BufferedOutputStream(response.getOutputStream());
	//한번에 최대 1M byte 씩 읽어올수 있는 버퍼
	byte[] buffer=new byte[1024*1024];
	int readedByte=0;
	//반복문 돌면서 출력해주기
	while(true){
	   //byte[] 객체를 이용해서 파일에서 byte 알갱이 읽어오기
	   readedByte = fis.read(buffer);
	   if(readedByte == -1)break; //더이상 읽을 데이터가 없다면 반복문 빠져 나오기
	   //읽은 만큼 출력하기
	   bos.write(buffer, 0, readedByte);
	   bos.flush(); //출력
	}
	//FileInputStream 닫아주기 
	fis.close();   
%>

 

- 파일명에 다운로드 링크 추가하기. 

- <a>로 감싸주고, get방식으로 전송되는 파일 번호도 같이 넣어준다.

 

- num을 이용해서 저 3가지 값(FileDto)를 가져오는 메소드를 만들어야 한다.(FileDao에)

 

<FileDao>에 메소드 추가 (getData)

//파일 하나의 정보를 리턴하는 메소드	
public FileDto getData(int num) {		
    FileDto dto=null;
    //필요한 객체를 담을 지역변수를 미리 만들어둔다.
    Connection conn = null;
    PreparedStatement pstmt = null;
    ResultSet rs = null;
    try {
        //Connection Pool에서 Connection 객체를 하나 얻어온다.
        conn = new DbcpBean().getConn();
        //실행할 sql문의 뼈대 구성하기
        String sql = "SELECT writer, title, orgFileName, saveFileName, fileSize, regdate"
                + " From board_file"
                + " WHERE num=?";			
        //sql문의 ?에 바인딩할게 있으면 한다.			
        pstmt = conn.prepareStatement(sql);
        pstmt.setInt(1, num);
        //SELECT문을 수행하고 결과값을 받아온다.
        rs = pstmt.executeQuery();
        //반복문 돌면서 resultSet에서 필요한 값을 얻어낸다.
        if (rs.next()) {
            dto=new FileDto();
            dto=new FileDto();
            dto.setNum(num);
            dto.setWriter(rs.getString("writer"));
            dto.setTitle(rs.getString("title"));
            dto.setOrgFileName(rs.getString("orgFileName"));
            dto.setSaveFileName(rs.getString("saveFileName"));
            dto.setFileSize(rs.getLong("fileSize"));
            dto.setRegdate(rs.getString("regdate"));				
        }
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        try {
            if (rs != null)
                rs.close();
            if (pstmt != null)
                pstmt.close();
            if (conn != null)
                conn.close(); //Connection Pool에 Connection 반납하기

        } catch (Exception e2) {
        }
    }
    return dto;
}

 

 

- application 메소드를 통해 realPath 라는 경로를 얻어낼 수 있다.

 

- 저 application은 Servlet Context type 객체이다. 이 경로는 서버가 어떻게 세팅되었느냐에 따라 다르다!

 그래서 메소드를 통해서 얻어낼 수밖에 없다.

 

String path=application.getRealPath("/upload") + File.separator+saveFileName;

- 전체 경로를 구성한다.

 

- byte[] 배열을 돌면서 응답해낼 내용을 추출해낸다.

 

- 파일로부터 네트워크로 출력해낸 것으로,

 클라이언트에게 출력할 수 있는 Stream으로 보내주는 것이다. 저 자체가 응답이다!

 

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

→ <%@ page language="java" contentType="application/octet-stream; charset=UTF-8" pageEncoding="UTF-8"%>

 

- 웹브라우저에게 이 jsp파일에서 '파일을 응답할 것'이라고 알려주어야 한다

 따라서 맨 위에 있는 text/html 을 바꾸어주기!

- 이것을 mime type이라고 부른다.(마임 타입)

 

 

- 다운로드를 위해서는 3가지가 필요하다.

1)저장된 파일명 2)파일의 크기 3)원본파일 이름

- 파일을 읽어들이기 위해 필요한 정보이다.

 

 

- 저장된 파일명(경로)을 이용해서 FileInputStream 객체를 만들 수 있다.

 

 

- 원본 파일명은 (인코딩되어) 경로가 만들어지는 과정으로 전달된다.

 

- 파일의 크기를 알려주고, 반복문을 돌면서  FileInputStream 을 통해서 출력해준다.

 

- 파일을 업로드하면 DB에 저장되고, 개별 파일을 누르면 다운로드도 가능하다!!