如何在不使用“-F”选项的情况下重写此CURL多部分/表单数据请求?

45

我该如何改写以下 CURL 命令,以使其不使用 -F 选项,但仍然生成完全相同的 HTTP 请求? 即使它直接将 multipart/form-data 传递到请求正文中。

curl -X POST -F example=test http://localhost:3000/test
7个回答

76

解决:

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

假设 test.txt 包含以下文本,并且最重要的是具有 CRLF (\r\n) 行结尾符

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

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

注意:使用--data-binary而不是老式的-d非常重要,因为前者会保留换行符(这非常重要)。 此外,请注意正文中的边界以额外的--开头。

我要重复一遍,因为它非常重要,但请求体文件必须具有CRLF换行符。一个跨平台文本编辑器,带有良好的换行支持是jEdit(如何在jEdit中设置行尾)。

如果您想了解我是如何解决问题的(通过Ruby on Rails应用程序进行调试),而不仅仅是最终解决方案,请查看我的博客,我已经详细记录了我的调试步骤。


3
干得好,先生。我连续花了4小时才弄清楚你告诉我行尾需要是CRLF格式。非常感谢。 - Tim Fletcher
Tim,不用谢。这个问题困扰了我很久。RFC中有关键信息http://tools.ietf.org/html/rfc2046(搜索“CRLF”)。更糟糕的是,如果你使用“-d”,curl实际上会改变行尾! - William Denniss
为什么在Unix中必须使用CRLF作为行结尾符?感谢您指出这一点。 - Camilo Martin
我刚刚注意到,在你的 test.txt 示例中,你有一个尾随的 --,这是故意的吗? - Camilo Martin
1
@CamiloMartin 是的,请参见http://tools.ietf.org/html/rfc2046#section-5.1.1,特别是文本`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.` 您只需要在最后一个项目上执行此操作。 - William Denniss
显示剩余4条评论

34
你可以使用--form参数并显式地
curl -H "Content-Type: multipart/related" \
  --form "data=@example.jpg;type=image/jpeg" http://localhost:3000/test

4
谢谢,但我特别希望传递原始数据进行测试,以便我能够理解它,然后在非基于curl的程序中使用它。 - William Denniss
5
谢谢, "type=..." 对我帮助很大! - Hemeroc
这应该是被接受的答案,因为它允许只使用curl命令行界面功能来完成此操作,而无需手动构建多部分请求。 - ixi
这应该是被接受的答案,因为它允许使用curl命令行界面功能来完成,而无需手动构建多部分请求。 - undefined

16

这里有一个使用-d将原始CURL语句重写为单行的替代答案,而不需要临时文件。个人认为使用临时文件的方法更容易理解,但我也把它放在这里供参考:

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

注意:使用$'blar'语法是为了让bash将\r\n解析为CRLF标记。感谢此答案提供的提示。


谢谢。还有需要注意的是,除非您还传递具有静态边界值的原始数据,否则Content-Type标头不应指定边界值。在设置边界值的同时使用-F key=value参数可能会使curl后缀出现第二个boundary=值,这显然是不打算的。 - Jeff R

5
这是我正在使用的,我认为它很干净,不需要临时文件,也不会在上传整个文件时占用大量内存(因此不会将文件读入内存中)。
# 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 @-)"

1

这是使用 "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'

0

这是我会怎么做:

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
'

或者更加复杂一些(应该可以在大多数现代shell中使用):

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
'

-3
这是用于multipart/form-data请求方法的。如果要上传文件,请添加--form filename="@path/image.jpg;type=image/jpeg"
curl --form key="value" --form key="value" http://localhost:3000/test

这并没有回答问题,因为-F--form是同一个选项;在手册页面中是这样写的:-F, --form <name=content> - J.J. Hakala

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