使用curl正确地POST multipart/form-data的方法是什么?

274
我使用以下语法上传文件及其它参数:
curl -v -include --form "key1=value1" --form upload=localfilename URL

文件大小约为500K。首先,在传输端我看到内容长度为254。稍后,服务器响应的内容长度为0。 我错在哪里了? 以下是该命令的完整跟踪记录。
* 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

可能是重复的问题:使用curl上传带有文件的POST数据 - Ciro Santilli OurBigBook.com
7个回答

396
以下语法可以为您解决此问题:
curl -v -F key1=value1 -F upload=@localfilename URL

Windows和curl.exe怎么样? - Piotr
4
多个附件怎么办? - hellboy
15
它在Windows上的工作方式与其他平台相同,并支持多个“附件”/文件:只需添加更多的“-F”实例即可! - Daniel Stenberg
1
这个答案提供了一个上传多个文件的好例子。 https://dev59.com/Smgu5IYBdhLWcg3wEzLP - bmoran
4
可以。在curl中,我们不需要像这样添加任何内容:-H 'content-type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW'。原文的意思已经被保留,而且内容更加通俗易懂。 - Emily
显示剩余3条评论

37

这是对我有效的方法

curl --form file='@filename' URL

看起来当我回答这个问题 (4年前) 时,我并没有真正理解问题或表单字段的工作方式。我只是根据我在不同场景中尝试过的内容回答,并且对我有效。

首先,OP 的唯一错误就是没有在文件名之前使用 @ 符号。其次,我的回答仅在我尝试上传的表单字段称为 file 时才能使用 file=...。如果您的表单字段被称为其他名称,则应使用该名称。

解释

curlman页面;在选项--form的描述下,它说:

这使得上传二进制文件等成为可能。要强制将'content'部分作为文件,请在文件名之前加上@符号。要仅从文件获取内容部分,请在文件名之前加上符号< 。然后 @ 和 < 之间的区别在于 @ 使文件作为文件上传附加在帖子中,而 < 使文本字段只从文件获取该文本字段的内容。

如果您正在尝试进行表单上传,很可能需要使用 @ 前缀来上传文件,而不是上传文件的内容的 <

补充说明

现在我必须补充说,必须小心使用 < 符号,因为在大多数 unix shell 中,< 是输入重定向符号 [巧合的是,在 < 之前,它还将文件的给定内容提供给程序的标准输入命令]。这意味着,如果您没有正确转义该符号或将其包装在引号中,则可能会发现您的 curl 命令的行为与您的预期不同。

在同一笔记上,我也建议引用 @ 符号。


您可能还对标题为:application/x-www-form-urlencoded or multipart/form-data? 的其他问题感兴趣。

我这样说是因为 curl 提供了其他上传文件的方法,但是它们在标题中设置的内容类型有所不同。例如,--data 选项提供了一种类似于上传文件作为数据的机制,但使用不同的内容类型进行上传。

无论如何,这就是我想说的关于这个答案的所有内容,因为它开始获得更多的投票。我希望这可以消除任何困惑,例如此答案与 接受 答案之间的区别。除了这个解释外,实际上没有其他区别。


29

在Windows中使用curl上传文件,我发现路径需要转义双引号

例如:

curl -v -F 'upload=@\"C:/myfile.txt\"' URL

4
这个答案帮助了我很多。尽管在我的情况下我不应该转义双引号 ", 在我的 Mac 上,我需要像这样发送它:curl -X POST -F key1=value1 -F 'image=@"/Users/ivkremer/Downloads/file name.jpg"' - ivkremer
在我的情况下 - 我成功传输了没有引号的内容: curl -v -F file=@/Users/path/to/file/testq.jpg http://192.168.0.101:8080/upload-image - chatlanin
在我的Windows环境中,我无法使用单引号,而必须像这样使用双引号:curl -F "filename=@\"C:\temp\file.jpg\"" https://someurl.com - Beems

4

我曾经试图使用 curl 向 Java 后端发送一个多部分(multipart)的 HTTP PUT 请求,但遇到了困难。

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

文件内容如下

-----------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--

但我总是收到一个错误,说边界不正确。通过一些Java后端调试,我发现Java实现会将\r\n--添加为边界的前缀,所以在更改输入文件后问题得到了解决。

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

一切正常!

简而言之

在多部分边界内容开头添加换行符(CRLF \r\n),并在边界开头添加--,然后再试一次。

也许您正在向需要这些更改的Java后端发送请求。


在使用Firefox Web控制台复制POST数据时,我还注意到它使用的是\n而不是\r\n。即使使用mitmproxy复制为cURL也是使用\n,因此我必须使用mitmproxy复制原始请求。我通过hexdump看到它使用的十六进制代码是0A而不是0D 0A - baptx
1
请查看 https://tools.ietf.org/html/rfc2046#section-5.1.1 第19页,\r\n 是必需的。 - Adham Zahran
  1. 这个后端是基于哪个Java后端框架的?
  2. 请您粘贴完整的工作curl命令。
- Sebastian Sastre
curl 命令不变,只是多部分输入文件中添加了 \r\n--。框架是 Spring Surf。 - Lusk116

1
在Windows 10中,使用PowerShell内的curl 7.28.1,我发现以下内容对我有效:
$filePath = "c:\temp\dir with spaces\myfile.wav"
$curlPath = ("myfilename=@" + $filePath)
curl -v -F $curlPath URL

0

使用Smartbear Zephyr Scale(服务器版),您可以按以下方式将文件附加到测试循环:

curl -H "Authorization: Basic YkskfdygyzghhMg==" -X POST -H "Content-Type: multipart/form-data" https://jira/jira/rest/atm/1.0/testrun/TDLT-C32/attachments --form file="@file2.txt"

(返回{"id":7099})

在file2.txt中:测试周期还链接到测试计划和问题。

{
  "projectKey": "TDD",
  "testPlanKey": "TDD-P1",
  "name": "Bonjour chez vous, Le Prisonnier",
  "issueKey": "TDLT-999"
  }

我发帖是因为在官方文档中找不到这个信息 :)


-4

或许这个表单可以工作

curl -X POST -d 'key1=value1&key2=value2' http://URL -H "Content-Type: application/x-www-form-urlencoded"


很明显,OP正在请求多部分表单数据,而不是应用程序url编码。 - edusanketdk

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接