230222 Spring Boot 이미지파일 불러오기
파일처리
1.데이터베이스에 img를 통째로 넣기
-용량을 많이 차지한다. 이미지가 별로 없을 때 쓰는 것.
2.하드디스크에 저장
-프로젝트에 저장하는게 아님. 서버의 하드디스크에
3.클라우드에 저장
-코드로 작성.
이미지파일 불러오기.
src를 통해 이미지를 보여주는 건 서버에 요청을 하는 것!!!
src에 컨트롤러를 통해 요청을 보내도록.
컨트롤러에선 rest api를 사용.
이미지에 대한 정보는 이진데이터를 구해서 던진다. 기능은 스프링이 제공
이진데이터 보내기. FileCopyUtils.copyToByteArray(파일)<-일반 자바에는 없고 스프링에 있다.
요청은 src에서 보내고, 컨트롤러는 그걸 찾아서 return에 이진데이터를 실어 보냄.
->파일은 이진데이터로 이루어져 있다. 그런데 java의 file객체는 이진데이터가 아님. System.out.printLn()을 하면 파일의 총 경로가 나온다. 그래서 이진데이터로 바꿔 보내주는 과정을 거치는 것.
서버측에서 보내주는 데이터타입-이진데이터.
부메랑에서 경로요청을 하고 헤더를 보면
Content-Type application/octet-stream
로 나온다.
application/octet-stream은
이미지나 동영상을 이진수로 표현하는 가장 기본적인 타입.
1.타입 명시
컨트롤러에서 @GetMapping의 속성에 produces="image/해당이미지확장자"를 입력하면
부메랑 해더의 Content-Type이 image/해당이미지확장자가 되고
경로를 브라우저에서 띄우면 이진데이터가 아닌 이미지가 출력된다.
-이미지 종류를 명시하는게 어렵다는 단점.
2.ResponseEntity 사용
new ResponseEntity<>(반환 데이터, 헤더, 상태코드). 반환은 ResponseEntity. 매개변수 담는 방법은 여러개가 있다.
제네릭은 데이터타입을 따라간다.
헤더의 contentType을 지정할 수가 있다! 해당파일이 가지고 있는 contentType을 가져와서 보낼 수 있음.
Files.probeContentType()을 쓰면 파일의 contentType을 가져올 수 있음. 파일마다 바뀌는 이미지 종류 명시가 쉬워진다.
생성한 file변수의 file.toPath()를 사용하여 받는 파일의 path를 구함.
모달창 이미지 불러오기
모달키는 법
1.modalOn클래스가 들어가면 모달창오픈.
2.modalOn()함수사용.
ajax
get방식이 맞는데 연습용으로 post방식으로
json파일을 보내면 컨트롤러에서 받을 때는 @RequestBody를 사용.
post방식은 vo나 map으로 받는다!
ResponseEntity는 방식을 지정한다. 즉 List안에 ResponseEntity가 아니라 ResponseEntity안에 List.
어차피 JSON이다. 헤더 필요 없음.
AjaxController.java
//이미지정보를 처리
//1.?키=값 쿼리스트릥
//2.@PathVarialble
//화면에는 2진데이터 타입이 반환됩니다.
// @GetMapping("/display/{filepath}/{uuid}/{filename}")
// public byte[] display(@PathVariable("filepath")String filepath,
// @PathVariable("uuid")String uuid,
// @PathVariable("filename")String filename) {
//
// //파일이 저장된 경로
// String savename = uploadpath+"\\"+filepath+"\\"+uuid+"_"+filename;
// File file = new File(savename);
//
// //저장된 이미지파일의 이진데이터 형식을 구함
// byte[] result=null;
//
// try {
// result = FileCopyUtils.copyToByteArray(file);
// } catch (IOException e) {
// e.printStackTrace();
// }
//
// return result;
// }
//prod_id값을 받아서 이미지정보를 반환
@PostMapping("/getProductImg")
public ResponseEntity<List<ProductUploadVO>> getProductImg(@RequestBody ProductVO vo) {//vo, map
return new ResponseEntity<>(productService.getProductImg(vo),HttpStatus.OK);
}
@GetMapping("/display/{filepath}/{uuid}/{filename}")
public ResponseEntity<byte[]> display(@PathVariable("filepath")String filepath,
@PathVariable("uuid")String uuid,
@PathVariable("filename")String filename) {
//파일이 저장된 경로
String savename = uploadpath+"\\"+filepath+"\\"+uuid+"_"+filename;
File file = new File(savename);
//저장된 이미지파일의 이진데이터 형식을 구함
byte[] result=null;//1. data
ResponseEntity<byte[]> entity=null;
try {
result = FileCopyUtils.copyToByteArray(file);
//2. header
HttpHeaders header = new HttpHeaders();
header.add("Content-type",Files.probeContentType(file.toPath())); //파일의 컨텐츠타입을 직접 구해서 header에 저장
//3. 응답본문
entity = new ResponseEntity<>(result,header,HttpStatus.OK);//데이터, 헤더, 상태값
} catch (IOException e) {
e.printStackTrace();
}
return entity;
}
-getProductImg 메서드는 ResponseEntity로 보내지 않아도 되지만, 추후 header 등에 수정할 부분이 생기거나 했을 때 편하게 하기 위해 ResponseEntity로 보냄.
-FileCopyUtils.copyToByteArray(file)로 이진데이터를 보낸다.
-header.add()에서 Files.probeContentType(file.toPath())는 파일의 contentType을 구한다. file.toPath()는 파일의 path를 가져온다. =>파일의 컨텐츠타입을 직접 구해서 header에 저장한다.
-헤더에서 contentType이 지정되었다. 때문에 ResponseEntity를 통해 이진데이터로 만들어진 result가 지정한 컨텐츠타입으로 변해서 나온다.
ProductService.java, ProductMapper.java
//이미지데이터조회
public List<ProductUploadVO> getProductImg(ProductVO vo);
글을 등록할 때 최대 3개의 이미지를 등록할 수 있다. 때문에 반환이 List인 것.
ProductServiceImpl.java
@Override
public List<ProductUploadVO> getProductImg(ProductVO vo) {
return productMapper.getProductImg(vo);
}
ProductMapper.xml
<select id="getProductImg" resultType="ProductUploadVO">
select * from PRODUCT_UPLOAD where prod_id=#{prod_id}
</select>
productList.html에서
...
<a href="#" class="normal_btn modalOn">이미지수정</a></td>
...
<div class="filebox preview-image">
...
<script src="/js/modal.js"></script>
<!-- 1.modalOn클래스가 들어가면 모달창오픈, 2.modalOn()으로 켤 수 있음 -->
<!-- 모달창 제어 -->
<script>
//이미지 수정버튼을 클릭했을 때. modalOn()
$(".modalOn").click((e)=>{
e.preventDefault(); //a링크의 고유이벤트 중지
//1.클릭한 대상의 prod_id값
var prod_id=$(e.target).closest("td").next().html();
//2.post방식으로 img데이터 조회
$.ajax({
url:"../getProductImg",
type:"post",
data:JSON.stringify({prod_id:prod_id}),//데이터
contentType:"application/json",//보내는데이터타입
success:(result)=>{
console.log(result); //반환된 데이터
var str = "";
var arr=['a','b','c'];
for(var i=0;i<result.length;i++){
str+='<div class="left">';
str+='<span>추가이미지</span>';
str+='<label class="upload-display" for="'+arr[i]+'_file">';
str+='<span class="upload-thumb-wrap">';
str+='<img class="upload-thumb" src="'+'../display/'+result[i].filepath+'/'+result[i].uuid+'/'+result[i].filename+'">';
str+='</span>';
str+='</label>';
str+='<input class="upload-name" value="파일선택" disabled="disabled">';
str+='<input type="file" name="file" id="'+arr[i]+'_file" class="upload-hidden">';
str+='<input type="hidden" value="">';
str+='<input type="hidden" value="">';
str+='<button type="button" class="normal_btn" style="display: block;">삭제</button>';
str+='<button type="button" class="normal_btn" style="display: block;" onclick="location.href='+"'"+'../download/'+result[i].filepath+'/'+result[i].uuid+'/'+result[i].filename+"'"+'">다운로드</button>';
str+='</div>';
}
$(".filebox").html(str);
},
error:(err)=>{alert("이미지 조회에 실패했습니다.");}
});
//modalOn();
});
</script>
※모달을 다시 키지 않아도 되는 상태로 진행하였다.
비동기 방식의 url은 getProductImg로 연결되어 sql에서 뽑아온 이미지 정보들이 들어있는 List가 ResponseEntity로 반환됨. 이걸 success의 result에서 받는다.
<img>태그의 href는 get방식으로 다시 컨트롤러와 연결된다. 컨트롤러의 display메서드를 통해 contentType이 각 이미지들에 맞게 지정된 entity를 받고, 화면에서 맞게 그려지는 것!
+)추가로 해보기
상품1:이미지N
상세페이지에선 조인으로 이미지 가져오기..?근데 지금 prod_id가 있음.
ajax방식 아님. form형식.