BUG NOTE : 사진이 회전된 상태로 서버에 업로드 되는 이슈

동훈이·2023년 3월 2일
0

bug-note

목록 보기
1/1
post-thumbnail

어떤 상황이였나요?

사진을 서버에 업로드하려고 할때 회전되어서 올라가는 문제가 발생하였습니다. 그런데 디버그로 해당 이미지를 비트맵으로 바꾸어 확인 하였을때, 상위 OS 에서는 발생하지 않고 하위기기에서 발생하였습니다.

💡 왜 상위 버전에서는 해당 문제가 발생하지 않았던걸까?

정확히는 안드로이드 OS의 차이도 있겠지만, 실제로 해당 사진을 찍은 카메라의 앱의 구현 방식에 따라서 달라졌을 거라는 생각을 하였습니다. 실제로 Camera API는 이미지 방향 메타데이터를 자동으로 설정하지 않아 수동으로 처리해야 합니다. 그러나 CameraX는 이미지 방향 메타데이터를 자동으로 설정하므로 문제가 발생하지 않았었습니다.

그래서 우선 사진 회전값을 알아내어 회전을 하여 다시 업로드를 해주는 로직이 필요하였습니다. 하지만 그래도 동일하게 문제가 발생하고 있었습니다. 사진을 회전하는 로직을 작성했음에도 사진은 그대로 였고, ExifInterface.TAG_ORIENTATION 의 값을 확인 하였을때 항상 0 으로 반환되는 상태였습니다. 당연히 0 으로 반환되니 추가적인 회전이 없어 사진은 회전되어있는 상태로 디버그에 찍혔습니다.

무엇이 문제였나요?

val bitmap: Bitmap = BitmapFactory.decodeStream(inputStream)
val inputStream: InputStream? = getFileToInputStream(uri)
val exif = ExifInterface(inputStream)
val matrix = getCorrectAngleMatrix(exif)

그래서 우선 ExifInterface 를 제대로 사용하고 있는지? 파악을 위해 생성자를 살펴보았을때 아래 문구를 확인해볼 수 있었습니다

이미지 파일에서 Exif 태그를 읽는 작업을 수행하는데, 이때 입력 스트림에 대한 속성 변경은 지원되지 않습니다. 따라서 입력 스트림의 현재 위치에서 계속 작업을 수행하게 됩니다. 또한, 이 생성자는 입력 스트림을 사용한 후에는 반드시 닫아주어야 합니다. 이 생성자는 네트워크 작업을 수행하는 입력 스트림과 함께 사용할 수 없습니다.

이중에서 포인트는 바로 따라서 입력 스트림의 현재 위치에서 계속 작업을 수행하게 됩니다. 라는 문구입니다. InputStream 은 파일을 읽을 때, 파일의 시작부터 끝까지 한 번에 모두 읽는 것이 아니라, 일정한 크기로 나누어서 읽습니다. 이때, "현재 위치"는 입력 스트림이 마지막으로 읽은 바이트의 위치를 의미합니다. 예를 들어, 입력 스트림이 10MB 단위로 나누어서 이미지 파일을 읽고 있다면, 현재 위치는 현재까지 읽은 10MB 단위의 마지막 바이트의 위치입니다.

💡 따라서 이미지 파일을 처음부터 다시 읽거나 불가능 하기에 InputStream 은 한번만 이미지를 읽어올 수 있습니다.

그럼 위 코드를 다시 살펴보면 이미 val bitmap: Bitmap = BitmapFactory.decodeStream(inputStream) 에서 한번 inputStream 을 사용하였기에 ExifInterface 를 생성할때 사용했던 inpuStream 에는 다음에 읽을 이미지 값이 없었고, 그래서 기본값이 0 을 항상 출력했었습니다.

BitmapFactory.decodeStreamExifInterface 를 반대로 배치하였을때는 BitmapFactory.decodeStream 에서 NullPointerException 이 발생하였습니다.

❓ 그러면 왜 BitmapFactory 에서만 IoException 이 발생하고 ExifInterface 에서는 발생하지 않았을까요?

이는 ExifInterface 클래스의 생성자에서 InputStream 객체를 사용하면서 이미 스트림이 끝까지 읽혀버렸기 때문에, BitmapFactory.decodeStream() 메서드에서 inputStream을 사용할 수 없게 되어 NullPointerException 예외가 발생합니다.

반면에 ExifInterface 클래스는 이미지 파일의 Exif 태그 정보를 읽는 클래스입니다. 이 클래스는 이미지 파일의 Exif 태그 정보가 저장된 위치를 찾아서 직접 데이터를 읽어오는데, 이 과정에서는 InputStream을 사용하지 않습니다. 즉, 객체를 생성할때만 사용하고, 태그 데이터를 읽어오는데는 사용하지 않는다는 뜻입니다.

어떻게 해결하였나요?

여러가지 방법이 있었지만 한번 가져온 이미지를 바이트화 시키고 바이트 스트림으로 변경하여 다시 업로드를 진행하였습니다.

val orientationStream = getFileToInputStream(uri)
val bitmapStream = getFileToInputStream(uri)
profile
안드로이드 개발자

0개의 댓글