IT/PHP

[EXIF] 이미지 회전 문제 Javascript -> PHP 로 보낸 blob 에서 EXIF 데이터 추출하기

월공 2022. 11. 22. 14:40
728x90
300x250

이미지 회전되는 이슈가 있어서 이미지 등록이 진행되는 부분에서 EXIF 값을 받아와야할일이 있었는데
계속 못받아오네 ?
javascript 에서 FileReader 를 이용해가지고 ajax 방식으로 blob 데이터를 php 쪽으로 보내는데 왜 못받아오는지 이해가 안갔다

우선 javascript 부분에서 대충 아래와 같이 대충 action.php 에다가 ajax 방식으로 blob 값 보내주는거 있다고 치자

..중략

imageLoader = function(file,num) {

    sel_files.push(file);
    var reader = new FileReader();				

    reader.onload = function(ee) {        
        $.ajax({
            type: 'POST',
            url: '/action.php',
            data: $('#formData').serialize() + '&filedata='+ee.target.result+'&filename='+file.name,
            cache:false,global:false,async:true,
            dataType: 'json',
            beforeSend: function() {
                $('#loading').show(); 
            },
            success: function(data) {
                console.log(data);
                if(data.error == 'T') {
                    console.error(data.msg);
                } else {
                    console.log('처리완료');								
                }
                $('#loading').hide();
            }
        });					
    }
    reader.readAsDataURL(file);
}

..중략

참고로 EXIF 이용해서 javascript 단에서 orientation 확인하고싶으면 아래 소스 사용하면됨
위에꺼랑 아래꺼 적당히 믹스해서 쓰면 된다.
근데 나한테 문제는 이게 아니었다.

<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/exif-js"></script>

<script>
    EXIF.getData(file, () => {
        const orientation = EXIF.getTag( file, "Orientation" );						
        switch( orientation ) {
            // 이미지 회전값이 0인경우 ( 정방향 )
            case 1 :
                document.getElementById( "thumbnailImg" ).style.transform = "rotate( 0deg )";
                break;

            // 이미지 90도 회전 ( 오른쪽으로 270도 회전해야됨 )
            case 6 :
                document.getElementById( "thumbnailImg" ).style.transform = "rotate( 270deg )";
                break;

            // 이미지 180도 회전 ( 오른쪽으로 180도 회전해야됨 )
            case 3 :
                document.getElementById( "thumbnailImg" ).style.transform = "rotate( 180deg )";
                break;

            // 이미지 270도 회전 ( 오른쪽으로 90도 회전해야됨 )
            case 8 :
                document.getElementById( "thumbnailImg" ).style.transform = "rotate( 90deg )";
                break;
        }						
    });
</script>

action.php 에 blob 값이 분명히 잘 넘어왔는데 도대체 왜 PHP 단에서 exif 값이 안보이는건지 도무지 이해가 가지않았다.
이거 때문에 별 삽질을 다해봤는데 우선 직접 이미지를 로컬에서 blob 값을 추출해보기도 해봤다.

//참고로 아래처럼 넣으면 파일 인식 못해서 없다고 오류남, 꼭 url 로 넣어줘야됨 ㅋㅋ
//$blob = addslashes(file_get_contents('./KakaoTalk_20221103_173624744.png'));

/*
아래처럼 넣어주면 blob 값을 잘 갖고온다. 근데 addslashes 는 굳이 필요없어서 안해줘도됨
addslashes 는 문자열안에 mysql 이 뻑날수 있는 홀따옴표나 쌍따옴표 등 이 포함되있으면 앞에다
역슬래시 추가시켜주는 놈이다.
반대로 stripslashes 라는 놈도 있다.
*/
// $blob = addslashes(file_get_contents('http://localhost/images/142825_20220907154304_5919.jpg'));

// 이미지 파일에서 blob 데이터 추출
$blob = file_get_contents('http://localhost/images/142825_20220907154304_5919.jpg');

$exif = exif_read_data("data://image/jpeg;base64," . base64_encode($blob));

// 이미지 회전값 추출
echo $exif['Orientation'];

이러면 잘 가져온다.
근데 왜 넘긴 php 페이지에선 같은 이미지인데도 불구하고 orientation 을 못받아올까 ??

도저히 이해가 안가서 심지어 두개 파일 length 비교까지 해봤는데 똑같더라 ㅋㅋㅋㅋ

//로컬 파일 blob 길이
echo strlen(base64_encode(file_get_contents('http://localhost/images/142825_20220907154304_5919.jpg')));


