파일 업로드 (DB를 이용하지 않고)
1. 파일 전송
<form>태그의 enctype 속성
- 폼 데이터를 서버로 보낼 때 해당 데이터가 인코딩되는 방식을 명시
application/x-www-form-urlencoded | default 값으로, 모든 문자들을 서버로 보내기 전에 인코딩됨을 명시 |
text/plain | 공백 문자(space)는 “+” 기호로 변환, 나머지 문자는 모두 인코딩하지 않음을 명시 |
multipart/form-data |
모든 문자를 인코딩하지 않음을 명시한다. 이 방식은 <form> 요소가 파일이나 이미지를 서버로 전송할 때 주로 사용한다. |
- multipart/form-data
<form>에는 여러 content-type의 <input>이 여러 개 들어갈 수 있다
서로 다른 content-type의 데이터가 하나의 HTTP Request Body에 들어갈 때
각각의 데이터를 구별하여 HTTP Request Body에 넣는 방법
< 참고 : https://velog.io/@shin6403/HTTP-multipartform-data-%EB%9E%80 >
** 파일 업로드
- <form> 태그의 method(전송 방식)를 post로, enctype(인코딩 방식)을 multipart/form-data로 한다
- <input> 태그의 type를 file로 하면 파일을 첨부할 수 있다
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<h1>DB없이 파일 업로드</h1>
<form action = "/" method = "post" enctype = "multipart/form-data">
<input type = "text" name = "uploader">
<br><br>
<input type = "file" name = "attachment">
<br><br>
<button type = "submit">업로드</button>
</form>
2. src/main/resources에서 application.properties에서 multipart resolver에 대한 설정 추가
- spring.servlet.multipart.max-file-size : 서버로 전송되는 파일의 개별 용량 제한
- spring.servlet.multipart.max-request-size : 서버로 전송되는 파일의 총 용량 제한
# multipart resolver
spring.servlet.multipart.max-file-size=1MB
spring.servlet.multipart.max-request-size=10MB
3. Controller에서의 파일 수신 (Multipart Request 처리)
- Controller에서는 multipartFile 형태로 파일 수신
- 기존 @RequestParam, @ModelAttribute 등의 입력 방식 사용 가능
@Controller
public class FileController {
// 의존성 주입
@Autowired
private AttachmentDao attachmentDao;
// 파일 업로드 Mapping
// 파일 업로드 Mapping에 DTO 전달 및 DB 처리 후 파일 저장
@PostMapping("/upload")
public String upload(@RequestParam MultipartFile attachment) throws IllegalStateException, IOException {
// 첨부파일 업로드 기록 등록
// 파일 업로드 기록 등록을 위해 다음 시퀀스 번호 반환
int attachmentNo = attachmentDao.sequence();
// AttachmentDto의 인스턴스 생성
// - 첨부파일 번호(attachmentNo)는 반환한 다음 시퀀스 번호
// - 첨부파일 이름(attachmentName)은 전달받은 MultipartFile의 원본 파일 이름
// - 첨부파일 종류(attachmentType)는 전달받은 MultipartFile의 파일 종류
// - 첨부파일 크기(attachmentSize)는 전달받은 MultipartFile의 파일 크기
// 생성한 AttachmentDto의 인스턴스로 첨부파일 업로드 기록 등록(INSERT)
attachmentDao.insert(AttachmentDto.builder()
.attachmentNo(attachmentNo)
.attachmentName(attachment.getOriginalFilename())
.attachmentType(attachment.getContentType())
.attachmentSize(attachment.getSize())
.build());
// 실제 첨부파일을 특정 경로에 저장
// 해당 문자열을 추상 경로로 변환하여 File 클래스의 인스턴스 dir 생성
File dir = new File("C:\\\\Users\\\\hyeul\\\\upload");
// 해당 추상 경로의 디렉토리 생성 (폴더가 없으면 자동으로 생성)
dir.mkdirs();
// dir의 추상 경로를 상위 경로, 해당 파일의 시퀀스 번호를 하위 경로로 하는 File의 인스턴스 생성
File target = new File(dir, String.valueOf(attachmentNo));
// View에서 전달받은 MultipartFile을 해당 디렉토리로 보내서 시퀀스 번호를 파일명으로 바꿔서 저장
attachment.transferTo(target);
return "redirect:/";
}
// 파일 업로드 기록 전체조회 Mapping
@GetMapping("/list")
public String list(Model model) {
// model에 전체조회 결과를 첨부
model.addAttribute("list", attachmentDao.selectList());
return "list";
}
}
MultipartFile 인터페이스
- org.springframework.web 패키지에 포함
- 메소드 일부
byte[] | getBytes() | 파일의 내용을 byte 배열로 반환 |
String | getContentType() | 파일의 content-type 반환 |
String | getName() | multipart 형식의 매개변수 이름을 반환 |
long | getOriginalFileName() | 클라이언트의 파일 시스템에 있는 원래 파일 이름을 반환 |
boolean | isEmpty() | 업로드된 파일이 없는지 여부를 반환 |
default void | transferTo(File dest) | 수신한 파일을 해당 추상 경로로 보낸다 |
File 클래스
- java.io 패키지에 포함
- 생성자
File(File parent, String child) | 상위 경로를 갖는 File 클래스의 인스턴스와 하위 경로 문자열을 이용하여 인스턴스 생성 |
File(String pathname) | 해당 경로명 문자열을 추상 경로로 변환하여 인스턴스 생성 |
File(String parent, String child) | 상위 경로 문자열과 하위 경로 문자열을 이용하여 인스턴스 생성 |
File(URI uri) | uri를 경로로 하는 인스턴스 생성 |
** File parent : 상위 디렉토리를 추상경로로 변환한 File 클래스의 인스턴스
** String parent : 상위 디렉토리를 나타내는 문자열
** String child : 하위 디렉토리를 나타내는 문자열
- 메소드
boolean | mkdir() | 추상 경로 이름의 디렉토리 생성 (상위 디렉토리가 없으면 생성하지 않는다) |
boolean | mkdirs() | 추상 경로 이름의 디렉토리 생성 (상위 디렉토리가 없다면 생성한다) |
회원 가입시 프로필 사진 업로드
MemberController
- 회원 가입시 첨부 파일을 저장할 수 있도록 회원 가입 Mapping을 수정
- 첨부파일이 존재할 때만 첨부파일을 특정 디렉토리에 저장하도록 설정
@Controller
@RequestMapping("/member")
public class MemberController {
// 의존성 주입
@Autowired
private MemberDao memberDao;
// 1. 회원 가입
// 1-1. 회원 가입 Mapping
@GetMapping("/join")
public String join() {
// 회원 가입 페이지(join.jsp)로 연결
// return "/WEB-INF/views/member/insert.jsp";
return "member/join";
}
// 1-2. 회원 가입 Mapping에 DTO 전달 및 DB 처리
@PostMapping("/join")
public String join(@ModelAttribute MemberDto memberDto,
@RequestParam MultipartFile memberProfile
) throws IllegalStateException, IOException {
// DB에서 등록(INSERT) 실행
memberDao.join(memberDto);
// 프로필 사진 첨부파일 저장
if(!memberProfile.isEmpty()) { // 첨부파일이 존재한다면(비어있지 않으면)
// 해당 문자열을 추상 경로로 변환하여 File 클래스의 인스턴스 생성
File directory = new File("C:\\Users\\hyeul\\upload\\member");
// 해당 추상 경로의 디렉토리 생성 (폴더가 없으면 자동으로 생성)
directory.mkdirs();
// dir의 추상 경로를 상위 경로, memberDto의 memberId를 하위 경로로 하는 File의 인스턴스 생성
File target = new File(directory, memberDto.getMemberId());
// View에서 전달받은 MultipartFile을 해당 디렉토리로 보내서 memberDto의 memberId와 같은 이름으로 저장
memberProfile.transferTo(target);
}
// 등록(INSERT) 처리 후 등록 완료(join_success) Mapping으로 강제 이동(redirect) (새로고침 시 중복 입력 방지)
return "redirect:join_success";
}
// 1-3. 회원 가입 완료 Mapping
@GetMapping("/join_success")
public String joinSuccess() {
// 회원 가입 완료 페이지(joinSuccess.jsp)로 연결
// return "/WEB-INF/views/member/joinSuccess.jsp";
return "member/joinSuccess";
}
}
member 폴더의 join.jsp
- 프로필 사진을 업로드하기 위한 input을 추가한다
- <input> 태그의 type 속성을 file로 하면 첨부파일을 선택할 수 있다
- <input> 태그의 accept 속성을 설정하면 첨부파일의 확장자를 제한할 수 있다
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<jsp:include page = "/WEB-INF/views/template/header.jsp">
<jsp:param name = "title" value = "회원 가입"/>
</jsp:include>
<h1>가입 정보 입력</h1>
<%-- enctype을 multipart/form-data로 변경 --%>
<form action = "insert" method = "post" enctype = "multipart/form-data">
아이디 : <input name ="memberId" placeholder = "아이디 입력" type = "text" required><br><br>
비밀번호 : <input name ="memberPw" placeholder = "비밀번호 입력" type = "password" required ><br><br>
닉네임 : <input name ="memberNick" placeholder = "닉네임 입력" type = "text" required><br><br>
생년월일 : <input name ="memberBirth" placeholder = "생년월일 입력" type = "date" required><br><br>
연락처(선택) : <input name ="memberTel" placeholder = "연락처 입력" type = "text"><br><br>
이메일(선택) : <input name ="memberEmail" placeholder = "이메일 입력" type = "text"><br><br>
우편번호(선택) : <input name ="memberPost" placeholder = "우편주소 입력" type = "text" maxlength = "6" size = "6"><br><br>
기본주소(선택) : <input name ="memberBaseAddress" placeholder = "기본주소 입력" type = "text"><br><br>
상세주소(선택) : <input name ="memberDetailAddress" placeholder = "상세주소 입력" type = "text"><br><br>
<%-- 프로필 사진 업로드용 input 추가 --%>
<%-- accept 속성을 통해 업로드 가능한 파일의 확장자를 제한할 수 있다 --%>
프로필 이미지 : <input type = "file" name = "memberProfile" accept = ".png, .jpg"><br><br>
<button>등록</button>
</form>
<jsp:include page = "/WEB-INF/views/template/footer.jsp"></jsp:include>
파일 다운로드 (DB를 이용하지 않고)
회원 가입시 업로드한 프로필 사진이 마이페이지에 나오도록
1. apache commons io 의존성 추가
2. Controller에서 ResponseEntity를 반환
- Controller에서 파일 다운로드에 대한 Mapping의 반환형은 ResponseEntity이다
- 파일 다운로드에 대한 Mapping의 메소드 실행 결과(return값)를 Http Response Body에 담아야 하므로
메소드 위에 @ResponseBody 어노테이션을 붙인다
@Controller
@RequestMapping("/member")
public class MemberController {
// 의존성 주입
@Autowired
private MemberDao memberDao;
// 회원 가입시 업로드한 프로필사진 다운로드
@GetMapping("/download")
@ResponseBody
public ResponseEntity<ByteArrayResource> download(@RequestParam String memberId) throws IOException {
// 해당 문자열을 추상 경로로 변환하여 File 클래스의 인스턴스 생성
File directory = new File("C:\\Users\\hyeul\\upload\\member");
// dir의 추상 경로를 상위 경로, memberDto의 memberId를 하위 경로로 하는 File의 인스턴스 생성
File target = new File(directory, memberId);
if(target.exists()) { // 해당 디렉토리에 해당 파일이 존재한다면
// target의 내용을 byte 배열로 변환
byte[] data = FileUtils.readFileToByteArray(target);
// byte 배열인 data를 이용하여 ByteArrayResource의 인스턴스 생성
ByteArrayResource resource = new ByteArrayResource(data);
// HTTP Response Header에 내용의 인코딩 방식, 길이, 배치 방식, resource의 형식 정보를 반환
// HTTP Response Body에 ByteArrayResource를 포함하는 ResponseEntity 반환
return ResponseEntity.ok()
.header("Content-Encoding", "UTF-8")
.header("Content-Length", String.valueOf(data.length))
.header("Content-Disposition", "attachment; filename=" + memberId)
.header("Content-Type", "application/octet-stream")
.body(resource);
}
else { // 해당 디렉토리에 해당 파일이 존재하지 않는다면
// 1) 사용자 정의 예외를 발생시키는 방법
// throw new TargetNotFoundException("프로필 없음");
// 2) 404 error를 전송하는 방법
return ResponseEntity.notFound().build();
}
}
}
ResponseEntity<T> 클래스
- T : the body type
- T는 ByteArrayResource로 한다 (필수)
FileUtils 클래스
static byte[] | readFileToByteArray(File file) | File을 byte의 배열로 변환 |
ByteArrayResource 클래스
- 주어진 byte 배열로 Resource를 구현(implement)한다
3. 마이페이지에서 회원가입시 업로드한 이미지가 표시되도록 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="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<jsp:include page = "/WEB-INF/views/template/header.jsp">
<jsp:param name = "title" value = "마이 페이지"/>
</jsp:include>
<h1>마이 페이지</h1>
<%-- 마이페이지에서 회원가입시 업로드한 이미지가 표시되도록 --%>
<img src = "download?memberId=${memberDto.memberId}" width = "100" height = "100">
<h3>아이디 : "${memberDto.getMemberId()}"</h3>
<h3>닉네임 : ${memberDto.getMemberNick()}</h3>
<h3>생년월일 : ${memberDto.getMemberBirth()}</h3>
<h3>연락처 : ${memberDto.getMemberTel()}</h3>
<h3>이메일 : ${memberDto.getMemberEmail()}</h3>
<h3>우편주소 : ${memberDto.getMemberPost()}</h3>
<h3>기본주소 : ${memberDto.getMemberBaseAddress()}</h3>
<h3>상세주소 : ${memberDto.getMemberDetailAddress()}</h3>
<h3>포인트 : ${memberDto.getMemberPoint()}</h3>
<h3>등급 : ${memberDto.getMemberGrade()}</h3>
<h3>가입일 : <fmt:formatDate value="${memberDto.memberJoin}" pattern="y년 M월 d일 E a h시 m분 s초"/></h3>
<h3>최종 로그인 : <fmt:formatDate value="${memberDto.memberLogin}" pattern="y년 M월 d일 E a h시 m분 s초"/></h3>
<br>
<c:choose>
<c:when test="${mg == '관리자'}">
<!-- 관리자용 메뉴 -->
<h2><a href = "member/list">목록 보기</a></h2>
<h3><a href = "edit?memberId=${memberDto.getMemberId()}">회원 정보 변경</a></h3>
</c:when>
<c:otherwise>
<!-- 회원용 메뉴 -->
<h3><a href = "/member/information">개인 정보 변경</a></h3>
<h3><a href = "/member/password">비밀번호 변경</a></h3>
<h3><a href = "/member/goodbye">회원 탈퇴</a></h3>
</c:otherwise>
</c:choose>
<jsp:include page = "/WEB-INF/views/template/footer.jsp"></jsp:include>
파일 업로드 (DB를 이용하여)
- DB를 이용하면 파일 업로드시 업로드한 파일의 정보를 DB에 기록하여 그 값을 조회를 통해 불러올 수 있다
- 파일 업로드 기록에 대한 CRUD가 필요하다
테이블 생성
첨부파일(attachment) - 첨부파일 번호(attachment_no) : 사용자가 저장한 파일의 파일명을 시퀀스로 대체, 숫자, 기본키 - 첨부파일 이름(attachment_name) : 사용자가 업로드한 파일명, 문자(256 byte), 필수 입력 - 첨부파일 유형(attachment_type) : 업로드한 파일의 유형, 문자(30 byte), 필수 입력 - 첨부파일 크기(attachment_size) : 업로드한 파일의 크기, 숫자, 필수 입력, 0 이상의 값이 들어가도록 - 첨부파일 업로드 시간(attachment_time) : 업로드한 시간, 날짜, 기본값을 sysdate로, 필수 입력 |
-- 테이블 생성
create table attachment(
attachment_no number primary key,
attachment_name varchar2(256) not null,
attachment_type varchar2(30) not null,
attachment_size number not null check(attachment_size >= 0),
attachment_time date default sysdate not null
);
-- 시퀀스 생성
create sequence attachment_seq;
AttachmentDto
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class AttachmentDto {
// 필드
int attachmentNo;
String attachmentName;
String attachmentType;
long attachmentSize;
Date attachmentTime;
}
1. 파일 업로드 기록 등록 및 조회
AttachmentDao
public interface AttachmentDao {
// 추상 메소드 - 첨부파일 번호를 위한 다음 시퀀스 번호 반환
int sequence();
// 추상 메소드 - 첨부파일 업로드 기록 등록
void insert(AttachmentDto attachmentDto);
// 추상 메소드 - 첨부파일 업로드 기록 전체조회
List<AttachmentDto> selectList();
}
AttachmentDaoImpl
@Repository
public class AttachmentDaoImpl implements AttachmentDao {
// 의존성 주입
@Autowired
private JdbcTemplate jdbcTemplate;
// AttachmentDto에 대한 RowMapper
private RowMapper<AttachmentDto> mapper = new RowMapper<>() {
@Override
public AttachmentDto mapRow(ResultSet rs, int rowNum) throws SQLException {
return AttachmentDto.builder()
.attachmentNo(rs.getInt("attachment_no"))
.attachmentName(rs.getString("attachment_name"))
.attachmentType(rs.getString("attachment_type"))
.attachmentSize(rs.getLong("attachment_size"))
.attachmentTime(rs.getDate("attachment_time"))
.build();
}
};
// AttachmentDto에 대한 ResultSetExtractor
private ResultSetExtractor<AttachmentDto> extractor = new ResultSetExtractor<>() {
@Override
public AttachmentDto extractData(ResultSet rs) throws SQLException, DataAccessException {
if(rs.next()) {
return AttachmentDto.builder()
.attachmentNo(rs.getInt("attachment_no"))
.attachmentName(rs.getString("attachment_name"))
.attachmentType(rs.getString("attachment_type"))
.attachmentSize(rs.getLong("attachment_size"))
.attachmentTime(rs.getDate("attachment_time"))
.build();
}
else {
return null;
}
}
};
// 추상 메소드 오버라이딩 - 첨부파일 번호를 위한 다음 시퀀스 번호 반환
@Override
public int sequence() {
String sql = "select attachment_seq.nextval from dual";
return jdbcTemplate.queryForObject(sql, int.class);
}
// 추상 메소드 오버라이딩 - 첨부파일 업로드 기록 등록
@Override
public void insert(AttachmentDto attachmentDto) {
String sql = "insert into attachment("
+ "attachment_no, "
+ "attachment_name, "
+ "attachment_type, "
+ "attachment_size) "
+ "values(?, ?, ?, ?)";
Object[] param = new Object[] {attachmentDto.getAttachmentNo(),
attachmentDto.getAttachmentName(),
attachmentDto.getAttachmentType(),
attachmentDto.getAttachmentSize()
};
jdbcTemplate.update(sql, param);
}
// 추상 메소드 - 첨부파일 업로드 기록 전체조회
@Override
public List<AttachmentDto> selectList() {
String sql = "select * from attachment";
return jdbcTemplate.query(sql, mapper);
}
}
AttachmentController
@Controller
public class FileController {
// 의존성 주입
@Autowired
private AttachmentDao attachmentDao;
// 파일 업로드 Mapping
// 파일 업로드 Mapping에 DTO 전달 및 DB 처리 후 파일 저장
@PostMapping("/upload")
public String upload(@RequestParam MultipartFile attachment) throws IllegalStateException, IOException {
// 첨부파일 업로드 기록 등록
// 파일 업로드 기록 등록을 위해 다음 시퀀스 번호 반환
int attachmentNo = attachmentDao.sequence();
// AttachmentDto의 인스턴스 생성
// - 첨부파일 번호(attachmentNo)는 반환한 다음 시퀀스 번호
// - 첨부파일 이름(attachmentName)은 전달받은 MultipartFile의 원본 파일 이름
// - 첨부파일 종류(attachmentType)는 전달받은 MultipartFile의 파일 종류
// - 첨부파일 크기(attachmentSize)는 전달받은 MultipartFile의 파일 크기
// 생성한 AttachmentDto의 인스턴스로 첨부파일 업로드 기록 등록(INSERT)
attachmentDao.insert(AttachmentDto.builder()
.attachmentNo(attachmentNo)
.attachmentName(attachment.getOriginalFilename())
.attachmentType(attachment.getContentType())
.attachmentSize(attachment.getSize())
.build());
// 실제 첨부파일을 특정 경로에 저장
// 추상경로에 대한 File의 인스턴스 생성
File dir = new File("C:\\\\Users\\\\hyeul\\\\upload");
// 해당 추상경로의 디렉토리 생성 (폴더가 없으면 자동으로 생성)
dir.mkdirs();
// 해당 디렉토리에 해당 파일의 시퀀스 번호를 파일명으로 하여 파일을 저장하도록 하는 File의 인스턴스 생성
File target = new File(dir, String.valueOf(attachmentNo));
// View에서 전달받은 MultipartFile을 해당 디렉토리로 보내서 시퀀스 번호를 파일명으로 바꿔서 저장
attachment.transferTo(target);
return "redirect:/";
}
// 파일 업로드 기록 전체조회 Mapping
@GetMapping("/list")
public String list(Model model) {
// model에 전체조회 결과를 첨부
model.addAttribute("list", attachmentDao.selectList());
return "list";
}
}
root.jsp (파일 업로드)
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<h1>파일 업로드 테스트</h1>
<h1>DB 사용하는 업로드</h1>
<form action="/upload" method="post" enctype="multipart/form-data">
<input type="file" name="attachment">
<button type="submit">등록</button>
</form>
<h1><a href="list">파일목록 보기</a></h1>
list.jsp (파일 목록)
<c:forEach var = "attachmentDto" items = "${list}">
<h1>파일 목록</h1>
<h3>
[${attachmentDto.attachmentType}]
${attachmentDto.attachmentName}
(${attachmentDto.attachmentSize} bytes)
</h3>
</c:forEach>
파일 다운로드 (DB를 이용하여)
AttachmentDao
public interface AttachmentDao {
// 추상 메소드 - 첨부파일 업로드 기록 단일조회
AttachmentDto selectOne(int attachmentNo);
}
AttachmentDaoImpl
@Repository
public class AttachmentDaoImpl implements AttachmentDao {
// 의존성 주입
@Autowired
private JdbcTemplate jdbcTemplate;
// AttachmentDto에 대한 ResultSetExtractor
private ResultSetExtractor<AttachmentDto> extractor = new ResultSetExtractor<>() {
@Override
public AttachmentDto extractData(ResultSet rs) throws SQLException, DataAccessException {
if(rs.next()) {
return AttachmentDto.builder()
.attachmentNo(rs.getInt("attachment_no"))
.attachmentName(rs.getString("attachment_name"))
.attachmentType(rs.getString("attachment_type"))
.attachmentSize(rs.getLong("attachment_size"))
.attachmentTime(rs.getDate("attachment_time"))
.build();
}
else {
return null;
}
}
};
// 추상 메소드 - 첨부파일 업로드 기록 단일조회
@Override
public AttachmentDto selectOne(int attachmentNo) {
String sql = "select * from attachment where attachment_no = ?";
Object[] param = new Object[] {attachmentNo};
return jdbcTemplate.query(sql, extractor, param);
}
}
FileController
- View에서 입력받은 첨부파일 번호(attachmentNo)로 단일 조회 실행
- 해당 번호의 첨부파일이 존재한다면 File 클래스의 인스턴스 생성를 생성하여 byte 배열로 만든 후
ByteArrayResource 형태로 HTTP Response Body에 첨부하여 해당 경로로 파일 다운로드 구현
@Controller
public class FileController {
// 의존성 주입
@Autowired
private AttachmentDao attachmentDao;
// 파일 다운로드 Mapping
@GetMapping("/download")
public ResponseEntity<ByteArrayResource> download(@RequestParam int attachmentNo) throws IOException{
// 입력받은 첨부파일 번호(attachmentNo)로 단일 조회 실행
AttachmentDto attachmentDto = attachmentDao.selectOne(attachmentNo);
// 해당 첨부파일 번호(attachmentNo)의 첨부파일이 존재하지 않을 경우
if(attachmentDto == null) {
return ResponseEntity.notFound().build(); // 404 error
}
// 해당 첨부파일 번호(attachmentNo)의 첨부파일이 존재할 경우
// 해당 문자열을 추상 경로로 변환하여 File 클래스의 인스턴스 생성
File directory = new File("C:\\\\Users\\\\hyeul\\\\upload");
// directory의 추상 경로를 상위 경로, 해당 파일의 시퀀스 번호를 하위 경로로 하는 File의 인스턴스 생성
File target = new File(directory, String.valueOf(attachmentNo));
// target의 내용을 byte 배열로 변환
byte[] data = FileUtils.readFileToByteArray(target);
// byte 배열인 data를 이용하여 ByteArrayResource의 인스턴스 생성
ByteArrayResource resource = new ByteArrayResource(data);
// HTTP Response Header에 내용의 인코딩 방식, 길이, 배치 방식, resource의 형식 정보를 반환
// HTTP Response Body에 ByteArrayResource를 포함하는 ResponseEntity 반환
return ResponseEntity.ok()
.header("Content-Encoding", "UTF-8")
.header("Content-Length", String.valueOf(attachmentDto.getAttachmentSize()))
.header("Content-Disposition", "attachment; filename=" + attachmentDto.getAttachmentName())
.header("Content-Type", attachmentDto.getAttachmentType())
.body(resource);
}
** ReponseEntity를 반환하는 방법
i) 상수를 이용하지 않고 ResponseEntity 반환
// 상수를 이용하지 않고 ResponseEntity 반환
return ResponseEntity.ok()
.header("Content-Encoding", "UTF-8")
.header("Content-Length", String.valueOf(attachmentDto.getAttachmentSize()))
.header("Content-Disposition", "attachment; filename=" + attachmentDto.getAttachmentName())
.header("Content-Type", attachmentDto.getAttachmentType())
.body(resource);
ii) 상수를 이용하여 ReponseEntity 반환
// 상수를 이용하여 ResponseEntity 반환
return ResponseEntity.ok()
.header(HttpHeaders.CONTENT_ENCODING, StandardCharsets.UTF_8.name())
.contentLength(attachmentDto.getAttachmentSize())
.contentType(MediaType.APPLICATION_OCTET_STREAM)
.header(HttpHeaders.CONTENT_DISPOSITION,
ContentDisposition.attachment().filename(attachmentDto.getAttachmentName(),
StandardCharsets.UTF_8)
.build().toString())
.body(resource);
i) 방식과 ii) 방식의 비교
- Content-Encoding에 대하여 두 방식의 차이는 없다
- Content-Length에 대하여 String으로 값을 변환해야 하는 i) 방식은 번거로우므로 ii) 방식이 좋다
- Content-Type에 대하여 보다 일반적인 경우를 생각하면 i) 방식이 좋다 ( ii) 방식은 아예 Content-Type을 한정한다)
- Content-Disposition에 대하여 파일명이 한글이거나 띄어쓰기가 포함된 경우를 위해 ii) 방식이 좋다
list.jsp
- 업로드된 파일의 미리보기 표시
- 업로드된 파일을 다운로드할 수 있도록 하는 하이퍼링크 추가 (하이퍼링크를 통해 첨부파일 번호를 전달)
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib prefix = "c" uri="http://java.sun.com/jsp/jstl/core" %>
<c:forEach var = "attachmentDto" items = "${list}">
<h3>
<%-- 미리보기 표시 --%>
<img src = "download?attachmentNo=${attachmentDto.attachmentNo}" width = "50" height = "50">
[${attachmentDto.attachmentType}]
<%-- 업로드된 파일을 다운로드할 수 있도록 하는 하이퍼링크 추가 --%>
<a href = "download?attachmentNo=${attachmentDto.attachmentNo}">${attachmentDto.attachmentName} </a>
(${attachmentDto.attachmentSize})
</h3>
</c:forEach>
'국비교육 > 국비교육' 카테고리의 다른 글
day45 - 0928 (0) | 2022.09.28 |
---|---|
day44 - 0927 (0) | 2022.09.27 |
day42 - 0923 (0) | 2022.09.24 |
day41 - 0922 (0) | 2022.09.22 |
day40 - 0921 (0) | 2022.09.22 |