39

How can I rewrite the following CURL command, so that it doesn't use the -F option, but still generates the exact same HTTP request? i.e. so that it passes the multipart/form-data in the body directly.

curl -X POST -F example=test http://localhost:3000/test
William Denniss
  • 14,938
  • 5
  • 76
  • 117

7 Answers7

70

Solved:

curl \
  -X POST \
  -H "Content-Type: multipart/form-data; boundary=----------------------------4ebf00fbcf09" \
  --data-binary @test.txt \
  http://localhost:3000/test

Where test.txt contains the following text, and most importantly has CRLF (\r\n) line endings:

------------------------------4ebf00fbcf09
Content-Disposition: form-data; name="example"

test
------------------------------4ebf00fbcf09--

Notes: it is important to use --data-binary instead of plain old -d as the former preserves the line endings (which are very important). Also, note that the boundary in the body starts with an extra --.

I'm going to repeat it because it's so important, but that request-body file must have CRLF line endings. A multi-platform text editor with good line-ending support is jEdit (how to set the line endings in jEdit).

If you're interested in how I worked this out (debugging with a Ruby on Rails app) and not just the final solution, I wrote up my debugging steps on my blog.

jwfearn
  • 26,394
  • 26
  • 89
  • 117
William Denniss
  • 14,938
  • 5
  • 76
  • 117
  • 3
    Well done, sir. It took me 4 hours straight to get to you telling me the line endings need to be CRLF. Thanks so much. – Tim Fletcher Mar 28 '13 at 19:29
  • Tim, you're welcome. This puzzled me for ages. The RFC has the key http://tools.ietf.org/html/rfc2046 (search 'CRLF'). The worse part is that curl will actually munge the line endings if you use `-d`! – William Denniss Jun 05 '13 at 07:00
  • Why for the love of Unix must the line endings be CRLFs?! Thanks for pointing this out. – Camilo Martin Feb 22 '16 at 01:38
  • Also I just noticed, in your `test.txt` example you have a trailing `--`, is it on purpose? – Camilo Martin Feb 22 '16 at 20:01
  • 1
    @CamiloMartin yes, see http://tools.ietf.org/html/rfc2046#section-5.1.1, specifically the text `The boundary delimiter line following the last body part is a distinguished delimiter that indicates that no further body parts will follow. Such a delimiter line is identical to the previous delimiter lines, with the addition of two more hyphens after the boundary parameter value.` You only need to do this on the *last* item. – William Denniss Feb 22 '16 at 20:14
  • @WilliamDenniss Thanks! You've cleared up many things nicely in this whole Q&A. – Camilo Martin Feb 23 '16 at 00:57
  • both links are broken – Michel Ayres Aug 25 '16 at 14:56
  • What would this look like if instead of plain text we wanted to POST an image? – Luis Chaves Rodriguez Jan 17 '21 at 23:17
30

You can use the --form argument with an explicitly

curl -H "Content-Type: multipart/related" \
  --form "data=@example.jpg;type=image/jpeg" http://localhost:3000/test
mimming
  • 13,706
  • 3
  • 39
  • 73
16

Here's an alternative answer with the original CURL statement re-written using -d as a one-liner, without temporary files. Personally I think the temporary files approach is easier to understand, but I'm putting this here for reference as well:

curl -X POST -H "Content-Type: multipart/form-data; boundary=----------------------------4ebf00fbcf09" -d $'------------------------------4ebf00fbcf09\r\nContent-Disposition: form-data; name="example"\r\n\r\ntest\r\n------------------------------4ebf00fbcf09--\r\n' http://localhost:3000/test

Notes: the $'blar' syntax is so that bash will parse the \r\n as a CRLF token. Thanks to this answer for that tip.

Community
  • 1
  • 1
William Denniss
  • 14,938
  • 5
  • 76
  • 117
5

This is what I'm using, I think it's clean and doesn't need temporary files nor gobbles up RAM in case you want to upload whole files (so no reading files into memory).

# Set these two.
file='path/to/yourfile.ext'
url='http://endpoint.example.com/foo/bar'

delim="-----MultipartDelimeter$$$RANDOM$RANDOM$RANDOM"
nl=$'\r\n'
mime="$(file -b --mime-type "$file")"

# This is the "body" of the request.
data() {
    # Also make sure to set the fields you need.
    printf %s "--$delim${nl}Content-Disposition: form-data; name=\"userfile\"${nl}Content-Type: $mime$nl$nl"
    cat "$file"
    printf %s "$nl--$delim--$nl"
}

# You can later grep this, or something.
response="$(data | curl -# "$url" -H "content-type: multipart/form-data; boundary=$delim" --data-binary @-)"
Camilo Martin
  • 34,128
  • 20
  • 104
  • 150
1

This is to upload one image file using "Content-Type: multipart/related",

curl --trace trace.txt -X POST -H 'Content-Type: multipart/related; boundary=boundary_1234' --data-binary $'--boundary_1234\r\nContent-Type: application/json; charset=UTF-8\r\n\r\n{\r\n\t"title": "TestFile"\r\n}\r\n\r\n--boundary_1234\r\nContent-Type: image/jpeg\r\n\r\n' --data-binary '@Image0177.jpg' --data-binary $'\r\n--boundary_1234--\r\n' 'http://localhost:3000/google/upload/drive/v2/files?uploadType=multipart'
Mohan R
  • 91
  • 1
  • 2
0

Here it is how I would do it:

curl https://httpbin.org/post \
    -H 'content-type: multipart/form-data; boundary=----FormBoundary123456789' \
    --data-binary $'------FormBoundary123456789\r
Content-Disposition: form-data; name="example"\r
\r
test\r
------FormBoundary123456789--\r
'

Or a bit more sophisticated (should be portable to most modern shells):

DELIM=----FormBoundary$RANDOM$RANDOM

curl https://httpbin.org/post \
    -H "content-type: multipart/form-data; boundary=$DELIM" \
    --data-binary --$DELIM$'\r
Content-Disposition: form-data; name="example"\r
\r
test\r
'--$DELIM--$'\r
'
Eduardo
  • 1
  • 3
-3

This is for multipart/form-data request method. for uploading a file add --form filename="@path/image.jpg;type=image/jpeg"

curl --form key="value" --form key="value" http://localhost:3000/test

Sanjeev Kumar
  • 437
  • 1
  • 4
  • 10
  • This doesn't answer the question, since `-F` and `--form` are the same option; in the manual page `-F, --form `. – J.J. Hakala Jan 19 '16 at 11:24