[ 상품 이미지 업로드 ]
○ 순서
- [뷰] 사용자가 이미지를 선택
- [뷰] 선택된 이미지 서버로 전송
- [서버] 전송 받은 이미지 저장 후, 저장한 이미지 정보를 [뷰]로 재전송
- [뷰] 전송 받은 데이터 활용해 이미지 미리보기, <input> 태그에 데이터 저장
- [뷰] '등록 버튼' 클릭
- [서버] 이미지 정보 DB에 저장
[ 이미지 파일 업로드 기본 설정 ]
○ 라이브러리 추가
- javax.servlet 3.0.0 혹은 3.1.0 추가 dependency 추가
○ web.xml 설정
- namespace 3.1 코드로 변경
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1">
- multipart-config 설정 코드 <servlet> 태그 내부에 삽입
<!-- 업로드 관련 설정 -->
<multipart-config>
<location>C:\\upload\\temp</location>
<max-file-size>20971520</max-file-size> <!-- 1MB * 20 -->
<max-request-size>41943040</max-request-size> <!-- 40MB -->
<file-size-threshold>20971520</file-size-threshold> <!-- 20MB -->
</multipart-config>
- enctype : 웹 클라이언트와 웹 서버 간의 데티어 주고 받을 때 해당 데이터의 인코딩 되는 방식
- <location> : 업로드한 파일 임시로 저장되는 경로
- <max-file-size> : 업로드 가능한 최대 파일 그키
- <max-request-size> : 업로드 되는 파일의 최대 크기
- <file-size-threshold> : 임시 파일로 저장 여부를 결정할 데이터 크기
○ servlet-context.xml 설정
- 스프링에서는 multipart 데이터 처리 불가능하므로 multipartResolver 등록
<!-- 업로드 관련 빈 -->
<beans:bean id="multipartResolver" class="org.springframework.web.multipart.support.StandardServletMultipartResolver"></beans:bean>
[ productEnrollForm.jsp ]
○ 업로드 UI 추가
- 'file' type <input> 태그 추가
<div class="form_section">
<div class="form_section_title">
<label>상품 이미지</label>
</div>
<div class="form_section_content">
<input type="file" id ="fileItem" name='uploadFile' style="height: 30px;">
</div>
</div>
○ 이미지 업로드
- fileList 객체가 맞는지 확인하기 위해 변수 선언 및 fileList로 초기화
- formData : FromData.append(key, value) 메서드 사용
- url : 서버로 요청을 보낼 url
- processData : 서버로 전송할 데이터를 queryStirng 형태로 변환할지 여부
- contentType : 서버로 전송되는 데이터의 content-type
- data : 서버로 전송할 데이터
- type : 서보 요청 타입(GET, POST)
- dataType : 서버로부터 반환받을 데이터 타입
/* 이미지 업로드 */
$("input[type='file']").on("change", function(e){
let formData = new FormData();
let fileInput = $('input[name="uploadFile"]');
let fileList = fileInput[0].files;
let fileObj = fileList[0];
if(!fileCheck(fileObj.name, fileObj.size)){
return false;
}
formData.append("uploadFile", fileObj);
$.ajax({
url: '/product/uploadAjaxAction',
processData : false,
contentType : false,
data : formData,
type : 'POST',
dataType : 'json',
success : function(result){
console.log(result);
},
error : function(result) {
alert("이미지 파일이 아닙니다.");
}
});
// multiple 설정
//for(let i = 0; i < fileList.length; i++){
// formData.append("uploadFile", fileList[i]);
//}
// alert("통과");
});
○ fileCheck
- fileCheck 메서드에 fileName, fileSize 파라미터 부여
- 두 조건 불만족 시, 경고문구, false
- 두 조건 만족 시, 경고문구, true
/* var, method related with attachFile */
let regex = new RegExp("(.*?)\.(jpg|png)$");
let maxSize = 1048576; //1MB
function fileCheck(fileName, fileSize){
if(fileSize >= maxSize){
alert("파일 사이즈 초과");
return false;
}
if(!regex.test(fileName)){
alert("해당 종류의 파일은 업로드할 수 없습니다.");
return false;
}
return true;
}
[ ProductController.java ]
○ c 경로에 upload 폴덩 생성
- 설정한 용량 초과하는 파일 c/upload/temp에 저장
○ 첨부파일 업로드
- MultipartFile 타입의 uploadFile 변수 매개변수 부여
- 오늘 날짜 'yyyy/MM/dd' 형식의 String 데이터 얻기 위해 SimpleDateForamt, Date 클래스 사용
- SimpleDateForamt : Date 클래스 통해 얻은 오늘의 날짜 지정된 형식의 문자열 데이터 생성 위해 사용
○ 썸네일 이미지 구성
"s_" + "uuid_" + "원본파일 이름. 이미지 타입" |
○ ImageIO
- ImageIO : 이미지 읽어오거나 생성하는 메서드
- BufferedImage : 이미지 데이터 처리하거나 조작에 필요한 값과 메서드 제공
- Graphics2D : 그림을 그리는 데 필요로 하는 설정값과 메서드 제공
/* 첨부 파일 업로드 */
@PostMapping(value="/uploadAjaxAction", produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
// 업로드 수행하는 url 매핑 메서드의 리턴 타입
public ResponseEntity<List<AttachImageVO>> uploadAjaxActionPOST(MultipartFile[] uploadFile) {
logger.info("uploadAjaxActionPOST..........");
/* 이미지 파일 체크 */
for(MultipartFile multipartFile: uploadFile) {
File checkfile = new File(multipartFile.getOriginalFilename());
String type = null;
try {
type = Files.probeContentType(checkfile.toPath());
logger.info("MIME TYPE : " + type);
} catch (IOException e) {
e.printStackTrace();
}
if(!type.startsWith("image")) {
List<AttachImageVO> list = null;
return new ResponseEntity<>(list, HttpStatus.BAD_REQUEST);
}
}// for
String uploadFolder = "C:\\upload";
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
// 오늘의 날짜 데이터를 얻기 위해 Date 클래스 타입 변수 선언 및 초기화
Date date = new Date();
// date 변수를 'yyyy-MM-dd' 형식의 문자열로 변환
String str = sdf.format(date);
// '-'를 경로를 replace 사용해 구분자 '\'로 변경
String datePath = str.replace("-", File.separator);
/* 폴더 생성 */
// 개체화 해주는 코드에 인자 부여
// 부모 경로 : uploadFolder
// 자식 경로 : datePath
File uploadPath = new File(uploadFolder, datePath);
// File 클래스에서 대상 파일 혹은 디렉터리가 존재하는지 유무 반환
if(uploadPath.exists() == false) {
// mkdirs : 여러 개의 폴더 생성을 위한 메서드
uploadPath.mkdirs();
}
/* 이미저 정보 담는 객체 */
List<AttachImageVO> list = new ArrayList();
// 향상된 for
for(MultipartFile multipartFile : uploadFile) {
/* 이미지 정보 객체 */
AttachImageVO vo = new AttachImageVO();
/* 파일 이름 */
// getOriginalFilename() : 뷰로부터 전달받은 파일 이름 그대로 사용 위한 메서드
String uploadFileName = multipartFile.getOriginalFilename();
vo.setFileName(uploadFileName);
vo.setUploadPath(datePath);
/* uuid 적용 파일 이름 */
String uuid = UUID.randomUUID().toString();
vo.setUuid(uuid);
uploadFileName = uuid + "_" + uploadFileName;
/* 파일 위치, 파일 이름을 합친 File 객체 */
// 파일 경로와 파일 이름 포함하는 File 객체로 초기화
File saveFile = new File(uploadPath, uploadFileName);
/* 파일 저장 */
// transferTo : 파일을 저장하는 메서드
try {
multipartFile.transferTo(saveFile);
File thumbnailFile = new File(uploadPath, "s_" + uploadFileName);
// ImageIO의 read() 메서드 호출해 BufferedImage 타입으로 변경
// BufferedImage 타입의 참조 변수 선언해 해당 변수에 대입
BufferedImage bo_image = ImageIO.read(saveFile);
/* 비율 */
double ratio = 3;
/*넓이 높이*/
int width = (int) (bo_image.getWidth() / ratio);
int height = (int) (bo_image.getHeight() / ratio);
// 썸네일 이미지 설정(넓이, 높이, 생성될 이미지 타입)
BufferedImage bt_image = new BufferedImage(width, height, BufferedImage.TYPE_3BYTE_BGR);
// bt_image에서 createGraphics() 메서드 호출해 Graphic2D 객체 생성
// Graphic2D 타입 참조 변수에 대입
Graphics2D graphic = bt_image.createGraphics();
// drawImage 메서드 호출해 원본 이미지(원본 BufferedImage)를
// 썸네일 BufferdImage에 지정한 크기로 변경해 왼쪽 상단 "0,0" 좌표로부터 그려줌
graphic.drawImage(bo_image, 0, 0, width, height, null);
// 제작한 썸네일 이미지(bt_image)를 파일로 만들기
// ImageIO의 write 메서드 호출해 파일로 저장
// (파일로 저장할 이미지(bt_image), 어떠한 이미지 형식으로 저장할지 String 타입, 이미지 저장 경로와 이름 생성한 File 객체(thumbnailFile))
ImageIO.write(bt_image, "jpg", thumbnailFile);
} catch (Exception e) {
e.printStackTrace();
}
// AttachImageVO 객체 LIst 요소로 추가
list.add(vo);
//logger.info("-----------------------------------------------");
//logger.info("파일 이름 : " + multipartFile.getOriginalFilename());
//logger.info("파일 타입 : " + multipartFile.getContentType());
//logger.info("파일 크기 : " + multipartFile.getSize());
}
ResponseEntity<List<AttachImageVO>> result = new ResponseEntity<List<AttachImageVO>>(list, HttpStatus.OK);
return result;
}
[ pom.xml ]
<!-- thumbnail -->
<!-- https://mvnrepository.com/artifact/net.coobird/thumbnailator -->
<dependency>
<groupId>net.coobird</groupId>
<artifactId>thumbnailator</artifactId>
<version>0.4.13</version>
</dependency>
○ uuid 사용
'유동적 경로', 'uuid', '원본 파일 이름' 원본 파일 : c:\upload + '유동적 경로' + 'uuid' + '_' + '원본 파일 이름' 썸네일 이미지 : c:\upload + '유동적 경로' + 's_' + 'uuid' + '_' + '원본 파일 이름' |
[ AttachImageVO.java ]
@Data
public class AttachImageVO {
/* 경로 */
private String uploadPath;
/* uuid */
private String uuid;
/* 파일 이름 */
private String fileName;
/* 상품 id */
private int bookId;
}
[ pom.xml ]
○ jackson-databind : java 객체 데이터를 JSON 타입 테이터 변환해 뷰로 전송
<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.12.1</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.dataformat/jackson-dataformat-xml -->
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
<version>2.12.1</version>
</dependency>
[ ProductController.java ]
@GetMapping("/display")
public ResponseEntity<byte[]> getImage(String fileName){
logger.info("getImage()............+ + fileName);
// '\\' : 특수문자 '\' 인식할 수 있도록 이스케이프 문자 사용
File file = new File("c:\\upload\\" + fileName);
// 뷰로 반환할 ResponseEntity 객체 주소를 저장할 참조 변수 선언, null로 초기화
ResponseEntity<byte[]> result = null;
try {
// Response의 header 관련 설정 객체 추가 위해 HttpHeaders 인스턴스화한 후 참조 변수 선언해 대입
HttpHeaders header = new HttpHeaders();
// File 클래스의 proveContentType()
// header의 'Content Type' 속성 값에 이미지 파일 MIME TYPE 추가 위해 HttpHeader 클래스의 add() 메서드 사용
// add(Response header의 속성명, 속성명에 부여할 값)
// header의 'Content Type'에 대상 파일의 MIME TYPE 부여해주는 코드 추가
header.add("Content-type", Files.probeContentType(file.toPath()));
// FileCopyUtils : 파일과 stream 복사에 사용할 수 있는 메서드 제공하는 클래스
// copyToByteArray() : 파라미터로 부여하는 File 객체 즉, 대상 파일 복사해 Byte 배열로 반환해주는 클래스
result = new ResponseEntity<>(FileCopyUtils.copyToByteArray(file), header, HttpStatus.OK);
}catch (IOException e) {
e.printStackTrace();
}
return result;
}
○ ResponseEntity(body에 첨부할 데이터, header 설정 부여된 객체, 전송하고자 하는 상태 코드)
ResponseEntity(T body, MultiValueMap<String,String> headers, HttpStatus status) |
[ productEnrollForm.java ]
<div class="form_section">
<div class="form_section_title">
<label>상품 이미지</label>
</div>
<div class="form_section_content">
<input type="file" id ="fileItem" name='uploadFile' style="height: 30px;">
<div id="result_card">
<div class="imgDeleteBtn">x</div>
</div>
</div>
</div>
[ productEnroll.css ]
<style type="text/css">
#result_card img{
max-width: 100%;
height: auto;
display: block;
padding: 5px;
margin-top: 10px;
margin: auto;
}
#result_card {
position: relative;
}
.imgDeleteBtn{
position: absolute;
top: 0;
right: 5%;
background-color: #ef7d7d;
color: wheat;
font-weight: 900;
width: 30px;
height: 30px;
border-radius: 50%;
line-height: 26px;
text-align: center;
border: none;
display: block;
cursor: pointer;
}
</style>
'프로그래밍 언어 > JSP' 카테고리의 다른 글
JSP_22-12-08_[ 쇼핑몰 장바구니 ]_소스 코드 정리 (0) | 2022.12.08 |
---|---|
JSP_22-12-06_[ 쇼핑몰 만들기_장바구니 ] (0) | 2022.12.06 |
JSP_22-11-22_상품 등록[2] (0) | 2022.11.24 |
JSP_22-11_24 (0) | 2022.11.24 |