`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 직접 업로드를 고려하세요.