<aside> 🍀 영수증 OCR API 명세서 → 명세서를 확인하며 API를 개발했습니다.
</aside>
const submit = async () => {
const formData = new FormData();
const file = base64toFile(image, 'image_file.png');
formData.append("uploadFile", file);
await axios.post("<https://nengcipe-server.store/api/clovaOCR>", formData, {
headers: {
Authorization: "Bearer eyJhbGciOiJIUzI1NiJ9.eyJtZW1iZXJJZCI6InRlc3QxMjMiLCJpZCI6MSwiaWF0IjoxNjg0Mjk4NTA3LCJleHAiOjE2ODQyOTkxMDd9.2lX3kEOL_mxHkL-ZJQYGfPjzPmUz3bcIVvw84WeJF6c"
}
}).then(data =>
console.log(data.data.images));
}
@RestController
public class OCRController {
@Autowired
private OCRService ocrService;
@PostMapping("/api/clovaOCR")
public ResponseEntity<Object> clovaOCR(@RequestPart("uploadFile") MultipartFile file){
String tempDir = System.getProperty("java.io.tmpdir");
System.out.println("임시 디렉토리 경로: " + tempDir);
Object result = null;
try {
result=ocrService.clovaOCRService(multipartFileToFile(file));
} catch (Exception e) {
e.printStackTrace();
}
ResultResponse res = ResultResponse.builder()
.code(HttpStatus.OK.value())
.message("영수증 정보 가져오기 성공.")
.result(result).build();
return new ResponseEntity<>(res, HttpStatus.OK);
}
//Client가 제공하는 MultipartFile을 File 형식으로 변경해야함 (NaverOcr Docs기반)
public File multipartFileToFile(MultipartFile file) throws IOException {
InputStream inputStream = file.getInputStream();
File convertedFile = File.createTempFile("temp", null);
FileOutputStream fileOutputStream = new FileOutputStream(convertedFile);
IOUtils.copy(inputStream, fileOutputStream);
fileOutputStream.close();
inputStream.close();
return convertedFile;
}
}
multipartFileToFile
@Service
public class OCRService {
@Value("${ocr.url}")
private String apiURL;
@Value("${ocr.secretKey}")
private String secretKey;
public Object clovaOCRService(File file) {
List<List<String>> result;
try {
URL url = new URL(apiURL);
HttpURLConnection con = (HttpURLConnection) url.openConnection();
con.setUseCaches(false);
con.setDoInput(true);
con.setDoOutput(true);
con.setReadTimeout(30000);
con.setRequestMethod("POST");
String boundary = "----" + UUID.randomUUID().toString().replaceAll("-", "");
con.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + boundary);
con.setRequestProperty("X-OCR-SECRET", secretKey);
JSONObject json = new JSONObject();
json.put("version", "V2");
json.put("requestId", UUID.randomUUID().toString());
json.put("timestamp", System.currentTimeMillis());
JSONObject image = new JSONObject();
image.put("format", "jpg");
image.put("name", "demo");
JSONArray images = new JSONArray();
images.put(image);
json.put("images", images);
String postParams = json.toString();
con.connect();
DataOutputStream wr = new DataOutputStream(con.getOutputStream());
long start = System.currentTimeMillis();
writeMultiPart(wr, postParams, file, boundary);
wr.close();
int responseCode = con.getResponseCode();
BufferedReader br;
if (responseCode == 200) {
br = new BufferedReader(new InputStreamReader(con.getInputStream()));
} else {
br = new BufferedReader(new InputStreamReader(con.getErrorStream()));
}
String inputLine;
StringBuffer response = new StringBuffer();
while ((inputLine = br.readLine()) != null) {
response.append(inputLine);
}
br.close();
System.out.println(response); // API 호출 결과를 콘솔에 출력
// jsonToString() 메소드 호출하고 결과 받아옴
result = jsonToString(response.toString());
System.out.println(result);
return result;
} catch (Exception e) {
System.out.println("서비스에러");
System.out.println(e);
}
return null;
}
private static void writeMultiPart(OutputStream out, String jsonMessage, File file, String boundary) throws
IOException {
StringBuilder sb = new StringBuilder();
sb.append("--").append(boundary).append("\\r\\n");
sb.append("Content-Disposition:form-data; name=\\"message\\"\\r\\n\\r\\n");
sb.append(jsonMessage);
sb.append("\\r\\n");
out.write(sb.toString().getBytes("UTF-8"));
out.flush();
if (file != null && file.isFile()) {
out.write(("--" + boundary + "\\r\\n").getBytes("UTF-8"));
StringBuilder fileString = new StringBuilder();
fileString
.append("Content-Disposition:form-data; name=\\"file\\"; filename=");
fileString.append("\\"" + file.getName() + "\\"\\r\\n");
fileString.append("Content-Type: application/octet-stream\\r\\n\\r\\n");
out.write(fileString.toString().getBytes("UTF-8"));
out.flush();
try (FileInputStream fis = new FileInputStream(file)) {
byte[] buffer = new byte[8192];
int count;
while ((count = fis.read(buffer)) != -1) {
out.write(buffer, 0, count);
}
out.write("\\r\\n".getBytes());
}
out.write(("--" + boundary + "--\\r\\n").getBytes("UTF-8"));
}
out.flush();
}
public List<List<String>> jsonToString(String jsonResultStr) throws JSONException {
String result = "";
List<List<String>> mainResult = new ArrayList<>();
JSONObject jsonObj = new JSONObject(jsonResultStr);
JSONArray imageArray = jsonObj.getJSONArray("images");
JSONObject tempObj = imageArray.getJSONObject(0);
JSONObject receiptObj = tempObj.getJSONObject("receipt");
JSONObject resultObj = receiptObj.getJSONObject("result");
JSONArray subResultArray = resultObj.getJSONArray("subResults");
JSONObject temp2Obj = subResultArray.getJSONObject(0);
JSONArray itemsArray = temp2Obj.getJSONArray("items");
for (int i = 0; i < itemsArray.length(); i++) {
JSONObject sparseObj = (JSONObject) itemsArray.get(i);
String nameResultStr = sparseObj.getJSONObject("name").getString("text");
String countResultStr = sparseObj.getJSONObject("count").getString("text");
List<String> subResult = new ArrayList<>();
subResult.add(nameResultStr);
subResult.add(countResultStr);
mainResult.add(subResult);
}
System.out.println(mainResult);
//[[생삼겹살, 25], [맥주, 4], [음료수, 4], [밥+된장, 13]]
return mainResult;
}
}
writeMultiPart
jsonToString
Naver Server로부터 전달받은 Json 형태의 Response를 String으로 변경하는 메소드
Naver Server에서 제공해주는 Response에서 ‘냉시피’가 필요로하는 데이터만을 전달하기 위해 명세서를 참고하며 JsonArray와 JsonObject를 맞춰 파싱을 진행했습니다.
example)