2023. 6. 27. 18:57ㆍRestController
Spring Boot
Rest Controller
RestTemplate
구글 api사용법
https://cloud.google.com/vision/docs/object-localizer?hl=ko#detect_objects_in_a_local_image
1. Google Cloud Platform(GCP) 프로젝트 및 인증 설정
google cloud console로 검색후 콘솔로 이동
https://console.cloud.google.com
-GCP프로젝트 생성
새 프로잭트(기존 프로젝트 있는 경우 기존 프로젝트 클릭후 새 프로젝트) -> 프로젝트명 입력 후 ->만들기 클릭
프로젝트 선택후 대시보드 ->시작하기 섹션의 API탐색 및 사용설정 클릭->상단의 API 및 서비스 사용설정 클릭->Cloud Vision API 검색->사용 클릭
-서비스 계정 생성하기
※Google Cloud CLI를 설치후 gcloud auth print-access-token명령어로 ACCESS TOKEN을 발급 받는다
이 명령어를 사용하여 생성된 액세스 토큰은 기본적으로 1시간 동안 유효
토큰의 유효 기간을 설정하는 옵션은 제공되지 않는다
※대부분의 Google Cloud APIs는 API 키를 지원하지 않는다.
이 인증 방법을 사용하기 전에 사용하려는 API가 API 키를 지원하는지 확인해야한다.
API 키는 청구 및 할당 목적으로 요청을 Google Cloud 프로젝트와 연결한다.
API 키가 호출자를 식별하지 않기 때문에 종종 공개 데이터 또는 리소스에 액세스하는 데 사용된다.
많은 Google Cloud API가 인증용으로 API 키를 수락하지 않는다.
API 키 지원 여부를 확인하기 위해 사용하려는 서비스 또는 API에 대해 인증 문서를 확인해야 한다.
좌측의 사용자 인증 정보 클릭->서비스 계정관리 클릭->상단의 서비스 계정 만들기 클릭
서비스 계정명입력후 ->만들고 계속하기 클릭->역할 선택에서 소유자 선택->계속 클릭후 완료 버튼 클릭
목록의 우측 3 Dot 메뉴에서 키관리 클릭->키 추가 클릭->새 키 만들기->JSON선택후 다운로드(클라우드 리소스 액세스를 허용)
-Google Cloud CLI를 설치 초기화
https://cloud.google.com/sdk/docs/install?hl=ko
Google Cloud CLI설치
PowerShell 터미널을 관리자 권한으로 열고 다음 PowerShell 명령어를 실행
(New-Object Net.WebClient).DownloadFile("https://dl.google.com/dl/cloudsdk/channels/rapid/GoogleCloudSDKInstaller.exe", "$env:Temp\GoogleCloudSDKInstaller.exe")
& $env:Temp\GoogleCloudSDKInstaller.exe
안내에 따라 설치한다(수 분이 걸린다)
Google Cloud CLI 설치후 초기화
Google Cloud SDK Shell 실행후 아래 입력
C:\Users\kosmo\AppData\Local\Google\Cloud SDK>gcloud init
이후 이 Application에 대한 인증절차를 마친다
혹은 아래 명령어로 추후에 인증할 수도 있다
C:\Users\kosmo\AppData\Local\Google\Cloud SDK>gcloud auth application-default login
2. 스프링 프로젝트에 적용하기
선행 작업: 환경변수 등록 혹은 이클립스 프로젝트 선택후 마우스 우클릭->Run as ->Run Configuration (Google Cloud APIs필요시 사용)
Name : GOOGLE_APPLICATION_CREDENTIALS
Value : 다운받은 json파일의 위치
2-1. REST로 적용시
-객체 감지]
HTTP 메서드 및 URL:
POST https://vision.googleapis.com/v1/images:annotate
JSON 요청 바디:
{
"requests": [
{
"image": {
"content": "BASE64_ENCODED_IMAGE"
},
"features": [
{
"maxResults": RESULTS_INT,
"type": "OBJECT_LOCALIZATION"
},
]
}
]
}
혹은
{
"requests": [
{
"image": {
"source": {
"imageUri": "CLOUD_STORAGE_IMAGE_URI"
}
},
"features": [
{
"maxResults": RESULTS_INT,
"type": "OBJECT_LOCALIZATION"
},
]
}
]
}
요청 헤더:
"Authorization: Bearer $(gcloud auth print-access-token)"
"x-goog-user-project: PROJECT_ID"
"Content-Type: application/json; charset=utf-8"
-OCR]
HTTP 메서드 및 URL:
POST https://vision.googleapis.com/v1/images:annotate
JSON 요청 바디:
{
"requests": [
{
"image": {
"content": "BASE64_ENCODED_IMAGE"
},
"features": [
{
"type": "TEXT_DETECTION"
}
]
}
]
}
혹은
{
"requests": [
{
"image": {
"source": {
"imageUri": "CLOUD_STORAGE_IMAGE_URI"
}
},
"features": [
{
"type": "TEXT_DETECTION"
}
]
}
]
}
요청 헤더:
"Authorization: Bearer $(gcloud auth print-access-token)"
"x-goog-user-project: PROJECT_ID"
"Content-Type: application/json; charset=utf-8"
2-2.자바코드로 적용시
-pom.xml에 라이브러 추가
<dependency>
<groupId>com.google.cloud</groupId>
<artifactId>google-cloud-vision</artifactId>
<version>3.1.0</version>
</dependency>
-구글 사이트에 있는 언어별 소스 사용
import com.google.cloud.vision.v1.AnnotateImageRequest;
import com.google.cloud.vision.v1.AnnotateImageResponse;
import com.google.cloud.vision.v1.BatchAnnotateImagesResponse;
import com.google.cloud.vision.v1.EntityAnnotation;
import com.google.cloud.vision.v1.Feature;
import com.google.cloud.vision.v1.Feature.Type;
import com.google.cloud.vision.v1.Image;
import com.google.cloud.vision.v1.ImageAnnotatorClient;
import com.google.cloud.vision.v1.LocalizedObjectAnnotation;
import com.google.protobuf.ByteString;
//이미지 객체 검출
public static void detectLocalizedObjects(String filePath,HttpServletRequest req) throws IOException {
String path=req.getSession().getServletContext().getRealPath("/upload");
List<AnnotateImageRequest> requests = new ArrayList<>();
ByteString imgBytes = ByteString.readFrom(new FileInputStream(path+File.separator+"pizza.jpg"));
Image img = Image.newBuilder().setContent(imgBytes).build();
AnnotateImageRequest request =
AnnotateImageRequest.newBuilder()
.addFeatures(Feature.newBuilder().setType(Type.OBJECT_LOCALIZATION))
.setImage(img)
.build();
requests.add(request);
// Initialize client that will be used to send requests. This client only needs to be created
// once, and can be reused for multiple requests. After completing all of your requests, call
// the "close" method on the client to safely clean up any remaining background resources.
try (ImageAnnotatorClient client = ImageAnnotatorClient.create()) {
// Perform the request
BatchAnnotateImagesResponse response = client.batchAnnotateImages(requests);
List<AnnotateImageResponse> responses = response.getResponsesList();
// Display the results
for (AnnotateImageResponse res : responses) {
for (LocalizedObjectAnnotation entity : res.getLocalizedObjectAnnotationsList()) {
System.out.format("Object name: %s%n", entity.getName());
System.out.format("Confidence: %s%n", entity.getScore());
System.out.format("Normalized Vertices:%n");
entity
.getBoundingPoly()
.getNormalizedVerticesList()
.forEach(vertex -> System.out.format("- (%s, %s)%n", vertex.getX(), vertex.getY()));
}
}
}
}
//OCR
public static void detectText(HttpServletRequest req) throws IOException {
// TODO(developer): Replace these variables before running the sample.
String path=req.getSession().getServletContext().getRealPath("/upload");
String filePath =path+File.separator+ "sign_small.jpg";
detectText(filePath);
}
// Detects text in the specified image.
public static void detectText(String filePath) throws IOException {
List<AnnotateImageRequest> requests = new ArrayList<>();
ByteString imgBytes = ByteString.readFrom(new FileInputStream(filePath));
Image img = Image.newBuilder().setContent(imgBytes).build();
Feature feat = Feature.newBuilder().setType(Feature.Type.TEXT_DETECTION).build();
AnnotateImageRequest request =
AnnotateImageRequest.newBuilder().addFeatures(feat).setImage(img).build();
requests.add(request);
// Initialize client that will be used to send requests. This client only needs to be created
// once, and can be reused for multiple requests. After completing all of your requests, call
// the "close" method on the client to safely clean up any remaining background resources.
try (ImageAnnotatorClient client = ImageAnnotatorClient.create()) {
BatchAnnotateImagesResponse response = client.batchAnnotateImages(requests);
List<AnnotateImageResponse> responses = response.getResponsesList();
for (AnnotateImageResponse res : responses) {
if (res.hasError()) {
System.out.format("Error: %s%n", res.getError().getMessage());
return;
}
// For full list of available annotations, see http://g.co/cloud/vision/docs
for (EntityAnnotation annotation : res.getTextAnnotationsList()) {
System.out.format("Text: %s%n", annotation.getDescription());
System.out.format("Position : %s%n", annotation.getBoundingPoly());
}
}
}
}
몇몇 링크들:
https://console.cloud.google.com/
https://cloud.google.com/sdk/docs/install?hl=ko
https://cloud.google.com/vision/docs/object-localizer?hl=ko
환경변수 설정
pom.xml
com.kosmo.restapi.config패키지에 RestTemplateConfig.java
package com.kosmo.restapi.config;
import java.util.concurrent.TimeUnit;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;
//RestTemplate 커넥션 풀 사용하기 위한 빈 등록
@Configuration
public class RestTemplateConfig {
/*
* 반환 타입:IOC컨테이너(스프링 컨테이너)에 등록할 빈(싱글 톤)
* 메소드명:생성된 빈의 이름(아이디값)
* 예:RestTemplate restTemplate = new RestTemplate(ClientHttpRequestFactory)
*/
/*
사전 작업:POM.XML에 아래 등록(RestTemplate사용시 커넥션 풀을 사용하기 위함)
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.13</version>
</dependency>
*/
@Bean
public RestTemplate restTemplate() {
//1.커넥션 풀 사용을 위한 HttpClient객체 생성
CloseableHttpClient httpClient = HttpClientBuilder.create()
.setMaxConnTotal(50)//연결을 유지할 최대 Http커넥션 수
.setMaxConnPerRoute(50)//Route당(요청 URI주소당) 최대 Http커넥션 수
.setConnectionTimeToLive(5,TimeUnit.SECONDS)//커넥션 연결 유지시간
.build();
//2.타임아웃 설정을 위한 객체 생성
HttpComponentsClientHttpRequestFactory factory=new HttpComponentsClientHttpRequestFactory();
factory.setConnectionRequestTimeout(3000);//요청 타임아웃 시간
factory.setHttpClient(httpClient);
return new RestTemplate(factory);
}
}
com.kosmo.restapi.model패키지에 ObjectDetectDTO.java
[]:대괄호를 클래스로, 그 속에 있는 것을 요소로 보고 생성(원래는 아래 preview)으로 POJO로 변환시키는 것이지만 더 간단히 만들기 위해서)
package com.kosmo.restapi.model;
import java.util.List;
import lombok.Getter;
import lombok.Setter;
//구글의 객체감지 요청 본문(JSON형식)을 자바빈으로 변환
@Getter
@Setter
public class ObjectDetectDTO {
private List<Request> requests;
@Getter
@Setter
public static class Request{
private Image image;
private List<Feature> features;
private ImageContext imageContext;
}
@Getter
@Setter
public static class Feature{
private int maxResults;
private String type;
}
@Getter
@Setter
public static class Image{
private Source source;
}
@Getter
@Setter
public static class Source {
private String imageUri;
}
//응답을 한글로 받기(디폴트는 영어)
@Getter
@Setter
public static class ImageContext{
private List<String> languageHints;
}
}
FileUtils.java는 전, springapp프로젝트에서 com.kosmo.springapp.basic.fileupdown패키지에서 가져온다(복붙)
com.kosmo.restapi.controller패키지에 RestApiController.java
package com.kosmo.restapi.controller;
import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Vector;
import javax.servlet.http.HttpServletRequest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ClassPathResource;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RequestPart;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.client.DefaultResponseErrorHandler;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.multipart.MultipartFile;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.kosmo.restapi.model.FileUtils;
import com.kosmo.restapi.model.ObjectDetectDTO;
import com.kosmo.restapi.model.UsersDto;
import com.kosmo.restapi.model.UsersMapper;
import io.swagger.annotations.ApiOperation;
@RestControllerAdvice
@RestController
public class RestApiController {
@Autowired
private UsersMapper mapper;
//1.데이타를 key=value쌍으로 받아서 입력
//-FORM태그를 이용해서 요청하거나
//-jQuery ajax로 요청시에는 data:"username=KIM&password=1234&name=김길동",
// 혹은 data:{username:"KIM",password:"1234",name:"김길동"}
//-POSTMAN으로 요청시에는 Body탭의 x-www-form-urlencoded 선택후 key와 value입력
//요청 형식
//POST http://localhost:8080/users
/*
@PostMapping("/users")
public UsersDto join(UsersDto dto) {
int affected= mapper.save(dto);
return dto;
}*/
//2.데이타를 JSON으로 받아서 입력
//-jQuery ajax로 요청시에는 data:JSON.stringify({키:"값",키2:"값2"}),
// contentType:"application/json"
//-POSTMAN으로 요청시에는 Body탭의 raw 선택 및 json선택 후 json형태로 데이터 작성
//요청 형식
//POST http://localhost:8080/users
@PostMapping("/users")
@ApiOperation(value = "회원가입",notes = "JSON형식의 데이타를 받아 JSON형식의 데이타 반환")
public UsersDto join(@RequestBody UsersDto dto) {
int affected= mapper.save(dto);
return dto;
}
//3.회원의 모든 데이타 조회
/*
-FORM태그 혹은 A태그
-jQuery ajax
-POSTMAN
*/
//요청 형식
//GET http://localhost:8080/users
@GetMapping("/users")
@CrossOrigin
public List<UsersDto> getAllUsers(){
return mapper.find();
}
//4.URI 파라미터(패스 파라미터)로 회원 조회
/*
-FORM태그 혹은 A태그
-jQuery ajax
-POSTMAN
*/
//요청 형식
//GET http://localhost:8080/users/:username
@GetMapping("/users/{username}")
public UsersDto getUserByUsername(@PathVariable String username ) {
return mapper.findByUsername(username);
}
//5.데이타를 JSON으로 받아서 수정(반드시 JSON으로 받아야 한다)
//AJAX 혹은 POSTMAN으로 요청
//※PUT이나 DELETE도 데이타는 요청바디에 싣는다
//DTO에는 수정할 비번과 이름을 받자
//예]{
// "password":"5678",
// "name":"이길동"
// }
//요청 형식
//PUT http://localhost:9090/users/:username
@PutMapping("/users/{username}")
public UsersDto editUser(@PathVariable String username,@RequestBody UsersDto dto) {
//어느 회원을 수정할지 자바빈에 설정
dto.setUsername(username);
mapper.update(dto);
return dto;
}
//6.키를 URL파라미터로 받아서 삭제
//AJAX 혹은 POSTMAN으로 요청
//데이타는 필요없다
//요청 형식
//DELETE http://localhost:9090/users/:username
/*
@DeleteMapping("/users/{username}")
public UsersDto removeUser(@PathVariable String username) {
UsersDto dto= mapper.findByUsername(username);
mapper.delete(username);
return dto;
}*/
/*
//반환타입 MAP
/*
@DeleteMapping("/users/{username}")
public Map removeUser(@PathVariable String username) {
UsersDto dto= mapper.findByUsername(username);
mapper.delete(username);
//DTO를 Map으로 변경
//방법1:직접 DTO를 MAP으로 (MAP를 DTO로)
//Map map = new HashMap();
//map.put("username", dto.getUsername());
//map.put("password", dto.getPassword());
//map.put("name", dto.getName());
//map.put("joindate", dto.getJoindate());
//방법2:apache common의 BeanUtils.describe(DTO객체); 혹은 Jackson 의 ObjectMapper사용
ObjectMapper oMapper = new ObjectMapper();
Map map = oMapper.convertValue(dto, Map.class);
return map;
}*/
//반환타입 STRING
/*
@DeleteMapping("/users/{username}")
public String removeUser(@PathVariable String username) throws JsonProcessingException {
UsersDto dto= mapper.findByUsername(username);
mapper.delete(username);
//DTO(MAP)를 문자열(JSON형태)로 변환
ObjectMapper oMapper = new ObjectMapper();
return oMapper.writeValueAsString(dto);
}*/
//반환타입 RESPONSEENTITY
@DeleteMapping("/users/{username}")
public ResponseEntity<UsersDto> removeUser(@PathVariable String username){
UsersDto dto= mapper.findByUsername(username);
mapper.delete(username);
HttpHeaders headers= new HttpHeaders();
headers.set("Content-Type", "application/json;charset=UTF-8");
return ResponseEntity.ok().headers(headers).body(dto);
}
//7.파일 업로드
//key=value 형태로 전송
//FORM태그(enctype="multipart/form-data") 혹은 AJAX 혹은 POSTMAN으로 요청
//POSTMAN으로 요청시에는 Body탭의 form-data 선택후
//key와 value입력
//파일인 경우 key입력시 옆에 file선택
//요청 형식
//POST http://localhost:8080/files
@CrossOrigin
@PostMapping(value = "/files",consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
//public ResponseEntity<Map> upload(HttpServletRequest req,@RequestParam List<MultipartFile> files) throws IllegalStateException, IOException{
public ResponseEntity<Map> upload(HttpServletRequest req,@RequestPart List<MultipartFile> files) throws IllegalStateException, IOException{//@RequestPart로 받아야 스웨거에서 파일찾기 버튼이 생긴다
//서버의 물리적 경로 얻기
String path = req.getServletContext().getRealPath("/upload");
for(MultipartFile muFile:files) {
//2]File객체 생성
//파일 중복시 이름 변경
String newFilename=FileUtils.getNewFileName(path, muFile.getOriginalFilename());
File file = new File(path+File.separator+newFilename);
//3]파일 업로드
muFile.transferTo(file);
}
Map map = new HashMap();
map.put("success", true);
return ResponseEntity.ok().header("Content-Type", "application/json;charset=UTF-8").body(map);
}
@ExceptionHandler({Exception.class})
public ResponseEntity<String> error(){
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.header("Content-Type", "text/plain;charset=UTF-8")
.body("업로드 최대용량 초과 했어요");
}
/*
[RestTemplate]
-Spring 3.0부터 지원하는 내장 클래스로 스프링 서버에서 REST한 방식으로 HTTP 통신을 하기위한 API
-Rest방식으로 다른 서버와 HTTP 통신을 동기 방식으로 쉽게 할수 있는 템플릿
(AsyncRestTemplate는 비동기 통신)
-기본적으로 Connection pool을 사용하지 않아서
많은 요청을 하면 TIME_WAIT로 인해 자원이 점점 부족해져
서비스에 어려움이 있다
-내부적으로 java.net.HttpURLConnection 사용
-요청을 보낼때는 HttpEntity< Map혹은 DTO,HttpHeaders>타입에 요청바디(데이타)와 요청헤더와 설정
※클라이언트가 보내는 데이타가 Key=Value쌍(application/x-www-form-urlencoded)일때는 반드시 MultiValueMap 사용
데이타가 JSON일때는 (application/json)일때는 MultiValueMap 혹은 Map 사용
-응답을 받을때는 ResponseEntity<Map혹은 DTO>
*/
//JSON을 자바객체로 변환하기
//https://www.jsonschema2pojo.org
//※단,서버에서 받은 JSON의 키에 _가 포함되어 있는 경우 자동으로 카멜 케이스로 바뀌니까 _로 다시 수정해주자
//구글 비전 API 사용
//객체 감지
//POST https://vision.googleapis.com/v1/images:annotate
/*
JSON 요청 본문:
{
"requests": [
{
"image": {
"source": {
"imageUri": "CLOUD_STORAGE_IMAGE_URI"
}
},
"features": [
{
"maxResults": RESULTS_INT,
"type": "OBJECT_LOCALIZATION"
},
]
}
]
}
요청 헤더
-H "Authorization: Bearer $(gcloud auth print-access-token)" \
-H "x-goog-user-project: PROJECT_ID" \
-H "Content-Type: application/json; charset=utf-8" \
*/
@Autowired
private RestTemplate restTemplate;
@CrossOrigin
@PostMapping("/vision/object-detect")
public Map objectDetect(@RequestBody Map paramMap) throws IOException {
//401:인증 오류시 아래 에러 핸들러 추가
restTemplate.setRequestFactory(new HttpComponentsClientHttpRequestFactory());
restTemplate.setErrorHandler(new DefaultResponseErrorHandler() {
@Override
public boolean hasError(ClientHttpResponse response) throws IOException {
HttpStatus status= response.getStatusCode();
return status.series() == HttpStatus.Series.SERVER_ERROR;
}
});
//1.요청헤더 설정용 객체 생성
HttpHeaders headers = new HttpHeaders();
headers.add("Authorization", "Bearer ya29.a0AWY7CklGZ1PW9zWPZznG3vg_v-uif5bO9KKU2mpolxqTodPyR9nqTQOynSliP1rosBAeQcsQXai_FMR0NUg_m0hgDumI_gKnoy2ONg8aA4of2rXpvbj108jRWNWS7u5P1UxESAhs5okAkP_Dz_o-urPn66D1T35QiqYQ4waCgYKARESARASFQG1tDrpjg-HQhb_gh7hjwPytmZswg0173");
headers.add("x-goog-user-project", "nomadic-genre-391102");
headers.add("Content-Type", "application/json; charset=utf-8");
//2.구글의 요청본문(JSON형태)과 동일한 구조의 DTO로 요청바디 설정
//방법1:자바빈에 요청바디 설정
ObjectDetectDTO requestBody = new ObjectDetectDTO();
ObjectDetectDTO.Source source = new ObjectDetectDTO.Source();
//source.setImageUri("https://ilyo.co.kr/contents/article/images/2019/1004/1570150763720629.jpg");
source.setImageUri(paramMap.get("url").toString());
ObjectDetectDTO.Image images = new ObjectDetectDTO.Image();
images.setSource(source);
// "LABEL_DETECTION": 이미지에 포함된 물체나 개념을 탐지하고 관련된 라벨을 제공.
// "TEXT_DETECTION": 이미지에서 텍스트를 감지하고 추출.
// "DOCUMENT_TEXT_DETECTION": 이미지에 포함된 문서 전체의 텍스트를 감지하고 추출다.
// "FACE_DETECTION": 이미지에서 얼굴을 감지하고 얼굴에 대한 특성과 감정을 분석.
// "LOGO_DETECTION": 이미지에서 로고를 감지하고 관련 정보를 제공.
// "LANDMARK_DETECTION": 이미지에서 유명한 랜드마크를 감지하고 식별.
// "IMAGE_PROPERTIES": 이미지의 색상 정보를 분석하고 주요 색상 및 이미지 특성을 제공.
// "SAFE_SEARCH_DETECTION": 이미지에 대한 안전한 검색 결과를 제공하고 불순한 콘텐츠를 필터링.
ObjectDetectDTO.Feature feature = new ObjectDetectDTO.Feature();
feature.setMaxResults(20);
//feature.setType("FACE_DETECTION");
feature.setType(paramMap.get("type").toString());
ObjectDetectDTO.Request request= new ObjectDetectDTO.Request();
request.setImage(images);
request.setFeatures(Arrays.asList(feature));
ObjectDetectDTO.ImageContext imageContext = new ObjectDetectDTO.ImageContext();
imageContext.setLanguageHints(Arrays.asList("ko"));
request.setImageContext(imageContext);
requestBody.setRequests(Arrays.asList(request));
/*
//방법2:리소스의 클래스 패스상에 요청본문형식의 JSON파일 생성후
// 읽어서 맵으로 변환
ClassPathResource resource = new ClassPathResource("json");
//물리적 경로 얻기
File file = resource.getFile();
String directoryPath = file.getAbsolutePath();
//생성할 파일의 경로를 지정.
String newFilePath = directoryPath + File.separator + "detect.json";
ObjectMapper objectMapper= new ObjectMapper();
Map bodyMap=objectMapper.readValue(new File(newFilePath), Map.class);
System.out.println("json파일 읽기:"+bodyMap.get("requests"));
*/
//3.요청 헤더정보등을 담은 HttpEntity객체 생성
//DTO혹은 Map에는 요청시 서버에 보낼 데이타를 담는다.
//※데이타가 Key=Value쌍(application/x-www-form-urlencoded)일때는 반드시 MultiValueMap 사용
// 데이타가 JSON일때는 (application/json)일때는 MultiValueMap 혹은 Map 사용
//HttpEntity<DTO혹은 Map> entity = new HttpEntity(DTO혹은 Map객체,headers);
HttpEntity entity = new HttpEntity(requestBody,headers);
//4.RestTemplate으로 요청 보내기
//String url="한글이 포한된 요청URI";
//요청 URL에 한글 포함시는 UriComponents로 객체 생성후 사용시는 uri.toString()해서 사용한다
//UriComponents uri = UriComponentsBuilder.fromHttpUrl(url).build();
String url="https://vision.googleapis.com/v1/images:annotate";
//외부 OPEN API(구글)서버로부터 받은 데이타 타입이
//{}인 경우 Map 혹은 DTO
//[{},{},....]인 경우 List<Map 혹은 DTO>
ResponseEntity<Map> responseEntity = restTemplate.exchange(
url, //요청 URI
HttpMethod.POST,//요청 메소드
entity,//HttpEntity(요청바디와 요청헤더)
Map.class//응답 데이타가 {}일때
//DTO계열.class//응답 데이타가 {}일때
//List.class//응답 데이타가 [{},{},....]일때
);
System.out.println("응답코드:"+responseEntity.getStatusCodeValue());
System.out.println("응답헤더:"+responseEntity.getHeaders());
System.out.println("응답바디:"+responseEntity.getBody());
return responseEntity.getBody();
}///////////////////////////////
@CrossOrigin
@PostMapping("/vision/ocr")
public Map ocr(@RequestParam String base64) {
//401:인증 오류시 아래 에러 핸들러 추가
restTemplate.setRequestFactory(new HttpComponentsClientHttpRequestFactory());
restTemplate.setErrorHandler(new DefaultResponseErrorHandler() {
@Override
public boolean hasError(ClientHttpResponse response) throws IOException {
HttpStatus status= response.getStatusCode();
return status.series() == HttpStatus.Series.SERVER_ERROR;
}
});
//1.요청헤더 설정용 객체 생성
HttpHeaders headers = new HttpHeaders();
headers.add("Authorization", "Bearer ya29.a0AWY7CklGZ1PW9zWPZznG3vg_v-uif5bO9KKU2mpolxqTodPyR9nqTQOynSliP1rosBAeQcsQXai_FMR0NUg_m0hgDumI_gKnoy2ONg8aA4of2rXpvbj108jRWNWS7u5P1UxESAhs5okAkP_Dz_o-urPn66D1T35QiqYQ4waCgYKARESARASFQG1tDrpjg-HQhb_gh7hjwPytmZswg0173");
headers.add("x-goog-user-project", "nomadic-genre-391102");
headers.add("Content-Type", "application/json; charset=utf-8");
//2.구글의 요청본문(JSON형태)과 동일한 구조의 맵(방법3)으로 요청바디 설정
Map<String,List> requestBody = new HashMap<>();
List value = new Vector();
Map map = new HashMap();
Map imageValue = new HashMap();
imageValue.put("content", base64);
map.put("image", imageValue);
List featureValue = new Vector();
Map featureMap = new HashMap();
featureMap.put("type","TEXT_DETECTION");
featureValue.add(featureMap);
map.put("features", featureValue);
value.add(map);
requestBody.put("requests", value);
//3.요청 헤더정보등을 담은 HttpEntity객체 생성
HttpEntity entity = new HttpEntity(requestBody,headers);
//4.RestTemplate으로 요청 보내기
String url="https://vision.googleapis.com/v1/images:annotate";
//외부 OPEN API(구글)서버로부터 받은 데이타 타입이
//{}인 경우 Map 혹은 DTO
//[{},{},....]인 경우 List<Map 혹은 DTO>
ResponseEntity<Map> responseEntity = restTemplate.exchange(
url, //요청 URI
HttpMethod.POST,//요청 메소드
entity,//HttpEntity(요청바디와 요청헤더)
Map.class//응답 데이타가 {}일때
//DTO계열.class//응답 데이타가 {}일때
//List.class//응답 데이타가 [{},{},....]일때
);
System.out.println("응답코드:"+responseEntity.getStatusCodeValue());
System.out.println("응답헤더:"+responseEntity.getHeaders());
System.out.println("응답바디:"+responseEntity.getBody());
return responseEntity.getBody();
}
}
전 프로젝트(springapp)에서 webapp->WEB-INF->views->ajax10폴더에서 Ajax.jsp
에서 Rest Controller부분을 함 11~13까지
Ajax.jsp
Ajax.jsp의 script부분
//11]스프링 REST API서버로 자체 데이타 요청
$('#btnSelfRestApi').click(function(){
$.ajax({
url:"http://localhost:8080/users",
dataType:'json'
}).done(function(data){
console.log(data);
});
});
//12]스프링 REST API서버로 요청(구글 비젼-객체 탐지)
$('#btnGoogleRestApi').click(function(){
$.ajax({
method:"post",
url:"http://localhost:8080/vision/object-detect",
dataType:'json',
contentType:"application/json",
data:JSON.stringify({"url":"https://cdn.imweb.me/upload/S201911194483dd7a28d0d/ea7d24ddebffb.png","type":"TEXT_DETECTION"})
}).done(function(data){
console.log(data);
var detect=data.responses[0]['localizedObjectAnnotations'];
$.each(detect,function(index,item){
console.log("객체명:%s,정확도:%s",item.name,item.score*100+'%');
});
});
});
//13]OCR
//https://developer.mozilla.org/en-US/docs/Web/API/FileReader/readAsDataURL
//https://developer.mozilla.org/ko/docs/Web/API/FormData/FormData
$('input[name=files]').change(function(e){
var formData = new FormData();//AJAX로 파일 업로드시 사용
console.log("e.target.files:",e.target.files);
//append(첫번째 인자 파라미터명,두번째 인자 File객체)
formData.append("files",e.target.files[0]);
$.ajax({
url:"http://localhost:8080/files",
processData:false,
contentType:false,
data:formData,
method:'post'
}).done(function(data){
console.log('업로드 성공:',data);
var reader = new FileReader();//이미지파일을 Base64로 인코딩용
reader.onload=function(e){
//이미지 미리보기
$('#preview').attr("src",e.target.result);
//Base64인코딩
var base64=e.target.result;
console.log(base64.split(",")[1]);
//여기서 Base64인코딩된 문자열을 스프링 REST API서버로 전송한다
$.ajax({
url:"http://localhost:8080/vision/ocr",
method:'post',
data:"base64="+encodeURIComponent(base64.split(",")[1]),
dataType:'json'
}).done(function(data){
console.log('구글 서버로부터 받은 데이타:',data);
console.log(data['responses'][0]['fullTextAnnotation']['text'])
});
};
//미리보기용
reader.readAsDataURL(e.target.files[0]);
});
});
결과)
'RestController' 카테고리의 다른 글
76일차 2023-06-26 (0) | 2023.06.26 |
---|