分享

微信小程序云服务HTTP API上传文件的问题

最近在给一个小程序写后台,需要调用微信云服务的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}`;
    }
}
(2)

本文由 橙叶博客 作者:FrankGreg 发表,转载请注明来源!

关键词:

热评文章

评论:

1 条评论,访客:0 条,博主:0 条
  1. jianala
    jianala发布于: 

    我发现了一个比较好Js工具,不知道你听说过没有哈,名字叫***Lodash***,你有空可以看看

发表评论