본문 바로가기
TIL (Today I Learned)

TIL) Content type 'application/octet-stream' not supported 에러

by Won's log 2023. 9. 13.
  • 상황 : 프런트에서 AWS S3를 이용하여 게시물에 이미지를 업로드하고 생성할 때 백엔드에 게시물의 데이터를 전달하는 상황
  • 에러 상황: Content type 'application/octet-stream' not supported 에러
    • DefaultHandlerExceptionResolver : Resolved [org.springframework.web.HttpMediaTypeNotSupportedException: Content-Type 'application/octet-stream' is not supported]
  • 에러 이유: SpringBoot Controller의 핸들러 메서드에서 게시글에 필요한 requestDto 데이터와 이미지 저장을 위한 MultipartFile을 함께 사용시 발생한 오류

  • 포스트맨으로 사전 테스트 시, form-data 타입을 사용하여 이미지는 multipart/form-data으로, dto 데이터는 parameter에 담아 보내서 성공적으로 데이터를 전달하였다.

  • 그러나 프런트에서 ajax를 사용하여 백에 데이터를 전달할 때 Content-Type을 인식할 수 없어서 오류가 발생했고 이미지와 dto 데이터를 전달할 수 있도록 수정이 필요했다.
  • 시도 : image는 content type이 multipart/form-data 이고 dto는 content type이 application/json이기 때문에 contentType을 “contentType: false, *// Content-Type 자동 설정 비활성화”*로 고쳤으나 여전히 문제가 같았다.
  • 수정사항 : ajax에 데이터를 보내야하는 값은 하나였으나 image는 content type이 multipart/form-data 이고 dto는 content type이 application/json이기 때문에 둘을 합치는 로직이 필요했다. 그래서 이미지 업로드를 위한 imageFormData와 데이터를 JSON 형식으로 변환한 후 Blob으로 감싼 jsonDataBlob을 따로 생성했다. 그런 다음, 두 개의 FormData 객체를 합쳐서 formData에 추가하고 서버로 전송하였다. 이렇게 하면 이미지와 데이터를 각각 올바른 Content-Type으로 서버에 전송할 수 있게 되었고 결론적으로 게시물에 이미지가 잘 올라간 것을 확인할 수 있었다.
$(document).ready(function () {
    $('#savePost').click(function () {
        const title = $('#title').val();
        const category_id = $('#category').val();
        const content = $('#content').val();
        const imageFile = $('#image')[0].files[0];

        // 파일 크기 확인 (1MB 이상인 경우 처리)
        if (imageFile && imageFile.size > 1048576) { // 1MB 이상인 경우
            alert("파일 크기가 너무 큽니다. 1MB 보다 작은 파일을 선택해주세요.");
            return; // 업로드 중단
        }

        // 이미지 업로드를 위한 FormData 객체 생성
        const imageFormData = new FormData();
        imageFormData.append('image', imageFile);

        // 데이터를 JSON 형식으로 생성
        const jsonData = {
            title: title,
            category_id: category_id,
            content: content
        };

        // JSON 데이터를 문자열로 변환한 후 Blob으로 감싸기
        const jsonDataBlob = new Blob([JSON.stringify(jsonData)], { type: 'application/json' });

        // FormData 객체 생성하고 JSON 데이터 추가
        const dataFormData = new FormData();
        dataFormData.append('data', jsonDataBlob);

        // 두 개의 FormData 객체를 합치기
        const formData = new FormData();
        formData.append('image', imageFile);
        formData.append('data', jsonDataBlob);

        // 서버로 데이터 전송 (Ajax)
        $.ajax({
            type: 'POST',
            url: '/api/post',
            data: formData,
            contentType: false, // Content-Type 자동 설정 비활성화
            processData: false, // 데이터 처리 비활성화

            success: function (response) {
                // 성공 처리
                alert(response.message);
                window.location.href = '/view/posts';
            },
            error: function () {
                // 오류 처리
                alert('게시글 등록이 실패하였습니다');
            }
        });
    });
});