Excel VBA通过POST请求API下载PDF文件损坏 其他客户端下载正常
VBA调用API下载PDF出现末尾内容差异损坏的原因与修复方案
你当前对ADODB.Stream的二进制模式设置是完全正确的,损坏来源不是Stream的处理逻辑——因为Type=1模式下的ADODB.Stream会逐字节原样读写内容,不会主动修改二进制数据,问题全部出在HTTP请求组件返回的responseBody本身就不是完整、合法的PDF内容。
核心问题根源
- 组件版本匹配错误:你代码中使用的
CreateObject("MSXML2.XMLHTTP")是无版本号的组件ProgID,系统会默认加载最早的MSXML 3.0版本组件,这个老版本在处理二进制响应流时存在已知缺陷:接收大体积二进制内容时,无法完整读取响应流的最后一段TCP分段,最终返回的字节数组要么缺失PDF末尾必需的%%EOF标记,要么被错误追加几个0x00空填充字节,和你观察到的“仅文件末尾与正常PDF存在差异”的现象完全匹配。Postman、cURL等第三方客户端不会调用这个老版本组件,所以拿到的内容是完整合法的。 - 状态判断逻辑存在漏洞:原代码中
If xhr.Status Then的判断条件等价于“只要收到任何HTTP响应(不管是2xx成功还是4xx/5xx错误)就执行保存”。老版本MSXML组件在解析分块传输(chunked transfer encoding)的响应时,不会自动剥离最后一个块的大小标记字节,会把这些传输层分隔符当成业务内容写入文件,导致PDF阅读器在校验文件结构时识别到非法字符,无法正常打开。 - 请求体传递方式存在语法风险:原代码中
xhr.send (strBody)用括号包裹了发送参数,在VBA语法规则下,这种写法会强制将strBody从原本的字节数组类型隐式转换为字符串类型。虽然部分场景下服务器能容错解析,但会导致multipart请求的边界字符串被篡改,部分服务器会提前中断响应,最终只返回不完整的PDF内容。
可直接落地的修复代码
' 替换为6.0版本的XMLHTTP组件,修复老版本的二进制流bug Set xhr = CreateObject("MSXML2.XMLHTTP.6.0") xhr.Open "POST", Uri, False xhr.setRequestHeader "Content-Type", "multipart/form-data; boundary=" & strBoundary xhr.setRequestHeader "X-Api-Key", "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" ' 去掉参数外的括号,确保字节数组形式的请求体被原样发送 xhr.send strBody ' 明确判断2xx成功状态,避免把错误响应内容写入文件 If xhr.Status >= 200 And xhr.Status < 300 Then Set oStream = CreateObject("ADODB.Stream") oStream.Type = 1 ' 二进制模式 oStream.Open oStream.Position = 0 ' 显式将流指针移到起始位置,避免残留偏移 oStream.Write xhr.responseBody oStream.SaveToFile outputFileName, 2 ' 2代表覆盖已存在的文件 oStream.Close Set oStream = Nothing End If Set xhr = Nothing
额外排查方向
- 替换组件版本后如果仍存在末尾字节差异,可以检查服务器是否启用了gzip/deflate压缩响应,老版本MSXML 3.0不会自动解压压缩后的二进制响应,而6.0版本会默认自动处理这类压缩格式。
- 确保保存文件的路径没有系统权限限制,不要将文件写入受系统保护的目录(如Program Files文件夹下),权限不足时Windows会在文件写入完成前截断内容,同样会导致文件末尾缺失字节。
内容的提问来源于stack exchange,提问作者Victor
相关产品推荐
相关产品推荐

