달새 개발하다가 정말 이거로 3일 삽질 한 거 안 까먹고자 메모
개발 환경: Node.js

트위터에서는 GIF 파일을 15MB까지 지원 한 지 시간이 꽤 흘렀습니다.
물론 귀찮아서 기능을 넣지 않고 버티다 이번에 신규 개발을 하며 해당 기능을 넣고있는 상황입니다.
새로 나온 API는 API콜을 여러 차례로 나눠 보내야 하는데 마지막 FINALIZE에서 항상

Segments do not add up to provided total file size

이 에러가 떨어졌다 왜지 전송 데이터 bytes를 체크 해봐도 정상적으로 보냈는데?
저와 같은 삽질을 하는 분을 한 분이라도 더 구원하기 위해 이 글을 작성 합니다.


대용량 이미지 전송을 합시다

이미지 전송 제한

기본적으로 15MB를 넘지 않으면 되며
GIF의 가로, 세로, frame수 등도 영향이 있기는 합니다.
공식 문서 참고 바랍니다.

developer.twitter.com/en/docs/twitter-api/v1/media/upload-media/uploading-media/media-best-practices

새로운 이미지 전송

  • API 요청을 많이 해야 합니다.
  1. 이미지 전송 요청
  2. 이미지 분할 전송(1/3)
  3. 이미지 분할 전송(2/3)
  4. 이미지 분할 전송(3/3)
  5. 이미지 전송 완료 요청
  6. 이미지 처리 확인

이미지 전송 요청(INIT)

URL

upload.twitter.com/1.1/media/upload.json
모든요청의 URL은 동일합니다

메소드

  • POST

파라메터

  • command (필수)
    • "INIT" 고정값
  • total_bytes(필수)
    • 123456
  • media_type(필수)
    • image/gif MIME 타입을 적습니다.
  • media_category(필수 아님)
    • TweetImage, TweetVideo, TweetGif, DmImage, DmVideo, DmGif, Subtitles를 사용

요청 시 주의 할 점

  • 바디에 데이터를 넣지 말 것
  • 파라메터는 QueryParam으로 보낼 것
    • 바디에 넣어도 성공 하는 걸 확인은 했습니다만 언제 사양이 바뀔지 모르니 QueryParam으로 보냅시다.
  • OAuth 계산은 media, media_data를 제외하고 모두 계산에 포함 해야합니다.

리스폰스

{
  "media_id": 710511363345354753,
  "media_id_string": "710511363345354753",
  "size": 11065,
  "expires_after_secs": 86400,
  "image": {
    "image_type": "image/jpeg",
    "w": 800,
    "h": 320
  }
}

리스폰스에서 필요한 값

  • media_id 혹은 media_id_string
  • media_id_string는 8바이트 숫자도 표현 못하는 우리 똥같은 JS에서 써야 합니다.

이미지 전송 요청(APPEND) 이부분이 정말 중요합니다

URL

upload.twitter.com/1.1/media/upload.json

메소드

  • POST

파라메터

  • command (필수)
    • "APPEND" 고정값
  • media_id(필수), INIT에서 받은 media_id_string 값입니다.
    • 710511363345354753
  • media(필수)
    • 하지만 우리는 이걸 쓰지 않습니다.
  • media_data(필수)
    • 이미지를 여기에 할당 합니다.
  • segment_index(필수)
    • 이미지를 쪼갠 index입니다. 0~999를 사용하며 0 시작입니다.

media_data에 데이터 할당하기

이부분이 중요합니다.

  • 이미지를 Base64로 읽은 값을 예시로 들겠습니다.
    data:image/png;base64,이미지문자열
    읽은 Base64 값은 이렇게 구성 되어 있을 겁니다.
    우리가 보내야 할 건 data:image/png;base64,를 제외 한 이미지문자열 항목입니다.
  1. 이미지문자열 을 바이너리(Blob)로 변환합니다.이미지문자열 은 abcdef로 변환 되었습니다.
  2. const strBinary = atob("이미지문자열");
  3. 변환 된 바이너리를 쪼개기
  • abcdef 항목을 3개로 쪼갭니다. [ab, cd, ef]
  1. 쪼갠 바이너리를 Base64로 변환
  • [12, 34, 56] 이렇게 바뀌었습니다.

이미지 전송

  • INIT과 마찬가지로 QueryParam을 보냅니다. 단, media & media_data는 제외
  • segment_index 쪼갠 데이터 인덱스 순서에 주의!
  • 이미지 데이터는 FormData에 할당 후 보냅니다.
  • const body = new FormData(); body.append('media_data', 12);
  • OAuth 계산은 media, media_data를 제외하고 모두 계산에 포함 해야합니다.

리스폰스

  • 없음. 성공 시 HTTP StatusCode가 204로 응답이 옵니다.
  • 실패할 경우에만 리스폰스에 에러 메시지가 있습니다.

이미지 전송 완료 요청(FINALIZE)

이미지 다 보냈습니다 이미지 합쳐주세요.

URL

upload.twitter.com/1.1/media/upload.json
모든요청의 URL은 동일합니다

메소드

  • POST

파라메터

  • command (필수)
    • "FINALIZE" 고정값
  • media_id(필수), INIT에서 받은 media_id_string 값입니다.
    • 710511363345354753

요청 시 주의 할 점

  • 바디에 데이터를 넣지 말 것
  • 파라메터는 QueryParam으로 보낼 것
    • 바디에 넣어도 성공 하는 걸 확인은 했습니다만 언제 사양이 바뀔지 모르니 QueryParam으로 보냅시다.
  • OAuth 계산은 media, media_data를 제외하고 모두 계산에 포함 해야합니다.

리스폰스

// Example of sync FINALIZE response
{
  "media_id": 710511363345354753,
  "media_id_string": "710511363345354753",
  "size": 11065,
  "expires_after_secs": 86400,
  "video": {
    "video_type": "video/mp4"
  }
}

// Example of async FINALIZE response which requires further STATUS command call(s)
{
  "media_id": 710511363345354753,
  "media_id_string": "710511363345354753",
  "expires_after_secs": 86400,
  "size": 10240,
  "processing_info": {
    "state": "pending",
    "check_after_secs": 5 // check after 5 seconds for update using STATUS command
  }
}

우린 쿨합니다. 리스폰스에서 에러만 안 났다면 media_id를 담아 트윗을 전송합니다.

  • STATUS라는 별도의 API가 존재를 합니다.
    동영상 처리처럼 오래 걸리는 작업이 정상적으로 끝났는지를 확인하는 API입니다.
    별도의 확인 절차를 거치실 분은

이 글을 보고 누군가 삽질 하지 않고 구원 받길 기도하며...

사실 base64, blob변환 왜 저렇게 해야 되나 싶긴한데 서버에 base64 문자 자체를 보내는 방법이 있을 거 같긴 하지만...
정상 동작 하면 그만이죠
base64랑 blob이랑 읽는 bit수 자리가 다르다보니
서버에서 이미지 해석을 정상적으로 하지 못해 생기는 문제가 아닐까 싶습니다.


 

트위터 API글답게 블로그 댓글은 잘 안 보고 멘션은 잘 봅니다.
문의사항은 트위터로 와서 해주세요
twitter.com/hanalen_

+ Recent posts