오늘은 전에 고객사 측에서 요청한 jpg 파일이 반전되어 보인다!라는 이슈가 있어서 해결했던 것을 예제로 올려볼까 합니다.
보통 이런경우는 스캐너, 팩스, 디지털카메라, 핸드폰 카메라 등에 대한 메타데이터가 있기 마련입니다. 사용자가 보기엔 정상 적으로 보이는 것이 실질적으로 jpg 안에는 메타데이터가 있어서 회전정보가 있기 마련입니다.
거두절미 하고 리암이는 어떻게 해결을 하였나?
저는 metadata-extractor라는 라이브러리를 사용했는데요
1) 해당 프로젝트의 pom.xml에 dependency를 추가한다! 참고로 리암이는 2.9.1 버전을 사용하였습니다~
<dependency>
<groupId>com.drewnoakes</groupId>
<artifactId>metadata-extractor</artifactId>
<version>2.9.1</version>
<dependency>
2) 코드예시를 한번 볼까요?
// jpg 회전값 정상적으로 돌리기
public static void rotateJpgMetaInfo throws Exception {
String FilePath = "D:드라이브의 어쩌구저쩌구~" // 스트링으로 해당파일의 경로를 준것입니당
File getfile = Paths.get(filePath).toFile(); // 이 작업을 거치면 getfile이 생성 될겁니다~
String fileext = FilenameUtils.getExtension(getfile.getName());// Commons-io 라이브러리 이용 확장자 가져오기를 할꺼에요
if (!("jpg".equalsIgnoreCase(fileext) || "jpeg".equalsIgnoreCase(fileext))) { // 만약 jpg를 JPG이렇게 할수도있으니 IgnoreCase 써준것이구요.
return; //return을 해주는이유는 아쉽게도 확장자 jpg,jpeg 만 해준다고합니다. png나 다른것은 이 라이브러리로 해결히 어렵습니다.
}
try {
// 초기값 설정
int rotation = 1;
int width = 0;
int height = 0;
int tempWidth = 0;
Metadata metadata = ImageMetadataReader.readMetadata(getfile);
ExifIFD0Directory exifDir = metadata.getFirstDirectoryOfType(ExifIFD0Directory.class);// 이미지 - 메타데이터 조회
JpegDirectory jpegDirectory = metadata.getFirstDirectoryOfType(JpegDirectory.class);// jpg - width와 height 정보
if (exifDir != null && exifDir.getTagCount() != 0) {// 해당 이미지 메타정보 유무 구분 getTagCount가 0이 아니면 메타데이터 정보가 있을수 있습니다.
rotation = exifDir.getInteger(ExifIFD0Directory.TAG_ORIENTATION); // rotation 값 1일때 정상출력 ex)roation=3(180도)
width = jpegDirectory.getImageWidth();
height = jpegDirectory.getImageHeight();
}
if (rotation == 1) { // 1이면 metadata 정상 or meta정보가 없는것
return;
}
AffineTransform atf = new AffineTransform(); // 변경할 값들 설정
switch (rotation) {
// case 1: // rotation 1 정상(그대로 출력)
// break;
case 2: // 좌우반전
atf.scale(-1.0, 1.0);
atf.translate(-width, 0);
break;
case 3: // 180도 회전
atf.translate(width, height);
atf.rotate(Math.PI);
break;
case 4: // 180도 회전+좌우반전
atf.scale(1.0, -1.0);
atf.translate(0, -height);
break;
case 5: // 270도 회전 + 좌우반전
atf.rotate(-Math.PI / 2);
atf.scale(-1.0, 1.0);
break;
case 6: // 270도 회전
atf.translate(height, 0);
atf.rotate(Math.PI / 2);
break;
case 7: // 90도 회전 + 좌우반전
atf.scale(-1.0, 1.0);
atf.translate(-height, 0);
atf.translate(0, width);
atf.rotate(3 * Math.PI / 2);
break;
case 8: // 90도 회전
atf.translate(0, width);
atf.rotate(3 * Math.PI / 2);
break;
}
switch (rotation) {
case 5:
case 6:
case 7:
case 8:
tempWidth = width; // CASE문 5,6,7,8 은 옆으로 누워있게 나오는 형식이라 위치 변경 별도로 해줌
width = height;
height = tempWidth;
break; // switch case문 5,6,7,8 다돌고 break 걸기 (이렇게도 스위치문 활용이 가능합니당)
}
BufferedImage image = ImageIO.read(getfile);
BufferedImage afterImage = new BufferedImage(width, height, image.getType());
AffineTransformOp rotateOp = new AffineTransformOp(atf, AffineTransformOp.TYPE_BILINEAR);
BufferedImage rotatedImage = rotateOp.filter(image, afterImage);// 여기까지 수정된 이미지 있음
try {
ImageIO.write(rotatedImage, fileext, getfile); // rotate되어진 이미지를 getfile로 재정의 하여 만듬
} catch (Exception e) {
e.printStackTrace();
} finally {
image.flush(); // 이미지 bufferedimg flush 해주기
afterImage.flush();
rotatedImage.flush();
}
} catch (Exception e) {
e.printStackTrace(); //혹여나 if문을 탓는데 nullPointException 에 대응하여 작성하였습니다.
}
}
주석을 보시면 switch case문으로 case1이 정상적인 이미지면 그냥 흘려보내고 아니면 전부 변환을 하여 case1의 정상적인 이미지(사용자가 보기에)로 바꾸어 줄 수 있습니다~ 질문 있으시면 댓글 달아주시면 자세하게 설명 도와드리겠습니다.
반응형
댓글