先说一下事请的起因,今天尝试用自己的小程序发帖时,发现传图不好使,并且返回都是403,应该是被sm.ms
拉黑了(起初我调接口用的不是很正规的方法)
小程序上传文件服务器要求域名备案,无奈sm.ms没有备案的马甲,改吧。
然后就有了下边这个流程,并记录下其中遇到的坑

1. 贴一下我封装后的Promise
代码,当伪代码看吧
1.1. 小程序选择图片
// 选择图片 |
1.2. 上传到云开发存储
const UPLOAD_PATH = 'upload/images/' |
1.3. 调用云函数,传递fileID,得到sm.ms图片地址
const cloudResult = await wxcloudcallFunction({ |
1.3.1. 云函数通过fileID下载文件
const file = await cloud.downloadFile({ |
1.3.2. 云函数取得sm.ms的Token
不知从何时,sm.ms接口出了v2版,上传图片需要token,而且旧版接口已废弃
// 取得smms token |
1.3.3. 云函数调用sm.ms上传接口
// 封装的request模块 |
在最后上传这里,出了很多问题,有以下几点要注意: - request模块的POST
multipart/form-data
方式,传参需要放到formData
中(而不是form
),否则Content-Type
会一直是x-www-form-urlencoded
,即使你在headers
设置了'Content-Type': 'multipart/form-data'
实际发出的请求头依然为application/x-www-form-urlencoded
。 - 搜索引擎搜到的上传File对象的代码基本都是下面这样 formData: {
smfile: fs.createReadStream(path)
}fs.createReadStream(path[, options])
- path <string> | <Buffer> | <URL>
- options <string> | <Object>
...path
支持路径
或者Buffer对象
或者URL
,然而当你直接把云存储下载得到的文件Buffer
对象作为入参时,像下边这样 formData: {
smfile: fs.createReadStream(file.fileContent)
}The "path" argument must be of type string without null bytes.
Buffer
非彼Buffer
,它期望的是缓冲区形式的路径,而不是数据缓冲区,详情见issues。 - 使用fs.writeFileSync(path, file.fileContent)
保存文件到云函数本地,再通过fs.createReadStream(path)
是不是就可以了? 然而,经测试,云函数文件环境为Read-only,并不能成功保存文件😂。 - 直接使用Buffer
对象上传时,像下边这样 formData: {
smfile: file.fileContent
}{"success":false,"msg":"Invalid file source"}
formData: {
smfile: {
value: file.fileContent,
options: {
filename: `图片${file_ext}`
}
}
}options
的方式手动提供"文件"相关的信息,request
的官方文档也有此方式的详细介绍。
好了,终于传上去了🏆🍻🍗