最近在调用钉钉上传文件的接口的时候需要用node读取二进制文件,遇到一些问题,需要大家指点。
node代码
const fs = require('fs')
fs.readFile('3.jpg', function (err, data) {
if (err) throw err;
console.log(data)
})
同时也用 python读了一次该图片文件
Python代码
filename = '3.jpg'
fr = open(filename, 'rb')
fileBuffer = fr.read()
fr.close()
print fileBuffer
待读取的图片文件
那么问题来了,经过两种方式的读取,采用相同的方式拼接HTTP POST请求的body, (fileFormdataTemplate, end变量值忽略,他们都是一样的)
node
const postData = fileFormdataTemplate + data + end
python
postData = fileFormdataTemplate + fileBuffer + end
拼接完后,发起上传文件的请求到钉钉服务器, python的请求是成功的,node的请求失败。 经过分析,主要原因是node和python 读到的数据不一致,及 data 和 fileBuffer 不同。导致请求结果差异。 那么在node端该怎样读文件才能得到跟python读到的fileBuffer一致的内容?求大牛指点,困扰很久了。谢谢各位。
补充
[钉钉的上传文件接口] (https://open-doc.dingtalk.com/docs/doc.htm?spm=a219a.7629140.0.0.d5yPo3&treeId=172&articleId=104971&docType=1)
node 发起请求的代码片段
fs.readFile(filePath, (err, data) => {
if (err) {
debug(err)
return;
}
const stats = fs.statSync(filePath)
const fileSizeInBytes = stats["size"]
const fileFormdataTemplate = "\r\n--" + boundary + '\r\nContent-Disposition:form-data;name="' + "media" + '";filename="' + fileName + '";filelength="' + fileSizeInBytes + '"\r\nContent-Type:application/octet-stream' + '\r\n\r\n'
const postData = fileFormdataTemplate + data + end
// debug(postData)
axios({
method: 'post',
url: url,
headers: {
'Content-Type': 'multipart/form-data; boundary=' + boundary
},
data: postData
}).then((res) => {
debug(res.data)
if (callback && typeof callback === 'function') {
callback(res.data)
}
}).catch(err => {
debug(err)
})
debug(postData)
})
python的代码请求片段
filename='3.jpg'
fr=open(filename,'rb')
fileBuffer=fr.read()
fr.close()
fileFormdataTemplate ="\r\n--" + boundary +'\r\nContent-Disposition:form-data;name="'+"media"+'";filename="' +filename+'";filelength="'+str(len(fileBuffer))+'"\r\nContent-Type:application/octet-stream' +'\r\n\r\n'
end = "\r\n--%s--\r\n" % boundary
data=fileFormdataTemplate+fileBuffer+end
http_url = 'https://oapi.dingtalk.com/media/upload?access_token=' + token + '&type=' + fileType
http_body=data
try:
headers = {'Content-Type': 'multipart/form-data; boundary=%s' % boundary}
r=requests.post(http_url, data=http_body, headers=headers)
print r.text
except Exception,e:
print 'http error'
没人吗
好像py读出来的是一个file object,而nodejs的fs.readFile返回是buffer
fs.readFile(filePath,{encoding: 'utf8'} , (err, data) => {
或者
data.toString('utf8')
postData = Buffer.concat([
Buffer.from(fileFormdataTemplate),
data,
Buffer.from(end),
])
@xiedacon 试过这样,不行的
@magicdawn 正解。哥,you just save my ass. 非常感谢!~
@coolicer 正解。
感谢社区,让我对node又重新燃起了信心。本来都打算放弃node投入到python的怀抱的。
@YUFENGWANG 你没有碰到{“errcode”:43008,“errmsg”:“参数需要multipart类型”}这样的错误吗?我按照你的方式进行构建,始终会出现这个错误 我走的是代理服务器,忘记让代理支持文件这块
@wujohns 0.0
@YUFENGWANG 请问一下在node提交的时候,如何同时提交表单字段和文件?目前在示例中仅仅看到了提交文件的内容,不知道可否提供提交表单字段的方法
- 可以的,你可以自己写个空白的html,做一个form,一个file一个input,然后自己抓包(chrome devtool network中二进制的数据不会显示)试一下,格式一目了然!
- 基本格式如下,其中
WebKitFormBoundaryLsEVTTICMYICXVmw
就是content-type
中的boundary
:
------WebKitFormBoundaryLsEVTTICMYICXVmw
Content-Disposition: form-data; name="att"; filename="tt.jpg"
Content-Type: image/jpeg
....(图片的二进制)
------WebKitFormBoundaryLsEVTTICMYICXVmw
Content-Disposition: form-data; name="inputFd"
333333(input填的val)
------WebKitFormBoundaryLsEVTTICMYICXVmw--
node
作为http server
已经提供了协议栈中应用层该有的功能,而具体发什么,没有限制,你按照http协议发就好- 既然已经用发请求的三方库dep( 如楼主用到的
axios
, 还有node端
专用的request ),不建议自己拼报文,用lib封装好的相关方法各种情况可以考虑的更加完整。