node实现后端签名上传阿里OSS并返回回调

和数据直传到 OSS 相比,以上方法有三个缺点:

  • 上传慢:用户数据需先上传到应用服务器,之后再上传到OSS。网络传输时间比直传到OSS多一倍。如果用户数据不通过应用服务器中转,而是直传到OSS,速度将大大提升。而且OSS采用BGP带宽,能保证各地各运营商之间的传输速度。
  • 扩展性差:如果后续用户多了,应用服务器会成为瓶颈。
  • 费用高:需要准备多台应用服务器。由于OSS上传流量是免费的,如果数据直传到OSS,不通过应用服务器,那么将能省下几台应用服务器。

  1. 在客户端通过JavaScript代码完成签名,无需过多配置,即可实现直传,非常方便。但是客户端通过JavaScript把AccesssKeyID和AccessKeySecret写在代码里面有泄露的风险,建议采用服务端签名后直传。

在这里插入图片描述
先来实现这种不需要回调的方式
需要如下几个参数
OSSAccessKeyId
policy
Signature
key
success_action_status
file
点这里是原文链接

前端代码(用的Jquery)

使用formData上传数据

<!DOCTYPE html>
<html lang="zh-CN">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <title>Document</title>
  </head>
  <body>
    hh
    <input type="file" id="upload" />
    <a href="https://www.bilibili.com" target="black">百度</a>
    <script src="/jquery.js"></script>
    <script>
      document.getElementById("upload").onchange = function() {
        var file = this.files[0];
        $.ajax({
          url: "/policy",
          data: "",
          type: "get",
          dataType: "json",
          success: function(data) {
            let param = new FormData(); // 创建form对象
            console.log(data.callbackbody);
            param.append("OSSAccessKeyId", data.OSSAccessKeyId);
            param.append("policy", data.policy);
            param.append("key", data.startsWith + data.saveName + file.name);
            param.append("success_action_status", 200);
            param.append("signature", data.signature);
            param.append("file", file, data.saveName);
            const xhr = new XMLHttpRequest();
            xhr.open("post", data.host, true);
            xhr.send(param);
          }
        });
      };
    </script>
  </body>
</html>
后端代码(用koa脚手架创建的)

从这获取你的OSS域名
在这里插入图片描述

从这获取你的ID和密匙
在这里插入图片描述

const crypto = require("crypto");
const path = require("path");
var router = require("koa-router")();

let oss = {
  OSSAccessKeyId: "你的AccessKey ID",
  secret: "你的Access Key Secret",
  host: "你的oss外网域名" 
};

router.get("/", function*(next) {
  yield this.render("index");
});

router.get("/policy", function*(next) {
  const dirPath = "images/"; //你的bucket 项目里的文件路径
  const { OSSAccessKeyId, host, secret } = oss;
  let end = new Date().getTime() + 360000;   //设置过期时间
  let expiration = new Date(end).toISOString();
/*Post policy中必须包含expiration和conditions。
Expiration
Expiration项指定了policy的过期时间,以ISO8601 GMT时间表示。例如2014-12-01T12:00:00.000Z指定了Post请求必须发生在2014年12月1日12点之前。
Conditions
Conditions是一个列表,可以用于指定Post请求的表单域的合法值。
这里详细点上面的链接看官方文档
*/	

  let policyString = {
    expiration,
    conditions: [
      { bucket: "你的bucket" },
      ["content-length-range", 0, 1048576000],
      ["starts-with", "$key", dirPath]
    ]
  };
  policyString = JSON.stringify(policyString);
  const policy = Buffer(policyString).toString("base64"); 
  //policy为一段经过UTF-8和base64编码的JSON文本,声明了Post请求必须满足的条件(文档)
  const signature = crypto
    .createHmac("sha1", secret)
    .update(policy)
    .digest("base64");
  this.body = {
    OSSAccessKeyId: OSSAccessKeyId,
    host,
    policy,
    signature,
    key: dirPath + end,
    saveName: end,
    startsWith: dirPath
  };
});

module.exports = router;
最后npm start 浏览器输入localhost:3000

这里随便发张图
在这里插入图片描述
这里可以看到控制台自己的那个OSS域名发来响应200
在这里插入图片描述
再去自己的bucket看到这张图,这就OK了!
在这里插入图片描述

最后实现返回回调这种方式

官方给的流程图
在这里插入图片描述

前端代码类似,只是FormData多了一个参数(callback)

这里可以去看官方详细解释
https://help.aliyun.com/document_detail/31927.html?spm=a2c4g.11186623.6.1466.7787b81e6FJzPm

https://help.aliyun.com/document_detail/31989.html?spm=a2c4g.11186623.2.16.1219b81eZVD571

