Node读取二进制文件的问题
发布于 4 年前 作者 YUFENGWANG 9530 次浏览 来自 问答

最近在调用钉钉上传文件的接口的时候需要用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

待读取的图片文件

3.jpg 那么问题来了,经过两种方式的读取,采用相同的方式拼接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'
13 回复

没人吗

好像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. 非常感谢!~

感谢社区,让我对node又重新燃起了信心。本来都打算放弃node投入到python的怀抱的。

@YUFENGWANG 你没有碰到{“errcode”:43008,“errmsg”:“参数需要multipart类型”}这样的错误吗?我按照你的方式进行构建,始终会出现这个错误 我走的是代理服务器,忘记让代理支持文件这块

@YUFENGWANG 请问一下在node提交的时候,如何同时提交表单字段和文件?目前在示例中仅仅看到了提交文件的内容,不知道可否提供提交表单字段的方法

@pingchangrenforcode

  • 可以的,你可以自己写个空白的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封装好的相关方法各种情况可以考虑的更加完整。
回到顶部