194

I used this syntax to post a file along with some parameters:

curl -v -include --form "key1=value1" --form upload=localfilename URL

The file is around 500K in size. First of all, I see content length to be 254 on the transmit side. Later the server response's content length is 0. Where am I going wrong?

Here is the complete trace of the command.

* Couldn't find host xxx.xxx.xxx.xxx in the _netrc file; using defaults
* About to connect() to xxx.xxx.xxx.xxx port yyyy (#0)
*   Trying xxx.xxx.xxx.xxx...
* Adding handle: conn: 0x4b96a0
* Adding handle: send: 0
* Adding handle: recv: 0
* Curl_addHandleToPipeline: length: 1
* - Conn 0 (0x4b96a0) send_pipe: 1, recv_pipe: 0
* Connected to xxx.xxx.xxx.xxx (xxx.xxx.xxx.xxx) port yyyy (#0)
* POST /zzzzzz/UploadFile HTTP/1.1
* User-Agent: curl/7.32.0
* Host: xxx.xxx.xxx.xxx:yyyy
* Accept: */*
* Content-Length: 254
* Expect: 100-continue
* Content-Type: multipart/form-data; boundary=------------------------948a6137eef50079
*
* HTTP/1.1 100 Continue
* HTTP/1.1 100 Continue

* HTTP/1.1 200 OK
* HTTP/1.1 200 OK
* Server Apache-Coyote/1.1 is not blacklisted
* Server: Apache-Coyote/1.1
* Server: Apache-Coyote/1.1
* Added cookie JSESSIONID="C1D7DD042E250211D9DEA82688876F88" for domain xxx.xxx.xxx.xxx, path /zzzzz/, expire 0
* Set-Cookie: JSESSIONID=C1D7DD042E250211D9DEA82688876F88; Path=/zzzzzz/;
* HttpOnly
* Set-Cookie: JSESSIONID=C1D7DD042E250211D9DEA82688876F88; Path=/zzzzzz/; HttpOnly
* Content-Type: text/html;charset=ISO-8859-1
Content-Type: text/html;charset=ISO-8859-1
* Content-Length: 0
* Content-Length: 0
* Date: Tue, 01 Oct 2013 11:54:24 GMT
* Date: Tue, 01 Oct 2013 11:54:24 GMT
* Connection #0 to host xxx.xxx.xxx.xxx left intact
Max
  • 1,051
  • 1
  • 12
  • 20
Kamalakshi
  • 5,176
  • 3
  • 14
  • 20

5 Answers5

290

The following syntax fixes it for you:

curl -v -F key1=value1 -F upload=@localfilename URL
evandrix
  • 5,608
  • 4
  • 25
  • 33
Kamalakshi
  • 5,176
  • 3
  • 14
  • 20
  • what about Windows & curl.exe ? – Piotr Jul 01 '15 at 23:35
  • 2
    what about multiple attachments? – hellboy Nov 03 '15 at 12:06
  • 11
    It works exactly the same on Windows and.it supports multiple "attachments"/files: just add more -F instances! – Daniel Stenberg Mar 10 '16 at 08:33
  • 1
    This answer has a good example of uploading multiple files. http://stackoverflow.com/questions/11599957/upload-multiple-files-to-php-server-using-curl-command-line – bmoran Mar 17 '17 at 18:13
  • 2
    This works. on curl we do not need to add something like this: `-H 'content-type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW' ` – Emily May 07 '19 at 18:07
23

to upload a file using curl in Windows I found that the path requires escaped double quotes

e.g.

curl -v -F 'upload=@\"C:/myfile.txt\"' URL
Logan Scott
  • 231
  • 2
  • 3
  • 3
    This answer helped me a lot. Though in my case I should not escape `"`, that is in my case on Mac I should send it like `curl -X POST -F key1=value1 -F 'image=@"/Users/ivkremer/Downloads/file name.jpg"'`; – ivkremer May 31 '19 at 17:43
  • in my case - I had a success transfer without any quotes : curl -v -F file=@/Users/path/to/file/testq.jpg http://192.168.0.101:8080/upload-image – chatlanin Apr 21 '20 at 11:42
  • In my case in Windows, I could not use the single quotes and had to use double quotes like this `curl -F "filename=@\"C:\temp\file.jpg\"" https://someurl.com` – Beems May 24 '20 at 23:21
10

This is what worked for me

curl --form file='@filename' URL

It seems when I gave this answer (4+ years ago), I didn't really understand the question, or how form fields worked. I was just answering based on what I had tried in a difference scenario, and it worked for me.

So firstly, the only mistake the OP made was in not using the @ symbol before the file name. Secondly, my answer which uses file=... only worked for me because the form field I was trying to do the upload for was called file. If your form field is called something else, use that name instead.

Explanation

From the curl manpages; under the description for the option --form it says:

This enables uploading of binary files etc. To force the 'content' part to be a file, prefix the file name with an @ sign. To just get the content part from a file, prefix the file name with the symbol <. The difference between @ and < is then that @ makes a file get attached in the post as a file upload, while the < makes a text field and just get the contents for that text field from a file.

Chances are that if you are trying to do a form upload, you will most likely want to use the @ prefix to upload the file rather than < which uploads the contents of the file.

Addendum

Now I must also add that one must be careful with using the < symbol because in most unix shells, < is the input redirection symbol [which coincidentally will also supply the contents of the given file to the command standard input of the program before <]. This means that if you do not properly escape that symbol or wrap it in quotes, you may find that your curl command does not behave the way you expect.

On that same note, I will also recommend quoting the @ symbol.


You may also be interested in this other question titled: application/x-www-form-urlencoded or multipart/form-data?

I say this because curl offers other ways of uploading a file, but they differ in the content-type set in the header. For example the --data option offers a similar mechanism for uploading files as data, but uses a different content-type for the upload.

Anyways that's all I wanted to say about this answer since it started to get more upvotes. I hope this helps erase any confusions such as the difference between this answer and the accepted answer. There is really none, except for this explanation.

smac89
  • 26,360
  • 11
  • 91
  • 124
2

I had a hard time sending a multipart HTTP PUT request with curl to a Java backend. I simply tried

curl -X PUT URL \
   --header 'Content-Type: multipart/form-data; boundary=---------BOUNDARY' \
   --data-binary @file

and the content of the file was

-----------BOUNDARY
Content-Disposition: form-data; name="name1"
Content-Type: application/xml;version=1.0;charset=UTF-8

<xml>content</xml>
-----------BOUNDARY
Content-Disposition: form-data; name="name2"
Content-Type: text/plain

content
-----------BOUNDARY--

but I always got an error that the boundary was incorrect. After some Java backend debugging I found out that the Java implementation was adding a \r\n-- as a prefix to the boundary, so after changing my input file to

                          <-- here's the CRLF
-------------BOUNDARY       <-- added '--' at the beginning
...
-------------BOUNDARY       <-- added '--' at the beginning
...
-------------BOUNDARY--     <-- added '--' at the beginning

everything works fine!

tl;dr

Add a newline (CRLF \r\n) at the beginning of the multipart boundary content and -- at the beginning of the boundaries and try again.

Maybe you are sending a request to a Java backend that needs this changes in the boundary.

Community
  • 1
  • 1
Lusk116
  • 470
  • 4
  • 15
  • When copying POST data with Firefox web console, I also noticed that it was using `\n` instead of `\r\n`. Even mitmproxy copy as cURL was using `\n` so I had to copy the raw request with mitmproxy. I saw with hexdump that it was using hex code `0A` instead of `0D 0A`. – baptx Jul 05 '19 at 18:31
  • 1
    the `\r\n` is required. Look at https://tools.ietf.org/html/rfc2046#section-5.1.1 page 19. – Adam Zahran Mar 17 '20 at 20:55
1

On Windows 10, curl 7.28.1 within powershell, I found the following to work for me:

$filePath = "c:\temp\dir with spaces\myfile.wav"
$curlPath = ("myfilename=@" + $filePath)
curl -v -F $curlPath URL
Matt
  • 217
  • 1
  • 3