Upload a file with multipart/form-data

`multipart/form-data`로 파일과 텍스트 필드를 함께 업로드하는 레시피입니다. 브라우저 파일 업로드 폼과 같은 방식입니다.

개요

`multipart/form-data`로 파일과 텍스트 필드를 함께 업로드하는 레시피입니다. 브라우저 파일 업로드 폼과 같은 방식입니다.

코드로 보기

curl -X POST https://api.example.com/upload \
  -F 'file=@./photo.jpg;type=image/jpeg' \
  -F 'title=My photo'
const form = new FormData();
form.append('file', fileInput.files[0]);
form.append('title', 'My photo');

await fetch('https://api.example.com/upload', {
  method: 'POST',
  body: form // do NOT set Content-Type manually
});
import requests

with open('photo.jpg', 'rb') as f:
    res = requests.post('https://api.example.com/upload',
                        files={'file': ('photo.jpg', f, 'image/jpeg')},
                        data={'title': 'My photo'})
res.raise_for_status()

설명

curl의 `-F`는 각 필드를 파트로 만들고 `@`는 파일 내용을 실제로 첨부합니다(`<`는 파일 내용을 필드 값으로만 넣음). `;type=`으로 파트별 Content-Type을, `filename=`으로 전송 파일명을 지정할 수 있습니다.

핵심 함정: Content-Type 헤더를 직접 설정하지 마세요. multipart는 각 파트를 나누는 랜덤 `boundary` 문자열을 포함해야 하는데, `fetch`에 `FormData`를 주거나 `requests`에 `files=`를 주면 라이브러리가 boundary를 계산해 헤더를 자동으로 채웁니다. 손으로 `Content-Type: multipart/form-data`만 넣으면 boundary가 빠져 서버가 파싱에 실패합니다.

큰 파일은 서버 제한을 초과하면 413 Payload Too Large, 허용되지 않는 형식이면 415 Unsupported Media Type이 납니다. 매우 큰 파일은 청크·재개(Range) 업로드나 스토리지의 presigned URL 직접 업로드를 고려하세요.

관련 헤더

관련 상태코드