FileUpload / Download
cos 라이브러리 사용
‘웹’에서 파일 업로딩 생각해볼 것
- 수많은 사람들이 접속하여 웹을 통해 업로딩된 파일은 서버의 어디에 저장되나?
- 그 수많은 파일들은 어떤 이름으로 저장되어야 하나?
- 업로드 하는 ‘파일 이름이 중복’되면 어떻게 해야 하나?
- 애시당초 중복될 이름을 피할 방법은 있나?
- 파일의 이름이 변경되어 업로딩된다면, 나중에 그 파일을 다시 다운로드 받을때는 어떤 이름이어야 하는가?
- 업로딩 할때 변경된 이름? 업로딩 할때 원래 이름? 원래 이름은 어떻게 기억하고 있어야 하나?
- 정해진 규격의 파일만 업로드 허용해야 한다면 어떻게 해야 하는가?
ex) 이미지 파일만 올리기? 특정 포맷의 파일만 업로딩
ex) 특정 용량 이하의 파일만 업로딩
ex) 이미지의 경우 특정 width, height 규격에 맞는 파일만 업로딩..
* 파일 업로딩 구현 방법들
1. Servlet 3.x 를 이용한 파일 업로딩 방법
: multipart 처리 등의 코드 난해
: 임시 업로딩 파일 등의 처리들도 해야 한다
2. cos 외부 라이브러리를 이용한 파일 업로딩 방법
: 위 이슈들 다루기 편리
* cos 라이브러리 설치
www.servlets.com
하단의 cos-25Dec2008.zip 다운로드
적절한 폴더에 압축 해제후에 lib 폴더에 가서 cos.jar 파일 복사 -> 그리고, WEB-INF/lib 로 붙여 넣기.
cos 라이브러리 레펀런스
다운로드 받은 페이지에 보면 사용하는 클래스들에 대한 온라인 레펀런스들이 있다.
타 라이브러리 사용시 온라인 레퍼런스는 필수
파일 업로드 할때의 form
<input type=”file” ..>
POST 방식으로 전송
<form enctype=”multipart/form-data” ..> 명시
* multipart 에 대해
기존의 parameter 데이터 는 name: value 쌍들로만 구성된 ‘단일 part’ 였지만
multipart 는 성격이 다른 데이터를 여러 part 로 나누어서 서버로 전송된다.
‘파일 업로딩’이 있는 경우, parameter 데이터 뿐 아니라 파일의 개수만큼 여러 part로 쪼개어지기 때문에 반드시 multipart 로 전송해야 하고, 이에 대한 처리가 필요하다.
게시판 첨부파일(insert, select)
시나리오
1. 글작성 :
게시글 마다 ‘임의개수(복수개)의 첨부파일’ 추가 가능
업로드 경로는 Context Path / upload
업로드시 파일원본이름 기억(저장)
2. 글 수정 :
기존 첨부파일 삭제 가능.
첨부파일 새로 추가 가능
3. 글 삭제 :
DB뿐 아니라, 첨부파일도 경로에서 물리적인 파일도 삭제 하기
4. 글 조회 :
첨부파일 이 이미지인 경우 게시물 화면에 보이기.
첨부파일 명을 클릭하면 다운로드 동작하기.
다운로드시 업로드당시 파일원본이름으로 다운받게 하기.
* ERD, DDL 작성 실행
t2_file
* upload 폴더 생성 -webapp
* 필요 쿼리문 작성
*.sql
D.java
// ★첨부파일
// t2_write 를 SELECT
public static final String SQL_FILE_SELECT =
"SELECT uid \"uid\", source \"source\", file \"file\", write_uid \"write_uid\" \r\n"
+ "FROM t2_file \r\n"
;
// 첨부파일 1개 INSERT
public static final String SQL_FILE_INSERT =
"INSERT INTO t2_file"
+ "(source, file, write_uid) "
+ "VALUES"
+ "(?, ?, ?)";
// 특정 글 (write_uid)의 첨부파일(들)을 SELECT (조회, 수정)
public static final String SQL_FILE_SELECT_BY_WRUID =
SQL_FILE_SELECT
+ "WHERE write_uid = ? \r\n"
+ "ORDER BY uid DESC\r\n"
;
// 특정 첨부파일 (t2_write.uid) 하나를 SELECT
public static final String SQL_FILE_SELECT_BY_UID =
SQL_FILE_SELECT
+ "WHERE uid = ?";
// 특정 첨부파일 (t2_write.uid) 하나를 DELETE (수정)
public static final String SQL_FILE_DELETE_BY_UID =
"DELETE FROM t2_file WHERE uid = ?";
// 특정 글의 (write_uid) 의 첨부파일(들)을 DELETE (삭제)
public static final String SQL_FILE_DELETE_BY_WRUID =
"DELETE FROM t2_file WHERE write_uid = ?";
* FileDTO 작성
이번 예제에선 이미지 여부인 isImage 도 추가
package domain;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class FileDTO {
private int uid;
private String source;
private String file;
private boolean isImage;
}
* FileDAO 작성
package domain;
import java.io.File;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import common.D;
public class FileDAO {
Connection conn;
PreparedStatement pstmt;
Statement stmt;
ResultSet rs;
// DAO 객체가 생성될때 Connection 도 생성된다
public FileDAO(){
try {
Class.forName(D.DRIVER);
conn = DriverManager.getConnection(D.URL, D.USERID, D.USERPW);
System.out.println("FileDAO생성, 데이터베이스 연결!!");
} catch (Exception e) {
e.printStackTrace();
} // end try
}// 생성자
// DB 자원 반납 메소드, 만들어놓으면 편함..
public void close() throws SQLException{
if(rs != null) rs.close();
if(pstmt != null) pstmt.close();
if(stmt != null) stmt.close();
if(conn != null) conn.close();
} // end close();
// 특정 글 (write uid) 에 첨부파일(들) 을 추가 INSERT
// 글 insert, update 시 사용됨
public int insert(int wrUid, // 첨부될 글
List<String> originalFileNames, // 원본 파일명(들)
List<String> fileSystemNames// 저장한 파일명(들)
) throws SQLException {
int cnt = 0;
try {
pstmt = conn.prepareStatement(D.SQL_FILE_INSERT);
// 방법1: 매번 쿼리 실행하기
// for(int i = 0; i < originalFileNames.size(); i++) {
// pstmt.setString(1, originalFileNames.get(i));
// pstmt.setString(2, fileSystemNames.get(i));
// pstmt.setInt(3, wrUid);
// cnt += pstmt.executeUpdate();
// }
// 방법2: batch 사용
// 쿼리 실행을 바로 하지 않고, batch 에 담아두었다가, (addBatch)
// 한번에 실행 (executeBatch()) --> 훨씬 실행속도 빠르다
for(int i = 0; i < originalFileNames.size(); i++) {
pstmt.setString(1, originalFileNames.get(i));
pstmt.setString(2, fileSystemNames.get(i));
pstmt.setInt(3, wrUid);
pstmt.addBatch(); // batch 에 담기
pstmt.clearParameters(); // ? 파라미터 clear
}
int [] arr = pstmt.executeBatch(); // batch 실행 리턴값이 int[]
for(int value : arr) {
cnt += value;
}
} finally {
close();
}
return cnt;
} // end insert()
// 첨부파일 읽어들이기
// ResultSet --> List 로 리턴
public List<FileDTO> buildList(ResultSet rs) throws SQLException {
List<FileDTO> list = new ArrayList<>();
while(rs.next()) {
int uid = rs.getInt("uid");
String source = rs.getString("source");
String file = rs.getString("file");
FileDTO dto = FileDTO.builder()
.uid(uid)
.source(source)
.file(file)
.build()
;
list.add(dto);
}
return list;
} // end buildList()
// 특정 글(write uid) 의 첨부파일(들) SELECT
// 글 조회, 수정시 필요
public List<FileDTO> selectFilesByWrUid(int wrUid) throws SQLException{
List<FileDTO> list = null;
try{
// 트랜잭션 처리
pstmt = conn.prepareStatement(D.SQL_FILE_SELECT_BY_WRUID);
pstmt.setInt(1, wrUid);
rs = pstmt.executeQuery();
list = buildList(rs);
}finally{
close();
} // end try
return list;
} // end selectFilesByWrUid()
// 특정 첨부파일(t2_write.uid) 한개 SELECT
public List<FileDTO> selectByUid(int uid) throws SQLException{
List<FileDTO> list = null;
try{
// 트랜잭션 처리
pstmt = conn.prepareStatement(D.SQL_FILE_SELECT_BY_UID);
pstmt.setInt(1, uid);
rs = pstmt.executeQuery();
list = buildList(rs);
}finally{
close();
} // end try
return list;
} // end selectByUid()
// List<FileDTO> 의 물리적인 파일(들) 삭제
public int deleteFiles(List<FileDTO> list, HttpServletRequest request) {
if(list == null || list.size() == 0 || request == null) return 0;
int cnt = 0;
// 물리적인 경로
ServletContext context = request.getServletContext();
String saveDirectory = context.getRealPath("/upload"); // 실제 서버에 저장된 물리적인 경로
for(FileDTO dto : list) {
File f = new File(saveDirectory, dto.getFile()); // 물리적으로 저장된 파일들이 삭제 대상
System.out.println("삭제시도--> " + f.getAbsolutePath());
if(f.exists()) {
if(f.delete()) { // 삭제 성공
System.out.println("삭제 성공");
cnt++;
} else {
System.out.println("삭제 실패");
}
} else {
System.out.println("파일이 존재하지 않습니다");
}
}
return cnt;
} // end deleteFiles()
// 특정 글(write uid) 의 첨부파일(들) 삭제
// 1) 물리적인 파일(들) 삭제, 2)DB 삭제
public int deleteByWrUid(int wrUid, HttpServletRequest request) throws SQLException {
int cnt = 0;
try {
// 특정 글(wrUid) 의 첨부파일들 정보 가져오기
pstmt = conn.prepareStatement(D.SQL_FILE_SELECT_BY_WRUID);
pstmt.setInt(1, wrUid);
rs = pstmt.executeQuery();
List<FileDTO> list = buildList(rs);
// 물리적인 파일(들) 삭제
cnt = deleteFiles(list, request);
// ※ test_file 에서 외래키 ON DELETE CASCADE 적용되어있으면 DB는 자동 삭제
System.out.println("첨부파일 " + cnt + "개 삭제");
} finally {
close();
}
return cnt;
} // end deleteByWrUid()
// 복수개의 첨부파일(들) 제거
// 글 '수정' 단계에서 발생. '글' 이 삭제되는 것이 아니라서, 파일(들)만 개별적으로 삭제해야 함.
public int deleteByUid(int [] uids, HttpServletRequest request) throws SQLException {
if(uids == null || uids.length == 0 || request == null) return 0; // 매개변수 검증
int cnt = 0;
// 101, 204, 319 번 파일을 읽어오려면
// SELECT * FROM t2_file WHERE uid = 101 OR uid = 204 OR uid = 319
// SELECT * FROM t2_file WHERE uid IN (101, 204, 319)
// 101, 204, 319 번 파일을 지우려면?
// DELETE FROM t2_file WHERE uid = 101 OR uid = 204 OR uid = 319
// DELETE FROM t2_file WHERE uid IN (101, 204, 319)
try {
// 삭제할 파일 정보들 읽어오기 -> List<FileDTO>
StringBuffer sql = new StringBuffer("SELECT uid \"uid\", source \"source\", file \"file\" FROM t2_file WHERE uid IN (");
for(int uid : uids) {
sql.append(uid + ",");
}
sql.deleteCharAt(sql.lastIndexOf(",")); // 맨 끝의 콤마 삭제
sql.append(")");
stmt = conn.createStatement();
rs = stmt.executeQuery(sql.toString());
List<FileDTO> list = buildList(rs);
// 1. 물리적인 파일(들) 제거
deleteFiles(list, request); // 파일 삭제
// 2. file 테이블 삭제. '글' 이 삭제되는 것이 아니라서, file 데이터를 개별적으로 삭제해야함.
sql = new StringBuffer("DELETE FROM t2_file WHERE uid IN (");
for(int uid : uids) {
sql.append(uid + ",");
}
sql.deleteCharAt(sql.lastIndexOf(",")); // 맨 끝의 콤마 삭제
sql.append(")");
System.out.println("파일 테이블 삭제: " + sql);
cnt = stmt.executeUpdate(sql.toString());
System.out.println(cnt + " 개 삭제");
} finally {
close();
}
return cnt;
} // end deleteByUid()
}
글 작성
* write.jsp
write.jsp 수정
<form enctype="Multipart/form-data">
jQuery 사용
가변적으로 첨부 파일 추가 삭제 가능.
name = upfile + i ← 주목
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.1/dist/css/bootstrap.min.css" rel="stylesheet">
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.1/dist/js/bootstrap.bundle.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<title>작성</title>
</head>
<body>
<%-- 인증 헤더 --%>
<jsp:include page="/WEB-INF/views/common/header.jsp"/>
<div class="container mt-3">
<h2>작성</h2>
<hr>
<form action="writeOk.do" method="POST" enctype="Multipart/form-data">
<div class="mb-3">
<label for="name">작성자:</label>
<span class="form-control">${PRINCIPAL.username } (${PRINCIPAL.name })</span>
</div>
<div class="mb-3 mt-3">
<label for="subject">제목:</label>
<input type="text" class="form-control" id="subject" placeholder="제목을 입력하세요" name="subject" required>
</div>
<div class="mb-3 mt-3">
<label for="content">내용:</label>
<textarea class="form-control" rows="5" id="content" placeholder="내용을 입력하세요" name="content"></textarea>
</div>
<!-- 첨부파일 -->
<%-- 상단에 jQuery 추가하기--%>
<div class="container mt-3 mb-3 border rounded">
<div class="mb-3 mt-3">
<label>첨부파일:</label>
<div id="files">
</div>
<button type="button" id="btnAdd" class="btn btn-secondary">추가</button>
</div>
</div>
<script>
var i = 0;
$("#btnAdd").click(function(){
$("#files").append(
'<div class="input-group mb-2">'
+ '<input class="form-control col-xs-3" type="file" name="upfile' + i + '"/>'
+ '<button type="button" class="btn btn-outline-danger" onclick="$(this).parent().remove()">삭제</button>'
+ '</div>');
i++;
});
</script>
<!-- 첨부파일 -->
<button type="submit" class="btn btn-outline-dark">작성완료</button>
<a class="btn btn-outline-dark" href="list.do">목록</a>
</form>
</div>
</body>
</html>
* WriteCommand.java 수정
MultiPartRequest 사용!
일단 multipart 로 request 가 발생되면, 기존의 request.getParamerter가 안된다.
1. // 1. MultipartRequest 생성 -> 파일 업로드 됨
2. 업로드된 파일의 '원본이름(들)' 과 '저장된 이름(들)' 받아오기
3. 게시글 및 첨부파일 -> DB 에 저장 (WriteDAO 의 insert() 수정)
package command.write;
import java.io.IOException;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.oreilly.servlet.MultipartRequest;
import com.oreilly.servlet.multipart.DefaultFileRenamePolicy;
import com.oreilly.servlet.multipart.FileRenamePolicy;
import command.Command;
import common.C;
import domain.UserDTO;
import domain.WriteDAO;
import domain.WriteDTO;
public class WriteCommand implements Command {
@Override
public void execute(HttpServletRequest request, HttpServletResponse response) throws IOException {
int cnt = 0;
//---------------------------------------------
// 1. MultipartRequest 생성 -> 파일 업로드 됨
ServletContext context = request.getServletContext();
String saveDirectory = context.getRealPath("upload"); // 물리적으로 저장될 경로
System.out.println("업로드 경로: " + saveDirectory); // 확인해보자!
int maxPostSize = 5 * 1024 * 1024; // POST 받기 최대 5Mbyte
String encoding = "utf-8";
FileRenamePolicy policy = new DefaultFileRenamePolicy(); // 업로딩 파일 이름 중복에 대한 rename 정책
MultipartRequest multi = null; // cos
// MultipartRequest 생성단계에서 이미 파일은 저장됨.
multi = new MultipartRequest(
request,
saveDirectory,
maxPostSize,
encoding,
policy
);
//---------------------------------------------
// 2. 업로드된 파일의 '원본이름(들)' 과 '저장된 이름(들)' 받아오기
List<String> originalFileNames = new ArrayList<String>();
List<String> fileSystemNames = new ArrayList<String>();
Enumeration names = multi.getFileNames(); // type="file" 요소들의 name들 추출
while(names.hasMoreElements()) {
String name = (String)names.nextElement();
if(name.startsWith("upfile")) { // name="upfile##" 인 경우만 첨부파일 등록 (다른 웹 에디터와 섞이지 않도록)
String originalFileName = multi.getOriginalFileName(name); // 원본 이름
String fileSystemName = multi.getFilesystemName(name); // 저장된(rename)된 이름
System.out.println("첨부파일(" + name + ") " + originalFileName + "->" + fileSystemName);
if(originalFileNames != null && fileSystemName != null) {
originalFileNames.add(originalFileName);
fileSystemNames.add(fileSystemName);
}
}
} // end while
//---------------------------------------------
// 3. 게시글 및 첨부파일 -> DB 에 저장
// ★ request 에서가 아닌 MultipartRequest 객체로부터 가져와야 한다
// 입력한 값 받아오기
String subject = multi.getParameter("subject");
String content = multi.getParameter("content");
// ※ 파라미터 유효성 검증
// 현재 로그인 한 사용자 정보
UserDTO user = (UserDTO)request.getSession().getAttribute(C.PRINCIPAL);
WriteDTO dto = new WriteDTO();
dto.setUser(user);
dto.setSubject(subject);
dto.setContent(content);
WriteDAO dao = new WriteDAO();
try {
// 첨부파일 정보도 같이 DB 에 INSERT
cnt = dao.insert(dto, originalFileNames, fileSystemNames);
} catch (SQLException e) {
e.printStackTrace();
}
request.setAttribute("result", cnt);
request.setAttribute("dto", dto); // auto-generated key (uid)
}
}
글읽기
FileDAO 에 추가
// 첨부파일 읽어들이기
// ResultSet --> List 로 리턴
public List<FileDTO> buildList(ResultSet rs) throws SQLException {
List<FileDTO> list = new ArrayList<>();
while(rs.next()) {
int uid = rs.getInt("uid");
String source = rs.getString("source");
String file = rs.getString("file");
FileDTO dto = FileDTO.builder()
.uid(uid)
.source(source)
.file(file)
.build()
;
list.add(dto);
}
return list;
} // end buildList()
// 특정 글(write uid) 의 첨부파일(들) SELECT
// 글 조회, 수정시 필요
public List<FileDTO> selectFilesByWrUid(int wrUid) throws SQLException{
List<FileDTO> list = null;
try{
// 트랜잭션 처리
pstmt = conn.prepareStatement(D.SQL_FILE_SELECT_BY_WRUID);
pstmt.setInt(1, wrUid);
rs = pstmt.executeQuery();
list = buildList(rs);
}finally{
close();
} // end try
return list;
} // end selectFilesByWrUid()
// 특정 첨부파일(t2_write.uid) 한개 SELECT
public List<FileDTO> selectByUid(int uid) throws SQLException{
List<FileDTO> list = null;
try{
// 트랜잭션 처리
pstmt = conn.prepareStatement(D.SQL_FILE_SELECT_BY_UID);
pstmt.setInt(1, uid);
rs = pstmt.executeQuery();
list = buildList(rs);
}finally{
close();
} // end try
return list;
} // end selectByUid()
// List<FileDTO> 의 물리적인 파일(들) 삭제
public int deleteFiles(List<FileDTO> list, HttpServletRequest request) {
if(list == null || list.size() == 0 || request == null) return 0;
int cnt = 0;
// 물리적인 경로
ServletContext context = request.getServletContext();
String saveDirectory = context.getRealPath("/upload"); // 실제 서버에 저장된 물리적인 경로
for(FileDTO dto : list) {
File f = new File(saveDirectory, dto.getFile()); // 물리적으로 저장된 파일들이 삭제 대상
System.out.println("삭제시도--> " + f.getAbsolutePath());
if(f.exists()) {
if(f.delete()) { // 삭제 성공
System.out.println("삭제 성공");
cnt++;
} else {
System.out.println("삭제 실패");
}
} else {
System.out.println("파일이 존재하지 않습니다");
}
}
return cnt;
} // end deleteFiles()
// 특정 글(write uid) 의 첨부파일(들) 삭제
// 1) 물리적인 파일(들) 삭제, 2)DB 삭제
public int deleteByWrUid(int wrUid, HttpServletRequest request) throws SQLException {
int cnt = 0;
try {
// 특정 글(wrUid) 의 첨부파일들 정보 가져오기
pstmt = conn.prepareStatement(D.SQL_FILE_SELECT_BY_WRUID);
pstmt.setInt(1, wrUid);
rs = pstmt.executeQuery();
List<FileDTO> list = buildList(rs);
// 물리적인 파일(들) 삭제
cnt = deleteFiles(list, request);
// ※ test_file 에서 외래키 ON DELETE CASCADE 적용되어있으면 DB는 자동 삭제
System.out.println("첨부파일 " + cnt + "개 삭제");
} finally {
close();
}
return cnt;
} // end deleteByWrUid()
// 복수개의 첨부파일(들) 제거
// 글 '수정' 단계에서 발생. '글' 이 삭제되는 것이 아니라서, 파일(들)만 개별적으로 삭제해야 함.
public int deleteByUid(int [] uids, HttpServletRequest request) throws SQLException {
if(uids == null || uids.length == 0 || request == null) return 0; // 매개변수 검증
int cnt = 0;
// 101, 204, 319 번 파일을 읽어오려면
// SELECT * FROM t2_file WHERE uid = 101 OR uid = 204 OR uid = 319
// SELECT * FROM t2_file WHERE uid IN (101, 204, 319)
// 101, 204, 319 번 파일을 지우려면?
// DELETE FROM t2_file WHERE uid = 101 OR uid = 204 OR uid = 319
// DELETE FROM t2_file WHERE uid IN (101, 204, 319)
try {
// 삭제할 파일 정보들 읽어오기 -> List<FileDTO>
StringBuffer sql = new StringBuffer("SELECT uid \"uid\", source \"source\", file \"file\" FROM t2_file WHERE uid IN (");
for(int uid : uids) {
sql.append(uid + ",");
}
sql.deleteCharAt(sql.lastIndexOf(",")); // 맨 끝의 콤마 삭제
sql.append(")");
stmt = conn.createStatement();
rs = stmt.executeQuery(sql.toString());
List<FileDTO> list = buildList(rs);
// 1. 물리적인 파일(들) 제거
deleteFiles(list, request); // 파일 삭제
// 2. file 테이블 삭제. '글' 이 삭제되는 것이 아니라서, file 데이터를 개별적으로 삭제해야함.
sql = new StringBuffer("DELETE FROM t2_file WHERE uid IN (");
for(int uid : uids) {
sql.append(uid + ",");
}
sql.deleteCharAt(sql.lastIndexOf(",")); // 맨 끝의 콤마 삭제
sql.append(")");
System.out.println("파일 테이블 삭제: " + sql);
cnt = stmt.executeUpdate(sql.toString());
System.out.println(cnt + " 개 삭제");
} finally {
close();
}
return cnt;
} // end deleteByUid()
}
ViewCommand 수정
- wr_uid 값에 첨부된 파일을 test_file 에서 읽어들이기 → FileDTO []
- 이미지 파일 여부 체크하여 세팅 하기
- 결과(FileDTO [] ) 를 request 에 담아 VIEW 에 보내기
package command.write;
import java.sql.SQLException;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import command.Command;
import domain.WriteDAO;
import domain.WriteDTO;
public class ViewCommand implements Command {
@Override
public void execute(HttpServletRequest request, HttpServletResponse response) {
int uid = Integer.parseInt(request.getParameter("uid")); // 매개변수 검증 필요
WriteDAO dao = new WriteDAO();
List<WriteDTO> list = null;
try {
list = dao.readByUid(uid); // 읽기 + 조회수 증가
request.setAttribute("list", list);
} catch(SQLException e) {
e.printStackTrace();
}
}
}
view.jsp
- 첨부파일 및 다운로드 링크
- 이미지 파일인 경우 보여주기
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>
<%-- JSTL 을 사용하니 import 의 번잡함도 사라지고 JAVA 변수, JAVA코드도 사라진다! --%>
<c:choose>
<c:when test="${empty list || fn:length(list) == 0 }">
<script>
alert("해당 정보가 삭제되거나 없습니다");
history.back();
</script>
</c:when>
<c:otherwise>
<c:set var="dto" value="${list[0] }"/>
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.1/dist/css/bootstrap.min.css" rel="stylesheet">
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.1/dist/js/bootstrap.bundle.min.js"></script>
<title>조회 - ${dto.subject }</title>
</head>
<script>
function chkDelete(){
var answer = confirm("삭제하시겠습니까?");
if(answer){
document.forms['frmDelete'].submit();
}
}
</script>
<body>
<%-- 인증 헤더 --%>
<jsp:include page="/WEB-INF/views/common/header.jsp"/>
<div class="container mt-3">
<h2>조회 - ${dto.subject }</h2>
<hr>
<div class="mb-3 mt-3 clearfix">
<span class="float-start me-2">uid: ${dto.uid }</span>
<span class="float-end ms-4">작성일: ${dto.regDateTime }</span>
<span class="float-end">조회수: ${dto.viewCnt }</span>
</div>
<section>
<form name="frmDelete" action="deleteOk.do" method="POST">
<input type="hidden" name="uid" value="${dto.uid }">
</form>
<div class="mb-3">
<label for="name">작성자:</label>
<div class="border bg-light rounded p-2" >${dto.user.username }</div>
</div>
<div class="mb-3 mt-3">
<label for="subject">제목:</label>
<div class="border bg-light rounded p-2" >${dto.subject }</div>
</div>
<div class="mb-3 mt-3">
<label for="content">내용:</label>
<div class="border bg-light rounded p-2" >${dto.content }</div>
</div>
<c:if test="${sessionScope.PRINCIPAL.uid == dto.user.uid }">
<a class="btn btn-outline-dark" href="update.do?uid=${dto.uid }">수정</a>
</c:if>
<a class="btn btn-outline-dark" href="list.do?page=${page != null ? page : '' }">목록</a>
<c:if test="${sessionScope.PRINCIPAL.uid == dto.user.uid }">
<button type="button" class="btn btn-outline-dark" onclick="chkDelete()">삭제</button>
</c:if>
<c:if test="${not empty sessionScope.PRINCIPAL }">
<a class="btn btn-outline-dark" href="write.do">작성</a>
</c:if>
</section>
</div>
</body>
</html>
</c:otherwise>
</c:choose>
* Controller 에 추가
download.do 처리
DownLoadCommand.java .추가
특정 t2_file.uid 값의 파일 다운로드
'JSP' 카테고리의 다른 글
[JSP] Comment (0) | 2022.04.13 |
---|---|
[JSP] File Update, Delete (0) | 2022.04.12 |
[JSP] Pagination (0) | 2022.04.11 |
[JSP] Authentification - 회원제 게시판 (0) | 2022.04.10 |
[JSP] Authentication - 로그인/로그아웃 (0) | 2022.04.10 |