<!DOCTYPE html>
<html lang="zh-CN">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <title>Document</title>
  </head>
  <body>
    hh
    <input type="file" id="upload" />
    <script src="/jquery.js"></script>
    <script>
      document.getElementById("upload").onchange = function() {
        var file = this.files[0];
        $.ajax({
          url: "/policy",
          data: "",
          type: "get",
          dataType: "json",
          success: function(data) {
            let param = new FormData(); // 创建form对象
            console.log(data.callbackbody);
            param.append("OSSAccessKeyId", data.OSSAccessKeyId);
            param.append("policy", data.policy);
            param.append("key", data.startsWith + data.saveName + file.name);
            param.append("success_action_status", 200);
            param.append("callback", data.callbackbody); //base64编码
            param.append("signature", data.signature);
            param.append("file", file, data.saveName);
            param.append("dir", "images/");
            const xhr = new XMLHttpRequest();
            xhr.open("post", data.host, true);
            xhr.send(param);
          }
        });
      };
    </script>
  </body>
</html>

后端(也是koa框架)

如果是本地测试,要先准备内网穿透工具。
我这里使用的穷人版的花生壳,主要将外网代理到本地koa服务器设置的端口上。
在这里插入图片描述

1.这里的回调函数函数返回给OSS的结果要以下格式

在这里插入图片描述https://help.aliyun.com/document_detail/31989.html?spm=a2c4g.11186623.2.16.1219b81eZVD571

router.post("/detail", function*(next) {
  console.log(this.request.body);
  this.status = 200;
  this.set("Content-Type", "application/json");
  this.body = {
    status: 200,
    value: "ok"
  };
});
2.callbackbody(base64)字符串

这里加了callbackhost:“你的oss外网域名” 会报502错误,具体官方文档我也不是很明白。
https://help.aliyun.com/document_detail/31989.html?spm=a2c4g.11186623.2.16.1219b81eZVD571

 let callbackString = {
    callbackUrl: "http://229p895l44.iask.in/detail",  //花生壳设置的外网域名 detail为callback
    callbackBody:   "filename=${object}&size=${size}&mimeType=${mimeType}&height=${imageInfo.height}&width=${imageInfo.width}",
    callbackBodyType: "application/x-www-form-urlencoded"
  };
  callbackString = JSON.stringify(callbackString);
  let callbackbody = Buffer.from(callbackString).toString("base64");
完整的代码如下
const crypto = require("crypto");
const path = require("path");
var router = require("koa-router")();

let oss = {
  OSSAccessKeyId: "你的AccessKey ID",
  secret: "你的Access Key Secret",
  host: "你的oss外网域名" 
};

router.get("/", function*(next) {
  yield this.render("index");
});

router.post("/detail", function*(next) {
  console.log(this.request.body);
  this.status = 200;
  this.set("Content-Type", "application/json");
  this.body = {
    status: 200,
    value: "ok"
  };
});

router.get("/policy", function*(next) {
  const dirPath = "images/"; //bucket 项目里的文件路径
  const { OSSAccessKeyId, host, secret } = oss;
  let end = new Date().getTime() + 360000;
  let expiration = new Date(end).toISOString();
  let policyString = {
    expiration,
    conditions: [
      { bucket: "zyh2020img" },
      ["content-length-range", 0, 1048576000],
      ["starts-with", "$key", dirPath]
    ]
  };
  policyString = JSON.stringify(policyString);
  const policy = Buffer.from(policyString).toString("base64");
  let callbackString = {
    callbackUrl: "http://229p895l44.iask.in/detail",  //花生壳设置的外网域名(内网穿透)
    callbackBody:   "filename=${object}&size=${size}&mimeType=${mimeType}&height=${imageInfo.height}&width=${imageInfo.width}",
    callbackBodyType: "application/x-www-form-urlencoded"
  };
  callbackString = JSON.stringify(callbackString);
  let callbackbody = Buffer.from(callbackString).toString("base64");
  const signature = crypto
    .createHmac("sha1", secret)
    .update(policy)
    .digest("base64");
  this.body = {
    OSSAccessKeyId: OSSAccessKeyId,
    host,
    policy,
    signature,
    callbackbody,
    key: dirPath + end,
    saveName: end,
    startsWith: dirPath
  };
});

module.exports = router;
npm run dev,打开浏览器输入你配置的花生壳域名,打开主页

在这里插入图片描述

选择一张图片上传

在这里插入图片描述

这里可以看到自己OSS域名返回结果了

在这里插入图片描述

这是自己的koa服务器返回给OSS,然后OSS返回给客户端的响应

在这里插入图片描述

这里我的vscode也打印出从OSS拿到了上传的文件信息了

在这里插入图片描述

最后看OSS里面也有刚刚上传的图片

在这里插入图片描述

如果出现OSS相应客户端203(OSS接收到了图片,但是没有向服务器发送回调),状态码为400
在这里插入图片描述
官方链接 https://help.aliyun.com/document_detail/31989.html?spm=a2c4g.11186623.2.16.1219b81eZVD571
如果状态码为502,除了我之前说的加了callbackhost,还有可能其他原因
其他错误,可以这篇文档的末尾 https://www.cnblogs.com/softidea/p/7217201.html

踩了不少坑,用了不少时间,因为官方没有node的例子,自己就结合其他语言和文档,还有不少博客,先记下以后在看看。
  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值