curl 发送文件后换行丢失,Content-Length 小于文件大小
curl 使用 --data @文件 时会去掉回车、换行和空字节。保留请求体原始内容应使用 --data-binary,并核对媒体类型、原始字节及接口处理结果。
用 curl 把文件作为请求体发送,接口收到的内容少了换行,日志里的 Content-Length 也比本地文件字节数小。即使已经声明 JSON 类型,下面这种写法仍会处理文件中的换行:
curl -H 'Content-Type: application/json' \
--data @payload.json \
https://api.example.com/ingest
这里的地址和文件名都是示例。实际检查应使用自己控制的测试接口与不含凭据的样本,不要把真实业务数据交给公共回显服务。
问题出在 --data @文件 的读取规则:curl 会去掉文件中的回车、换行和空字节。Content-Type 只声明请求体的媒体类型,不会改变这一读取规则。把同样的内容直接放进命令行字符串,和让 curl 从文件读取,处理方式并不完全相同。
如果需要按文件原有字节发送请求体,改用 --data-binary。
保留文件内容与媒体类型
对于 JSON 文件,可写为:
curl -H 'Content-Type: application/json' \
--data-binary @payload.json \
https://api.example.com/ingest
--data-binary 也可以发送文本文件,名字里的 binary 不代表只能发送图片或压缩包。它保留文件中的换行和回车,也不会按普通 --data @文件 的规则丢掉空字节。
媒体类型仍要显式设置。--data-binary 默认使用 application/x-www-form-urlencoded,不会因为文件后缀是 .json 就自动改成 JSON。若接口要求其他类型,应按该接口的请求格式设置,不能一律照搬上面的请求头。
上述数据选项默认使用 POST,无须另外加 -X POST。如果原接口要求 PUT 或其他方法,要保留实际的方法要求;此次调整只针对请求体的读取方式。
curl 7.82.0 及以上也可使用 JSON 专用简写:
curl --json @payload.json https://api.example.com/ingest
--json 相当于使用 --data-binary,再设置 Content-Type: application/json 和 Accept: application/json。后一个头会告诉服务端客户端希望接收 JSON,因此替换前也应确认响应格式要求。curl 不会替你验证文件是否是合法 JSON;参数被识别、请求被发出,不代表 JSON 语法正确。
从标准输入发送时,使用 @-
如果数据来自前一道程序,仍要在 curl 读取时保留字节:
curl -H 'Content-Type: application/json' \
--data-binary @- \
https://api.example.com/ingest < payload.json
@- 表示从标准输入读取,示例由 shell 把文件接到 stdin。这与 --data-binary @payload.json 的主要区别是文件由谁打开;两种写法都可保留请求体里的换行。
如果上游程序本来就删掉了换行,--data-binary 不会把它们补回来。应检查传到 curl 之前的实际内容,不能只检查最初那份源文件。
不要把 --data-raw @payload.json 当成替代办法。--data-raw 不把 @ 解释为读文件标记,这样发送的是字面文本 @payload.json,不是文件内容。
普通 JSON 空白与逐字节内容的区别
普通 JSON 文档在字符串之外的排版换行,通常不会改变解析后的字段和值。因此,文件被压成一行,不一定让 JSON 解析失败。但如果接口签名针对原始请求体计算,或接收方按换行分隔记录,即使字段看起来相同,字节变化也可能导致验签失败或记录边界丢失。
JSON 字符串中的转义序列 \n 则是反斜杠和字母组成的文本,不等同于文件里的真实换行字节。不要把这两种情况混为一谈,也不要为了凑回文件长度,随意向 JSON 字符串里插入真实换行。
如果要一次发送多个记录,按接口要求把完整请求体准备在一个文件里,再使用一次 --data-binary。多次使用普通数据选项可能引入连接符,不能假设下面这种形式会把两个文件直接按原样拼接:
curl --data-binary @part1.txt --data-binary @part2.txt \
https://api.example.com/ingest
这是需要避免的拼接示例,不是推荐发送方式。文件格式、记录分隔符和最后一行是否需要换行,都由接收接口的协议决定。
核对原始请求体,不只看长度
在自己控制的测试环境里,让接口保存未经 JSON 解析、格式化或重新序列化的请求体,再与本地文件比较。若服务端日志只展示了解析后的对象,日志系统本身就可能改写空白,不能据此断定 curl 丢了换行。
需要查看 curl 的发送记录时,用无敏感信息的样本开启跟踪:
curl --trace ./curl-request.trace \
-H 'Content-Type: application/json' \
--data-binary @payload.json \
https://api.example.com/ingest
跟踪文件包含收发数据和说明信息,并不是一份可以直接与原文件比较哈希的纯请求体。查看其中的发送数据部分,结合测试服务保存的原始字节确认换行是否保留。跟踪可能记录认证头、Cookie 和完整业务内容,不应直接公开;排查后关闭跟踪,并按本地数据管理要求处理文件。
Content-Length 与文件大小相同只能说明长度一致,不能证明每个字节都相同;请求没有该头,也不能直接认定发送失败。对需要字节一致的接口,验收应同时确认原始内容、业务返回和服务端处理结果。
不要手工把 Content-Length 改成源文件大小来补救。请求体已经被处理过时,改请求头不会恢复丢掉的字节,还会让长度声明与实际发送内容不一致。应修正数据选项,并让 curl 为实际请求生成相应头部。
调整前保留原命令和测试样本,先在不会产生真实业务变更的接口验证。若正式 POST 已经发出,即使响应报错也可能发生了部分处理;重试前先核对业务记录或接口的幂等机制,避免重复提交。撤回参数修改不能撤销服务端已经完成的操作。


