OSS对象存储PostObject Node版本

最近有个需求需要用到OSS对象存储的Post Object API,它跟平常用的Put Object操作有点不一样,Put操作用阿里云提供的SDK就能上传了。但Post要把请求参数作为消息体中的表单域传递。这就需要我们自己组装表单请求元素了。 

遇到这个需求刚开始还不慌的,毕竟有文档!当我看了一轮后,发现只有Java表单上传的示例,其他全部语言都没有的。这不是为难我这个小小前端仔吗? 

在阿里云官网提交了工单找售后仍然没有解决。没有办法,只能食自己啦!

先从百度找到了一个nodejs模拟表单上传的代码,细想一波,这跟我要弄的不就是差个OSS?然后对代码进行改造。

1. 基础版本:nodejs模拟HTML表单上传文件

参考这个博文就行了:https://blog.csdn.net/weixin_30662849/article/details/97466324

具体代码:

const http = require('http')
const path = require('path')
const fs = require('fs')

function postFile(fileKeyValue, req) {
  // boundary为边界字符串,无需指定,随便生成就行
  var boundaryKey = Math.random().toString(16);
  var enddata = 'rn----' + boundaryKey + '--';

  var files = new Array();
  for (var i = 0; i < fileKeyValue.length; i++) {
     // 组装表单的请求元素: Content-Type以及其他需要的元素
    var content = "rn----" + boundaryKey + "rn" + 
      "Content-Type: application/octet-streamrn" + 
      "Content-Disposition: form-data; name="" + fileKeyValue[i].urlKey + 
      ""; filename="" + path.basename(fileKeyValue[i].urlValue) + 
      ""rn" + "Content-Transfer-Encoding: binaryrnrn"

    var contentBinary = Buffer.from(content, 'utf-8');//当编码为ascii时,中文会乱码。
    files.push({contentBinary: contentBinary, filePath: fileKeyValue[i].urlValue});
  }
  var contentLength = 0;
  for (var i = 0; i < files.length; i++) {
    // 读取文件状态
   var stat = fs.statSync(files[i].filePath);
   contentLength += files[i].contentBinary.length;
   contentLength += stat.size;
  }
  // 添加请求头
  req.setHeader('Content-Length', contentLength + Buffer.byteLength(enddata));
  req.setHeader('Content-Type''multipart/form-data; boundary=--' + boundaryKey);

  // 将参数发出
  var fileindex = 0;
  var doOneFile = function(){
    req.write(files[fileindex].contentBinary);
    var fileStream = fs.createReadStream(files[fileindex].filePath, {bufferSize : 4 * 1024});
    fileStream.pipe(req, {end: false});
    fileStream.on('end'function() {
      fileindex++;
      if(fileindex == files.length){
        req.end(enddata);
        console.log('文件读取结束')
      } else {
        doOneFile();
      }
   });
  };
  if(fileindex == files.length){
    req.end(enddata);
  } else {
    doOneFile();
  }
}

//测试用例
var files = [ {urlKey: "file1", urlValue: "D:\test.zip"} ]
// 自己创建服务器的路径:接收上传文件
var urlStr = 'http://localhost:3001/upload/test'
var req = http.request(urlStr, {
  method: 'POST'
}, function(res){
  console.log("RES:" + res);
  console.log('STATUS: ' + res.statusCode);
  console.log('HEADERS: ' + JSON.stringify(res.headers));
  res.on("data"function(chunk){
    console.log("BODY:" + chunk);
  })
})

req.on('error'function(e){
console.log('problem with request:' + e.message);
console.log(e);
})
//  console.log('req: ', req)
postFile(files, req)
console.log("done")

2. 改造:OSS PostObject Node(express)版本

对着官网查参数 https://help.aliyun.com/document_detail/31988.html

OSS对象存储PostObject  Node版本

  • 请求语法-请求体:只上传了必填参数

