如果你像我一样,在 node 中应用 axios 实例调用公众号上传临时素材的接口,很有可能会出现客户端错误。公众号该接口需要 post/form 的方式来上传,在 curl 中加上参数 -F media=@test-case-1.mp3
可以测得接口调用成功。所以问题就出在代码中,有一些参数是不对的。
文档中有介绍,media 字段所表征的 formdata 是包含有 filename, filelength, content-type等信息,在 node 中,FormData 可以由 form-data
库提供。但是字段还是不全,缺失的那个字段叫做 content-length
。
将 content-length
请求头提交给微信公众号接口,现在可以成功调通。该 content-length
由 formdata 可以计算得出。
代码如下:
async function uploadMedia({ filePath, type }) {
const { access_token: accessToken, expires_in: expiresIn } =
await getAccessToken();
// let url = `https://api.weixin.qq.com/cgi-bin/media/upload?access_token=${accessToken}&type=${type}`;
// debuglog(url);
const form = new FormData();
form.append('media', fs.createReadStream(filePath));
let contentLen = await promisify(form.getLength).call(form);
debuglog(`[contentLen](${contentLen})`);
const headers = form.getHeaders({ 'content-length': contentLen });
let res = await axios.request({
method: 'POST',
url: '/media/upload',
params: { access_token: accessToken, type },
data: form,
// 这里 content-length 一定要设置, axios 漏掉了,所以利用 formData 来补偿
headers,
});
return res.data;
}
关键的代码 form.getLength((err, len) => {})
, 利用其来计算 contentLength
。
调用
test.skip('1 voice', async () => {
let filePath = path.join(process.cwd(), 'test/secret/test-case-1.mp3');
debuglog(filePath);
let r = await uploadMedia({ filePath, type: 'voice' });
console.log(r);
});
只需要调用 uploadMedia, 参数 filePath
, type
两个。