最近在给一个小程序写后台,需要调用微信云服务的HTTP API,做些增删改查的工作。
用node写的,没怎么想就用的axios(事实证明在后端应用上用前端的库确实有问题,应该用request),
代码如下
async uploadfile(path, file){
// HTTP API需要先获取上传URL,然后在用这个URL上传
let {url, token, authorization:sign, cos_file_id:fileid, file_id:sourceId} = await this.getUploadUrl(path);
// 这个是node的formdata库
let form_data = new FormData();
let f = fs.createReadStream(file.path)
form_data.append('key', path);
form_data.append("Signature", sign);
form_data.append("x-cos-security-token", token);
form_data.append("x-cos-meta-fileid", fileid)
form_data.append('file', f);
return await axios.post(url, form_data, {
headers: {
// 加上formData的headers
...form_data.getHeaders();
}
})
.then(()=>{
return sourceId;
})
})
}
结果上传总是不对,一直返回The body of your POST request is not well-formed multipart/form-d
,但是同样的代码用Postman就没什么问题。
最后把整个请求都打印出来,发现content-length
没了。formdata提供的getLength
是个异步回调函数,遂用Promise包装之,
_getFormDataLength(form_data){
return new Promise((resolve, reject)=>{
form_data.getLength((err, length)=>{
if(err){
reject(err);
}else{
resolve(length)
}
})
})
}
然后在headers里加入
...省略
return await axios.post(url, form_data, {
headers: {
'content-length': await _getFormDataLength(form_data),
...(headers)
}
})
成功上传,因为印象中content-length不需要手动设置,可能是axios在node上有问题吧……
另外附上整个调用HTTP API的代码,仅供参考
const axios = require('axios')
const fs = require("fs");
const FormData = require("form-data")
class TecentApi{
/**
*
* @param {string} id app id
* @param {string} secret app secret
* @param {string} env 云环境id
*/
constructor(id, secret, env){
// 获取token
this.id = id;
this.secret = secret;
this.expire = -1;
this.loginTime = 0;
this.env = env;
this.url = "https://api.weixin.qq.com/tcb/";
}
async login(){
console.log("Login");
return await axios.get(`https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=${this.id}&secret=${this.secret}`)
.then((res)=>{
if(res.data.errcode==undefined){
this.token = res.data.access_token;
this.expire = res.data.expires_in;
this.loginTime = Date.now();
}else{
throw new Error(`Login failed: ${res.data.errorcode}: ${res.data.errmsg}`)
}
})
}
async refresh(){
console.log(this.expire)
console.log(this.expire*1000 + this.loginTime - Date.now());
let judge = (this.expire*1000 + this.loginTime - 50000)<=Date.now()
if(judge){
await this.login();
}
}
async count(col, filter){
let q = `db.collection(\"${col}\").where(${JSON.stringify(filter)}).count()`
return await this.access('databasecount', q);
}
async query(col, filter, limit, skip){
let limitQeury = limit>=0?`.limit(${limit})`:"";
let q = `db.collection(\"${col}\").orderBy('type', 'desc').where(${JSON.stringify(filter)}).skip(${skip})${limitQeury}.get()`;
return await this.access("databasequery", q);
}
async queryOne(col, docId){
let q = `db.collection(\"${col}\").doc(\"${docId}\").get()`;
return await this.access("databasequery", q);
}
async updateOne(col, docId, update){
let q = `db.collection(\"${col}\").doc(\"${docId}\").update(${JSON.stringify({data: update})})`
return await this.access("databaseupdate", q);
}
async deleteOne(col, id){
let q = `db.collection(\"${col}\").doc(\"${id}\").remove()`
return await this.access("databasedelete", q);
}
async update(col, filter, data){
let q = `db.collection(\"${col}\").where(${JSON.stringify(filter)}).update(${JSON.stringify({data: data})})`
return await this.access("databaseupdate", q);
}
async invoke(name, param){
return await this._invoke(name, param);
}
async add(col, document){
let q = `db.collection(\"${col}\").add({data: ${JSON.stringify(document)}})`
return await this.access("databaseadd", q);
}
async downloadfile(file_list){
return await this._request("batchdownloadfile", {
file_list,
}).then(res=>res.file_list)
}
async uploadfile(path, file){
let {url, token, authorization:sign, cos_file_id:fileid, file_id:sourceId} = await this.getUploadUrl(path);
let form_data = new FormData();
let f = fs.createReadStream(file.path)
form_data.append('key', path);
form_data.append("Signature", sign);
form_data.append("x-cos-security-token", token);
form_data.append("x-cos-meta-fileid", fileid)
form_data.append('file', f);
let headers = form_data.getHeaders();
return await this._getFormDataLength(form_data)
.then((length)=>{
return axios.post(url, form_data, {
headers: {
'content-length': length,
...(headers)
}
})
.then(()=>{
// 删除临时文件
//fs.unlink(file.path, ()=>{});
return sourceId;
})
})
}
_getFormDataLength(form_data){
return new Promise((resolve, reject)=>{
form_data.getLength((err, length)=>{
if(err){
reject(err);
}else{
resolve(length)
}
})
})
}
async batchDeleteFile(file_list){
return await this._request("batchdeletefile", {"fileid_list": file_list});
}
async getUploadUrl(path){
return await this._request("uploadfile", {
path,
})
.then(res=>{
return res;
})
}
async _request(api, body, config={}){
//console.log(body);
return await this.refresh().then(()=>{
let data = {
env: this.env,
...body
}
return axios.post(`${this.buildUrl(api)}`, data, config)
.then(({data})=>{
//console.log(data);
if(data.errcode){
throw new Error(`Access failed ${data.errcode}: ${data.errmsg}`)
}else{
return data;
}
})
})
}
async _invoke(name, param, config={}){
return await this.refresh().then(()=>{
let data = {
...param
}
return axios.post(`${this.buildUrl('invokecloudfunction')}&env=${this.env}&name=${name}`, data, config)
.then(({data})=>{
//console.log(data);
if(data.errcode){
throw new Error(`Access failed ${data.errcode}: ${data.errmsg}`)
}else{
return data;
}
})
})
}
async access(api, query){
return await this._request(api, {query: query});
}
buildUrl(apiName){
return `${this.url}${apiName}?access_token=${this.token}`;
}
}
本文由 橙叶博客 作者:FrankGreg 发表,转载请注明来源!
我发现了一个比较好Js工具,不知道你听说过没有哈,名字叫***Lodash***,你有空可以看看