注意:file必须为最后一个表单域,我另外用一个变量 content 列出来了

  const boundary = 9431149156168
  const enddata = `rn--${boundary}--`
  const formContent = `--${boundary}rnContent-Disposition: form-data; name="key"rnrn${key}`
    + `rn--${boundary}rnContent-Disposition: form-data; name="Content-Disposition"rnrnattachment;filename=${fileName}`
    + `rn--${boundary}rnContent-Disposition: form-data; name="OSSAccessKeyId"rnrn${OSSAccessKeyId}`
    + `rn--${boundary}rnContent-Disposition: form-data; name="policy"rnrn${policy}`
    + `rn--${boundary}rnContent-Disposition: form-data; name="Signature"rnrn${signature}
  let content = `rn--${boundary}rnContent-Disposition: form-data; name="file"; filename="${fileName}"rnContent-Type: application/octet-streamrnrn` 

对比官方文档语法,还差一个file_content,那就用fs模块读取文件,顺便将文件的长度读出来放在Headers上。

fs.readFile(filePath, 'binary'function(err, data) {
  if(err) return err
  content += data
  formBinary = Buffer.from(formContent, 'utf-8') //当编码为ascii时,中文会乱码。
  contentBinary = Buffer.from(content, 'utf-8')

  const absolutePath = path.resolve(__dirname, `../${filePath}`)
  let contentLength = 0
  const stat = fs.statSync(absolutePath)
  contentLength += formBinary.length + contentBinary.length
  contentLength += stat.size
}
  • 请求语法-请求头 需要的是:Method、Host、User-Agent、Content-Length、Content-Type,那就在http请求上设置,这里http.request返回的req是一个Http.client可读写的示例(node官网有说)
req.setHeader('Host', host)
req.setHeader('User-Agent''Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36')
req.setHeader('Content-Length', contentLength + Buffer.byteLength(enddata))
req.setHeader('Content-Type''multipart/form-data; boundary='+ boundaryKey)
  • 发送请求 现在Post Object的请求元素写好了,开始把请求元素写入http/https请求中,并把文件发送出去。
req.write(formBinary)
req.write(contentBinary)
const fileStream = fs.createReadStream(filePath, {bufferSize : 4 * 1024})
fileStream.pipe(req, {end: false})
fileStream.on('end'function() {
  req.end(enddata)
})

OK参数组装完毕。

3. 完整代码示例:

这里返回的逻辑写在req的回调函数里面原因:

  • req请求后只能通过回调函数的形式处理
  • 定义req我用的是const,所以不能放在读取文件fs.readFile之后了
  const fs = require('fs')
  const pathMod = require('path')
  const https = require('https')
  
  const path = '你文件的路径'
  //用来获取IOT平台返回的参数的(我这是通过IOT平台API返回的)
  const result = await GenerateOTAUploadURL()
  const { Policy, Signature, OSSAccessKeyId, Host, Key, FirmwareUrl} = result.Data
  // http请求路径:'https://iotx-ota.oss-cn-shanghai.aliyuncs.com'
  const urlStr = Host
  // 创建http/https请求实例
  const req = https.request(urlStr, { method: 'POST''Content-Type''multipart/form-data;boundary=9431149156168' }, async function(postRes) {
    // 上传升级包后的回调
    console.log('STATUS: ' + postRes.statusCode)
    // console.log('HEADERS: ' + JSON.stringify(res.headers))
    // 匹配状态码已2开头,我发送请求地址为https://iotx-ota.oss-cn-shanghai.aliyuncs.com,它没有返回内容,只有状态码204
    const reg = /^2/ 
    if (reg.test(postRes.statusCode)) {
      //文件上传成功后把本地服务器里的删了(浪费内存)
      fs.unlink(pathMod.resolve(__dirname, `../${path}`), (err) => next(err))
      res.status(200).json({msg: '上传成功!', success: 1 })
    }
    postRes.on("data"function(chunk) {
      console.log("BODY:" + chunk)
    })
  })
  //监听上传时的错误
  req.on('error'function(e) {
    console.log('problem with request:' + e.message)
    console.log(e)
  })
    
  // 上传文件 post Object OSS
  const boundary = 9431149156168
  const enddata = 'rn--' + boundary + '--'
  const formContent = `--${boundary}rnContent-Disposition: form-data; name="key"rnrn${Key}`
      + `rn--${boundary}rnContent-Disposition: form-data; name="Content-Disposition"rnrnattachment;filename=${originalname}`
      + `rn--${boundary}rnContent-Disposition: form-data; name="OSSAccessKeyId"rnrn${OSSAccessKeyId}`
      + `rn--${boundary}rnContent-Disposition: form-data; name="policy"rnrn${Policy}`
      + `rn--${boundary}rnContent-Disposition: form-data; name="Signature"rnrn${Signature}
  let content = `rn--${boundary}rnContent-Disposition: form-data; name="file"; filename="${originalname}"rnContent-Type: application/octet-streamrnrn`
  
  let formBinary, contentBinary
  fs.readFile(path, 'binary'function(err, data) {
    if(err) return err
    content += data
    formBinary = Buffer.from(formContent, 'utf-8') //当编码为ascii时,中文会乱码。
    contentBinary = Buffer.from(content, 'utf-8')
    const absolutePath = pathMod.resolve(__dirname, `../${path}`)
    let contentLength = 0
    const stat = fs.statSync(absolutePath)
    contentLength += formBinary.length + contentBinary.length
    contentLength += stat.size
    //oss
    req.setHeader('Host''iotx-ota.oss-cn-shanghai.aliyuncs.com')
    req.setHeader('User-Agent''Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36')
    req.setHeader('Content-Length', contentLength + Buffer.byteLength(enddata))
    req.setHeader('Content-Type''multipart/form-data; boundary='+ boundary)  
    // 将参数发出
    const doOneFile = function(){
      req.write(formBinary)
      req.write(contentBinary)
      const fileStream = fs.createReadStream(path, {bufferSize : 4 * 1024})
      fileStream.pipe(req, {end: false})
      fileStream.on('end'function() {
        req.end(enddata)
      })
    }
    doOneFile()
  })


被这个问题卡了差不多一周,我太菜了!先是想仿照Java示例实现的,后来发现不会,最后老老实实按照文档的语法一步一步来。



续记录一个前端菜狗的成长。


原文始发于微信公众号(Hephaestuses):OSS对象存储PostObject Node版本

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/45002.html

(0)
小半的头像小半

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!