package itn.let.kakao.kakaoComm.kakaoApi; import java.awt.Image; import java.awt.image.BufferedImage; import java.io.File; import java.io.FileInputStream; import java.io.InputStream; import java.text.SimpleDateFormat; import java.time.LocalDateTime; import java.util.ArrayList; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.ListIterator; import java.util.Map; import javax.annotation.Resource; import javax.imageio.ImageIO; import javax.swing.ImageIcon; import org.apache.commons.io.FilenameUtils; import org.apache.commons.lang3.StringUtils; import org.apache.http.HttpEntity; import org.apache.http.HttpResponse; import org.apache.http.client.HttpClient; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpPost; import org.apache.http.entity.ContentType; import org.apache.http.entity.StringEntity; import org.apache.http.entity.mime.MultipartEntityBuilder; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.client.HttpClients; import org.apache.http.util.EntityUtils; import org.json.simple.JSONObject; import org.json.simple.parser.JSONParser; import org.springframework.beans.factory.annotation.Value; import org.springframework.http.HttpStatus; import org.springframework.stereotype.Component; import org.springframework.web.multipart.MultipartFile; import egovframework.rte.fdl.property.EgovPropertyService; import itn.com.cmm.service.EgovFileMngService; import itn.com.cmm.service.EgovFileMngUtil; import itn.com.cmm.service.FileVO; import itn.let.kakao.kakaoComm.KakaoReturnVO; import itn.let.kakao.kakaoComm.KakaoVO; import itn.let.mail.service.StatusResponse; import itn.let.utl.fcc.service.EgovStringUtil; import lombok.extern.slf4j.Slf4j; @Slf4j @Component("kakaoApiImageUpload") public class KakaoApiImageUpload { /** 비즈 URL */ @Value("#{globalSettings['Globals.mjon.biz.url']}") private String mjonBizUrl; /** 비즈 회원 아이디 */ @Value("#{globalSettings['Globals.mjon.biz.id']}") private String mjonBizId; /** 비즈 회원 API 키*/ @Value("#{globalSettings['Globals.mjon.biz.kakao.apiKey']}") private String mjonBizKakaoApiKey; @Resource(name = "propertiesService") protected EgovPropertyService propertyService; /** 첨부파일 저장경로 */ @Value("#{globalSettings['Globals.file.saveDir']}") private String fileSaveDir; @Resource(name="EgovFileMngUtil") private EgovFileMngUtil fileUtil; @Resource(name="EgovFileMngService") private EgovFileMngService fileMngService; /** * @Method Name : kakaoApiImageUpload * @작성일 : 2023. 1. 19. * @작성자 : 우영두 * @Method 설명 : 친구톡 이미지 등록 */ public Map kakaoApiImageUpload(KakaoVO kakaoVO, Map files, int fileKeyParam) throws Exception { Map resultMap = new HashMap(); try { int fileKey = fileKeyParam; String storePathString = propertyService.getString("Globals.fileStorePath"); File saveFolder = new File(storePathString); if (!saveFolder.exists() || saveFolder.isFile()) { saveFolder.mkdirs(); } List tmp = new ArrayList(files.values()); ListIterator itr = tmp.listIterator(tmp.size()); MultipartFile file; String filePath = ""; FileVO fvo; File imgFile = null; String newName = ""; String fileExt = ""; while (itr.hasPrevious()) { file = itr.previous(); String orginFileName = file.getOriginalFilename(); //-------------------------------------- // 원 파일명이 없는 경우 처리 // (첨부가 되지 않은 input file type) //-------------------------------------- if ("".equals(orginFileName)) { continue; } ////------------------------------------ int index = orginFileName.lastIndexOf("."); fileExt = orginFileName.substring(index + 1); newName = EgovStringUtil.getTimeStamp() + fileKey; long _size = file.getSize(); //이미지 파일 처리라서 확장자까지 모두 붙여서 파일 생성 함 if (!"".equals(orginFileName)) { filePath = saveFolder + File.separator + newName + "." + fileExt; file.transferTo(new File(filePath)); File newFile = new File(filePath); InputStream inputStream = new FileInputStream(newFile); Image img = new ImageIcon(newFile.toString()).getImage(); // 파일 정보 추출 int orgWidth = img.getWidth(null); int orgHeight = img.getHeight(null); long bytes = file.getSize(); long kilobyte = bytes / 1024; long megabyte = kilobyte / 1024; //일반이미지, 와이드 이미지 타입별 용량, 크기 체크 String imgType = kakaoVO.getImageType(); /* * !기본 이미지 파일규격 : JPG, JPEG, PNG 만 가능/ 용량 최대 500KB 권장 사이즈 : 720px X 720px(가로 500px 미만, 가로:세로 비율이 2:1 미만 또는 3:4 초과 시 업로드 불가) !와이드 이미지 파일규격 : JPG, JPEG, PNG 만 가능/ 용량 최대 2MB 사이즈 : 800px X 600px * * */ if(imgType.equals("W")) { if(orgWidth != 800 && orgHeight != 600) {//와이드 이미지인 경우 800 X 600 사이즈만 등록 가능함. resultMap.put("code", "405"); resultMap.put("msg", "와이드 이미지 크기는 800px X 600px 이어야 합니다."); return resultMap; }else if(megabyte > 2) {//와이드 이미지 최대 용량은 2MB resultMap.put("code", "405"); resultMap.put("msg", "와이드 이미지 용량은 2MB 이내여야 합니다."); return resultMap; } }else {//일반 이미지 처리 //일반 이미지 최대 크기는 500KB를 넘을 수 없다. 실제 498KB여도 500이 넘었다고 등록이 안됨. ㅋ if(kilobyte > 497) { resultMap.put("code", "405"); resultMap.put("msg", "파일 최대 용량은 초과하였습니다. 이미지 최대 용량은 500KB를 초과할 수 없습니다."); return resultMap; }else if(orgWidth < 500) {//가로 사이즈가 500px 미만이면 안됨 resultMap.put("code", "405"); resultMap.put("msg", "일반 이미지의 최소 가로 사이즈는 500px 이상이어야 합니다."); return resultMap; } //일반 이미지 권장사이즈 초과시 리사이즈 처리 후 저장 int MAX_WIDTH = 720; int MAX_HEIGHT = 720; if(orgWidth > 720 || orgHeight > 720) { if (orgWidth > MAX_WIDTH) { orgHeight = (int) (orgHeight * (MAX_WIDTH / (float) orgWidth)); orgWidth = MAX_WIDTH; } if (orgHeight > MAX_HEIGHT) { orgWidth = (int) (orgWidth * (MAX_HEIGHT / (float) orgHeight)); orgHeight = MAX_HEIGHT; } //이미지 리사이징 요청 EgovFileMngUtil fileMngUtil = new EgovFileMngUtil(); BufferedImage resizedImage = fileMngUtil.resize(inputStream ,orgWidth , orgHeight ); //리사이징된 파일 덮어쓰기 ImageIO.write(resizedImage, "jpg", new File(filePath)); } } imgFile = new File(filePath); } } if(imgFile.exists()) {//파일이 존재하면 카카오로 전송 처리 //첨부파일 등록 API 전송 요청 String apiUrl = "/v3/kakao/image/upload"; String sendUrl = mjonBizUrl+ apiUrl; String fullFileName = newName + "." + fileExt; CloseableHttpClient httpClient = HttpClients.createDefault(); HttpPost httpPost = new HttpPost(sendUrl); HttpEntity httpEntity = MultipartEntityBuilder.create() .addTextBody("bizId", mjonBizId) .addTextBody("apiKey", mjonBizKakaoApiKey) .addTextBody("imageType", kakaoVO.getImageType()) .addTextBody("title", "test") .addTextBody("link", "https://maaa.com") .addTextBody("senderKey", kakaoVO.getSenderKey()) .addBinaryBody("image", new File(filePath),ContentType.MULTIPART_FORM_DATA,fullFileName) .build(); httpPost.setEntity(httpEntity); CloseableHttpResponse response = httpClient.execute(httpPost); String result = ""; String statusCode = Integer.toString(response.getStatusLine().getStatusCode()); if(statusCode.equals("200")) { result = EntityUtils.toString(response.getEntity()); result = new String(result.getBytes("iso-8859-1"));//한글 깨짐 현상이 있어서 변환 해줌. JSONParser parser = new JSONParser(); Object obj = parser.parse(result); JSONObject object = (JSONObject) obj; String code = object.get("code").toString(); String msg = object.get("message").toString(); String imgUrl = ""; log.info(" : code :: [{}]", code); if(code.equals("200")) { imgUrl = object.get("image").toString(); } resultMap.put("code", code); resultMap.put("msg", msg); resultMap.put("imgUrl", imgUrl); }else { System.out.println("오류 발생"); } httpClient.close(); } }catch (Exception e) { System.out.println("kakaoApiImageUpload API Error !!! " + e); resultMap.put("code", "400"); resultMap.put("msg", "친구톡 이미지 등록에 오류가 발생하였습니다."); resultMap.put("imgUrl", ""); return resultMap; } return resultMap; } /** * @methodName : kakaoApiImageUpload_advc * @author : 이호영 * @date : 2025. 6. 4. * @description : kakaoApiImageUpload 수정 * @return : StatusResponse * @param kakaoVO * @param files * @param i * @return * @throws Exception * */ public StatusResponse kakaoApiImageUpload_advc(KakaoVO kakaoVO, Map files, int fileKeyParam) throws Exception { // try { String storePathString = propertyService.getString("Globals.fileStorePath"); File saveFolder = new File(storePathString); if (!saveFolder.exists()) saveFolder.mkdirs(); // for (MultipartFile file : files.values()) { MultipartFile file = files.values().stream().findFirst().orElse(null); if (file == null || file.isEmpty()) { return new StatusResponse(HttpStatus.BAD_REQUEST, "유효한 이미지 파일이 없습니다.", LocalDateTime.now()); } String originalName = file.getOriginalFilename(); if (originalName == null || originalName.isEmpty()) { return new StatusResponse(HttpStatus.BAD_REQUEST, "파일명이 비어 있습니다.", LocalDateTime.now()); } String ext = FilenameUtils.getExtension(originalName).toLowerCase(); if (!ext.matches("jpg|jpeg|png")) { return new StatusResponse(HttpStatus.BAD_REQUEST, "지원하지 않는 이미지 형식입니다."); } long size = file.getSize(); if (size > 5 * 1024 * 1024) { return new StatusResponse(HttpStatus.BAD_REQUEST, "이미지 용량은 5MB 이내여야 합니다."); } BufferedImage image = ImageIO.read(file.getInputStream()); if (image == null) { return new StatusResponse(HttpStatus.BAD_REQUEST, "이미지를 읽을 수 없습니다."); } int width = image.getWidth(); int height = image.getHeight(); String type = kakaoVO.getImageType(); if ("W".equals(type)) { if (width != 800 || height != 600) { return new StatusResponse(HttpStatus.BAD_REQUEST, "와이드 이미지는 800x600 사이즈만 허용됩니다."); } } else { float ratio = width / (float) height; // log.info("width : [{}], ",width); // log.info("height : [{}], ",height); // log.info("ratio : [{}], ",ratio); if (width < 500 || ratio < 0.75 || ratio > 2.0) { return new StatusResponse(HttpStatus.BAD_REQUEST, "일반 이미지는 가로 500px 이상, 비율 2:1 이상 또는 3:4 이하만 허용됩니다."); } } String atchFileId = this.saveImgFile(files); String newName = EgovStringUtil.getTimeStamp() + fileKeyParam; String filePath = storePathString + File.separator + newName + "." + ext; file.transferTo(new File(filePath)); // 카카오 API 호출 CloseableHttpClient httpClient = HttpClients.createDefault(); String apiUrl = mjonBizUrl + "/v3/kakao/image/upload"; HttpPost httpPost = new HttpPost(apiUrl); /*HttpEntity httpEntity = MultipartEntityBuilder.create() .addTextBody("bizId", mjonBizId) .addTextBody("apiKey", mjonBizKakaoApiKey) .addTextBody("imageType", kakaoVO.getImageType()) .addTextBody("title", kakaoVO.getImgTitle()) .addTextBody("link", kakaoVO.getImgLink()) .addTextBody("senderKey", kakaoVO.getSenderKey()) .addBinaryBody("image", new File(filePath), ContentType.MULTIPART_FORM_DATA, newName + "." + ext) .build(); */ HttpEntity httpEntity = MultipartEntityBuilder.create() .addTextBody("bizId", mjonBizId) .addTextBody("apiKey", mjonBizKakaoApiKey) .addTextBody("imageType", kakaoVO.getImageType()) .addTextBody("title", originalName) .addTextBody("link", StringUtils.isEmpty(kakaoVO.getImgLink()) ? "https://" : kakaoVO.getImgLink()) .addTextBody("senderKey", kakaoVO.getSenderKey()) .addBinaryBody("image", new File(filePath), ContentType.MULTIPART_FORM_DATA, newName + "." + ext) .build(); httpPost.setEntity(httpEntity); CloseableHttpResponse response = httpClient.execute(httpPost); int statusCode = response.getStatusLine().getStatusCode(); if (statusCode == 200) { String result = EntityUtils.toString(response.getEntity(), "UTF-8"); JSONParser parser = new JSONParser(); JSONObject object = (JSONObject) parser.parse(result); String code = object.get("code").toString(); if ("200".equals(code)) { Map returnMap = new HashMap<>(); returnMap.put("imgUrl", object.get("image").toString()); returnMap.put("fileName", originalName); returnMap.put("atchFileId", atchFileId); return new StatusResponse(HttpStatus.OK, "이미지 등록이 완료 되었습니다.", returnMap); } else { return new StatusResponse(HttpStatus.BAD_REQUEST, object.get("message").toString(), LocalDateTime.now()); } } else { return new StatusResponse(HttpStatus.BAD_REQUEST, "카카오 API 요청 실패", LocalDateTime.now()); } // } // } catch (Exception e) { // log.error("kakaoApiImageUpload_advc API Error", e); // return new StatusResponse(HttpStatus.BAD_REQUEST, "친구톡 이미지 등록에 실패했습니다.", LocalDateTime.now()); // } } private String saveImgFile(Map files) throws Exception { String atchFileId = ""; String isThumbFile = ""; String imagePath = ""; String KeyStr = "CANVASIMG_"; Date now = new Date(); SimpleDateFormat formatDate = new SimpleDateFormat("yyyyMMdd"); String fdlDate = formatDate.format(now); imagePath = fileSaveDir+"/file/MMS/" + fdlDate; if (!files.isEmpty()) { List result = fileUtil.parseImageFileInf(files, KeyStr, 0, atchFileId, imagePath, isThumbFile); atchFileId = fileMngService.insertFileInfs(result); } return atchFileId; } /** * @Method Name : kakaoApiTemplateImageUpload * @작성일 : 2023. 2. 16. * @작성자 : 우영두 * @Method 설명 : 알림톡 이미지 업로드 요청 */ public Map kakaoApiTemplateImageUpload(KakaoVO kakaoVO, Map files, int fileKeyParam) throws Exception { Map resultMap = new HashMap(); try { int fileKey = fileKeyParam; String storePathString = propertyService.getString("Globals.fileStorePath"); File saveFolder = new File(storePathString); if (!saveFolder.exists() || saveFolder.isFile()) { saveFolder.mkdirs(); } List tmp = new ArrayList(files.values()); ListIterator itr = tmp.listIterator(tmp.size()); MultipartFile file; String filePath = ""; FileVO fvo; File imgFile = null; String newName = ""; String fileExt = ""; while (itr.hasPrevious()) { file = itr.previous(); String orginFileName = file.getOriginalFilename(); //-------------------------------------- // 원 파일명이 없는 경우 처리 // (첨부가 되지 않은 input file type) //-------------------------------------- if ("".equals(orginFileName)) { continue; } ////------------------------------------ int index = orginFileName.lastIndexOf("."); fileExt = orginFileName.substring(index + 1); newName = EgovStringUtil.getTimeStamp() + fileKey; long _size = file.getSize(); //이미지 파일 처리라서 확장자까지 모두 붙여서 파일 생성 함 if (!"".equals(orginFileName)) { filePath = saveFolder + File.separator + newName + "." + fileExt; file.transferTo(new File(filePath)); File newFile = new File(filePath); InputStream inputStream = new FileInputStream(newFile); Image img = new ImageIcon(newFile.toString()).getImage(); // 파일 정보 추출 int orgWidth = img.getWidth(null); int orgHeight = img.getHeight(null); long bytes = file.getSize(); long kilobyte = bytes / 1024; long megabyte = kilobyte / 1024; //일반이미지, 와이드 이미지 타입별 용량, 크기 체크 String imgType = kakaoVO.getImageType(); /* * !기본 이미지 파일규격 : JPG, JPEG, PNG 만 가능/ 용량 최대 500KB 권장 사이즈 : 720px X 720px(가로 500px 미만, 가로:세로 비율이 2:1 미만 또는 3:4 초과 시 업로드 불가) !와이드 이미지 파일규격 : JPG, JPEG, PNG 만 가능/ 용량 최대 2MB 사이즈 : 800px X 600px * * */ //이미지 최대 크기는 500KB를 넘을 수 없다. 실제 498KB여도 500이 넘었다고 등록이 안됨. ㅋ if(kilobyte > 497) { resultMap.put("code", "405"); resultMap.put("msg", "파일 최대 용량은 초과하였습니다. 이미지 최대 용량은 500KB를 초과할 수 없습니다."); return resultMap; }else if(orgWidth < 500) {//가로 사이즈가 500px 미만이면 안됨 resultMap.put("code", "405"); resultMap.put("msg", "이미지의 최소 가로 사이즈는 500px 이상이어야 합니다."); return resultMap; } //일반 이미지 권장사이즈 초과시 리사이즈 처리 후 저장 int MAX_WIDTH = 800; int MAX_HEIGHT = 400; //800 * 400 이 넘으면 리사이징 처리 /*if(orgWidth > 800 || orgHeight > 400) { orgWidth = MAX_WIDTH; orgHeight = MAX_HEIGHT; if (orgWidth > MAX_WIDTH) { orgHeight = (int) (orgHeight * (MAX_WIDTH / (float) orgWidth)); orgWidth = MAX_WIDTH; } if (orgHeight > MAX_HEIGHT) { orgWidth = (int) (orgWidth * (MAX_HEIGHT / (float) orgHeight)); orgHeight = MAX_HEIGHT; } //이미지 리사이징 요청 EgovFileMngUtil fileMngUtil = new EgovFileMngUtil(); BufferedImage resizedImage = fileMngUtil.resize(inputStream ,orgWidth , orgHeight ); //리사이징된 파일 덮어쓰기 ImageIO.write(resizedImage, "jpg", new File(filePath)); }*/ imgFile = new File(filePath); } } if(imgFile.exists()) {//파일이 존재하면 카카오로 전송 처리 //첨부파일 등록 API 전송 요청 String apiUrl = "/v3/kakao/image/alimtalk/template"; String sendUrl = mjonBizUrl+ apiUrl; String fullFileName = newName + "." + fileExt; CloseableHttpClient httpClient = HttpClients.createDefault(); HttpPost httpPost = new HttpPost(sendUrl); HttpEntity httpEntity = MultipartEntityBuilder.create() .addTextBody("bizId", mjonBizId) .addTextBody("apiKey", mjonBizKakaoApiKey) .addBinaryBody("image", new File(filePath),ContentType.MULTIPART_FORM_DATA,fullFileName) .build(); httpPost.setEntity(httpEntity); CloseableHttpResponse response = httpClient.execute(httpPost); String result = ""; String statusCode = Integer.toString(response.getStatusLine().getStatusCode()); if(statusCode.equals("200")) { result = EntityUtils.toString(response.getEntity()); result = new String(result.getBytes("iso-8859-1"));//한글 깨짐 현상이 있어서 변환 해줌. JSONParser parser = new JSONParser(); Object obj = parser.parse(result); JSONObject object = (JSONObject) obj; String code = object.get("code").toString(); String msg = object.get("message").toString(); String imgUrl = ""; if(code.equals("200")) { imgUrl = object.get("image").toString(); } resultMap.put("code", code); resultMap.put("msg", msg); resultMap.put("imgUrl", imgUrl); }else { System.out.println("오류 발생"); } httpClient.close(); } }catch (Exception e) { System.out.println("kakaoApiTemplateImageUpload API Error !!! " + e); resultMap.put("code", "400"); resultMap.put("msg", "알림톡 이미지 등록에 오류가 발생하였습니다."); resultMap.put("imgUrl", ""); return resultMap; } return resultMap; } /** * @throws Exception * @Method Name : kakaoApiImageDelete * @작성일 : 2023. 3. 23. * @작성자 : 우영두 * @Method 설명 : 카카오 친구톡 이미지 삭제 요청 */ @SuppressWarnings("unchecked") public KakaoReturnVO kakaoApiImageDelete(KakaoVO kakaoVO) throws Exception { KakaoReturnVO kakaoReturnVO = new KakaoReturnVO(); try { String apiUrl = "/v3/kakao/image/delete"; String sendUrl = mjonBizUrl + apiUrl; JSONObject jsonObject = new JSONObject(); jsonObject.put("bizId", mjonBizId); jsonObject.put("apiKey", mjonBizKakaoApiKey); jsonObject.put("imageUrl", kakaoVO.getTemplateImageUrl()); log.info("kakaoVO.getTemplateImageUrl() :: [{}]", kakaoVO.getTemplateImageUrl()); HttpClient httpClient = HttpClientBuilder.create().build(); HttpPost httpPost = new HttpPost(sendUrl); httpPost.setEntity(new StringEntity(jsonObject.toString(), "UTF-8")); httpPost.addHeader("Content-type", "application/json"); httpPost.addHeader("Accept", "application/json"); HttpResponse response = httpClient.execute(httpPost); String result = ""; String statusCode = Integer.toString(response.getStatusLine().getStatusCode()); log.info(" + statusCode :: [{}]", statusCode); if(statusCode.equals("200")) { result = EntityUtils.toString(response.getEntity()); result = new String(result.getBytes("iso-8859-1"));//한글 깨짐 현상이 있어서 변환 해줌. System.out.println(result); JSONParser parser = new JSONParser(); Object obj = parser.parse(result); JSONObject object = (JSONObject) obj; String code = object.get("code").toString(); String msg = object.get("message").toString(); kakaoReturnVO.setBizReturnCode(code); kakaoReturnVO.setBizReturnMsg(msg); }else { kakaoReturnVO.setBizReturnMsg("400 : 명령을 실행 오류"); } } catch (Exception e) { System.out.println("kakaoApiImageDelete API Error !!! " + e); kakaoReturnVO.setBizReturnCode("400"); kakaoReturnVO.setBizReturnMsg("친구톡 이미지 삭제에 오류가 발생하였습니다."); return kakaoReturnVO; } return kakaoReturnVO; } }