//javascript 에서 추출해서 넘긴 blob 길이
echo strlen($data['blob']);

눈으로 비교는 할수가없다 blob 파일 봤으면 알다시피 엄청나게 길다 ..
괜히 잘못 불러왔다가 에디터 뻑날정도로 길다.

결국 javascript 에서 넘긴거랑 PHP 자체에서 만든 blob 파일이랑 sublime text 에서 비교를 해봄
오죽하면 이렇게까지 했겠어 ~
여튼 보다보니 다른점이 보인다.

javascript 에서는 19OleV E/C91a HriFL5 이런식으로 넘겨준다고 치면
PHP 에서는 19OleV+E/C91a+HriFL5 이런식으로 넘겨진다.

결국 javascript 로 넘겨진 blob 데이터 안의 내용물의 빈칸을 + 로 replace 해주면 되겠다는 생각이 들었다.

//$data['mime'] 에는 'image/jpeg' , 'image/png' 같은 값들이 들어간다. 알아서 추출해서 넣어주면됨
$exif = exif_read_data("data://".$data['mime'].";base64," . str_replace(" ","+", $data['blob']));

echo $exif['Orientation'] . '아니 이게 뭐라고 ';
exit;


이렇게 해주니 javascript 에서 넘겨준 blob 데이터에서도 exif 데이터를 잘 추출해오는것을 확인할수 있었다.
되게 별거 아닌데 나를 좀 상당히 빡치게 해서 기록 남겨둔다..

이제 받아오는 데이터 기준으로 Orientation 값 추출 할수있으니 내 입맛에 맞게 rotate 시켜주면 되겠다~

if(!empty($exif['Orientation']))
{
    switch($exif['Orientation'])
    {
        case 8:
            $image = imagerotate($image,90,0);
        break;
        case 3:
            $image = imagerotate($image,180,0);
        break;
        case 6:
            $image = imagerotate($image,-90,0);
        break;
    }
}
header('Content-type: image/jpeg');
imagejpeg($image);
imagedestroy($image);

아이씨 이거 때문에 개빡쳤네


추가적으로 더 덧붙힘

이미지 오리값도 가져왔겠다 이제 그걸 바꿔서 다시 설정해줘야하는데 어떻게 할까 ?
보아하니 imagejpeg 이라는 함수가 바뀐 이미지를 저장해주는 역할을 해준다.
그럼 아래 처럼 되지않을까 ??

if($exif['Orientation']) {

    $source = imagecreatefromjpeg("data://".$data['mime'].";base64," . str_replace(" ","+", $data['blob']));

    switch($exif['Orientation']){
        case 8 : $source = imagerotate($source,90,0); break;
        case 3 : $source = imagerotate($source,-90,0); break; //기존에 -180 이었음
        case 6 : $source = imagerotate($source,-90,0); break;
    }
	
    //이미지 임시로 저장할 폴더 (회원번호별 폴더 생성)
    $path = "./img/temp/".$data['id'];

	//해당 폴더 존재하지않으면 만듬
    if (!is_dir($path)) {
        mkdir($path, 0777, true);
    }

	//거기다 저장해줌
    imagejpeg($source , $path.'/'.$_REQUEST['filename']);						

	//거기에 저장해준걸 다시 가져와서 blob 데이터로 변환함
    $data['blob'] = base64_encode(file_get_contents('http://localhost/images/'.$data['uid'].'/'.$_REQUEST['filename']));
}

근데 뭔가 상당히 비효율적이다.
이미지를 굳이 임시로라도 저장을 해야하나 ? 난 저장하기 싫은데 ..?
저장했다 쳐도 저거 나중에 또 날려줘야되잖아

그래서 좀 더 찾아보니 더 좋은 방법이 있더라
이미지 오리값 조정한거로 다시 저장하고 폴더 생성이고 뭐고 다 필요없고, 아래처럼 해주면 끝남

ob_start();
imagejpeg($source);
$i = ob_get_clean(); 

$data['blob'] = base64_encode($i);

간단하게 정리하면 ob_start() 라는 녀석이 출력 버퍼링을 켜는 명령어이고, 이게 켜져있는동안 헤더를 제외한 모든 출력을 내부 버퍼에 저장하고 실제 전송을 하지않는다고한다.
그리고 ob_get_clean 이라는놈이 출력 버퍼의 내용을 반환하고 , 버퍼링을 종료해서 저 $i 에 blob 값이 담긴다.

728x90
300x250