我用的 NodeJS 开发的,在用 node-fetch 上传图片是返回的 HTTP STATUS 是 412 错误,我很确定我的代码是正确的,并且发现了有类似相同的问题
这是另一个问题的链接 https://developers.weixin.qq.com/community/develop/doc/00000ef32dc64883d9d93f31f56800
但我对此问题的回答并不满意,因为 request 库早在很多年前就弃用了,原作者推荐用 node-fetch
我觉得不应该用弃用的库应对目前业务,可无奈所搜了一堆现有的微信公众号开发的列子,用的都是 request
希望社区有大神给予解答,附上我的代码
const fetch = require('node-fetch')
const fs = require('fs')
const FormData = require('form-data')
const media = fs.createReadStream('foo.jpg')
const formData = new FormData()
formData.append('media', media)
const response = await fetch(url, {
method: 'POST',
body: formData
})
if (!response.ok) {
console.log(response.status)
reject('error')
throw new Error('Upload material failed')
}
// 412
// Error: Upload material failed
好吧,既然没人回答,我自己折腾了一下午解决了问题。
这里面坑真多,为了能抓 node server 包就花了我好长时间。
我前后抓了 request 和 node-fetch 的包,进行报文头部的分析,因为我猜测,肯定是报文某个字段请求微信服务器它不认可。
request post
host: api.weixin.qq.com content-type: multipart/form-data; boundary=--------------------------432155727223273682777525 content-length: 45303 Connection: close ----------------------------432155727223273682777525 Content-Disposition: form-data; name="media"; filename="2.jpg" Content-Type: image/jpeg ...DATA... ----------------------------432155727223273682777525--
node-fetch post
Host: api.weixin.qq.com Content-Type: multipart/form-data;boundary=--------------------------297137759555768400521474 Accept: */* User-Agent: node-fetch/1.0 (+https://github.com/bitinn/node-fetch) Accept-Encoding: gzip,deflate Connection: close Transfer-Encoding: chunked ----------------------------297137759555768400521474 Content-Disposition: form-data; name="media"; filename="2.jpg" Content-Type: image/jpeg ...DATA... ----------------------------297137759555768400521474--
前后对面他们的请求报文字段几乎一致,不一致的 Accept User-Agent Accept-Encoding Transfer-Encoding,这几个都是无关紧要的,一般来说服务器不会对他们做出逻辑回应。
那么问题应该就出在 node-fetch 没有自动设置 content-length 了,猜测微信服务器应该对这个字段做了判断校验,比如媒体文件大小限制,发现没法判断,则返回了错误。
问题找到了,那就容易解决了(虽然也不容易还是有何种坑)
为什么 node-fetch 不会自动设置 content-length 字段呢,不应该啊,于是我扒了下官方文档看到这个
我就是以流文件上传的,所以他没法设置这个字段,那我就自己设置他好了
上代码
const fetch = require('node-fetch') const fs = require('fs') const FormData = require('form-data') const media = fs.createReadStream('foo.jpg') // 获取文件的信息 const stats = fs.statSync('foo.jpg') const formData = new FormData() // 在这里传递文件的长度 formData.append('media', media, { knownLength: stats.size }) const response = await fetch(url, { method: 'POST', headers:{ // 千万不要在这里传 'Content-Length',因为实际上你的报文长度和文件大小并不一致,之前我设置了 formData 的大小,这里就能自动生成真实报文长度了 } body: formData }) if (!response.ok) { console.log(response.status) reject('error') throw new Error('Upload material failed') }else{ console.log(await response.json()) }