Spring Boot

230222 Spring Boot 이미지파일 불러오기

주영재 2023. 2. 22. 20:22

파일처리
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를 받고, 화면에서 맞게 그려지는 것!

 

for문을 통해 innerHtml을 그린다(jquery방식으로). 등록할 때 이미지가 하나였으면 sql에서 나오는 게 하나이므로 화면에 그려지는 것도 하나다.

 

 

 


+)추가로 해보기

상품1:이미지N
상세페이지에선 조인으로 이미지 가져오기..?근데 지금 prod_id가 있음.
ajax방식 아님. form형식.