资讯专栏INFORMATION COLUMN

微信公众号图片上传至阿里云OSS

whatsns / 2513人阅读

摘要:最近在做微信公众号,需要将图片上传至阿里云。利用模块将图片写到内存微信多媒体文件文件路径图片上传阿里云结果上传文件出错这种方式将图片暂存在内存里面,那如果并发量很大,是不是内存要爆炸了都感觉还是不可取。

最近在做微信公众号,需要将图片上传至阿里云OSS。在做这个功能的过程中,我走了不少弯路,尝试过很多种方法,最后终于研究出一种便捷优美的方式。现在把这些方法和思路记录下来,避免遗忘。

一、通过浏览器直接传给OSS

这种方式最简单。因为微信公众号的跳转页面是基于QQ浏览器的,所以可以直接使用HTML的input元素选择图片。

OSS有一个Post Object的接口允许HTML表单上传文件,除了文件(file)之外,还有一些其他的字段如保存到OSS的路径(key)、策略(policy)、自己的OSS应用的accessKeyId、签名(signature)等。

所以需要构造表单。一般有两种方式:

1)构造DOM节点,表单提交上传

像下面代码这样构造form元素,然后利用$("form").submit()提交。


2)利用Html5的FormData对象上传

像下面这样构造FormData对象,再通过ajax或fetch post表单数据。

const formData = new FormData();
formData.append("key", filePath); // OSS的保存路径
formData.append("policy", policy); // 策略
formData.append("OSSAccessKeyId", accessKeyId); // OSS对象的标识
formData.append("success_action_status", "200"); // 成功返回码
formData.append("signature", signature); // 签名
formData.append("file", file); // 图片文件,$("input[name="pic"]").files[0]
二、服务端下载微信图片再转存至OSS

上面的方法虽然简单直接,但只能从相册中选择图片,而想要拍摄图片并上传,则必须通过微信JS-SDK来调用相机。

wx.chooseImage({
  count: 1, // 默认9
  sizeType: ["original", "compressed"], // 可以指定是原图还是压缩图,默认二者都有
  sourceType: ["album", "camera"], // 可以指定来源是相册还是相机,默认二者都有
  success: function (res) {
    // 返回选定照片的本地ID列表,localId可以作为img标签的src属性显示图片
    var localIds = res.localIds; 
  }
});

这里有个问题,微信JS-SDK选择图片之后返回的是图片的标识id,而不是实际的图片文件,所以不能构造form表单上传OSS。

那该怎么办呢?思路:将图片先上传至微信的服务器(最多保存3天),再通过微信的下载多媒体文件接口(http://file.api.weixin.qq.com...)将图片下载到服务器,再上传至OSS(虽然有点绕,但可行)。

客户端代码:

wx.chooseImage({
  count: 1, // 默认9
  sizeType: ["original", "compressed"],
  sourceType: ["album", "camera"],
  success: function (res) {
    var localIds = res.localIds;
    wx.uploadImage({
      localId: localIds[0], // 需要上传的图片的本地ID,由chooseImage接口获得
      isShowProgressTips: 1, // 默认为1,显示进度提示
      success: function (res) {
        var serverId = res.serverId; // 返回图片的服务器端ID
        // do something ...
        // 调用自己搭建的服务端的api,传入serverId,做获取微信图片上传OSS的相关操作
        doSomething();
      }
    });
  }
});

小tips:选择图片时只要选择了compressed,微信就会自动帮我们压缩图片,官方文档也说明上传的多媒体文件会控制格式和大小,其中图片控制在jpg格式和1M以下的大小。所以,基本不用考虑图片过大的问题。实测中,8M的图片压缩后只有120KB左右。

服务端的代码经过了3次的演变才完善:

1)利用fs将图片写到本地
const fs = require("fs");
const request = require("require");
const OSS = require("ali-oss").Wrapper;

const ossClient = new OSS({
  accessKeyId: "your access key",
  accessKeySecret: "your access secret",
  bucket: "your bucket name",
  region: "oss-cn-hangzhou"
});

// 需要获取微信accessToken,这里不细说
const accessToken = "access token";
const mediaId = "xxxxxxx"; // 微信多媒体文件id
const destPath = `weixin/images/201702/${mediaId}.jpg`; // OSS文件路径,按自己喜欢构造咯
const wxReq = request(`http://file.api.weixin.qq.com/cgi-bin/media/get?access_token=${accessToken }&media_id=${mediaId}`);

// 将文件流pipe到本地文件
wxReq.pipe(fs.createWriteStream(`${mediaId}.jpg`));
wxReq.on("end", () => {
  co(function* () {
    const result = yield ossClient.putStream(destPath, fs.createReadStream(`${mediaId}.jpg`), {timeout: 30 * 60 * 1000});
    console.log("图片上传阿里云结果", result);
    fs.unlink(`${mediaId}.jpg`);
    // res.status(200).json(result);
  }).catch(err => {
    console.warn(err);
    //res.status(500).send("上传文件出错");
  });
});

这种方式需要频繁地写文件和删文件,感觉一点都不极客。

2)利用memory-streams模块将图片写到内存
const request = require("require");
const OSS = require("ali-oss").Wrapper;
const streams = require("memory-streams");

const ossClient = new OSS({
  accessKeyId: "your access key",
  accessKeySecret: "your access secret",
  bucket: "your bucket name",
  region: "oss-cn-hangzhou"
});

const accessToken = "access token";
const mediaId = "xxxxxxx"; // 微信多媒体文件id
const destPath = `weixin/images/201702/${mediaId}.jpg`; // OSS文件路径
const writer = new streams.WritableStream();
const wxReq = request(`http://file.api.weixin.qq.com/cgi-bin/media/get?access_token=${accessToken }&media_id=${mediaId}`);

wxReq.pipe(writer);
wxReq.on("end", () => {
  co(function* () {
    const result = yield ossClient.put(destPath, writer.toBuffer(), {timeout: 30 * 60 * 1000});
    console.log("图片上传阿里云结果", result);
    // res.status(200).json(result);
  }).catch(err => {
    console.warn(err);
    //res.status(500).send("上传文件出错");
  });
});

这种方式将图片暂存在内存里面,那如果并发量很大,是不是内存要爆炸了都?感觉还是不可取。

3)将下载图片的流直接写入OSS文件

折腾了好久,发现原来可以这么简单和优雅:

const request = require("require");
const OSS = require("ali-oss").Wrapper;

const ossClient = new OSS({
  accessKeyId: "your access key",
  accessKeySecret: "your access secret",
  bucket: "your bucket name",
  region: "oss-cn-hangzhou"
});

const accessToken = "access token";
const mediaId = "xxxxxxx"; // 微信多媒体文件id
const destPath = `weixin/images/201702/${mediaId}.jpg`; // OSS文件路径
const wxReq = request(`http://file.api.weixin.qq.com/cgi-bin/media/get?access_token=${accessToken }&media_id=${mediaId}`);

wxReq.on("response", (response) => {
  // request的响应结果response可以作为读取流传给ossClient
  co(function* () {
    const result = yield ossClient.putStream(destPath, response, {timeout: 30 * 60 * 1000});
    console.log("图片上传阿里云结果", result);
    // res.status(200).json(result);
  }).catch(err => {
    console.warn(err);
    //res.status(500).send("上传文件出错");
  });
});

这种方式省去了前面两种方式的中间步骤,更加简练直接,个人认为是最好的。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/81513.html

相关文章

  • Web端文件上传阿里OSS(基于Angular 5项目)

    摘要:一准备工作开通阿里云服务,从控制台上获取和。参考资料阿里云官方文档开始使用阿里云官方文档介绍如何在中快速使用访问服务微信公众号图片上传至阿里云前端图片直传试验如何基于和,快速搭建音视频文件上传服务声明转发请注明出处,谢谢 一、准备工作 1. 开通阿里云OSS服务,从控制台上获取AccessKeyId和AccessKeySecret。 2. 创建Bucket,并登录OSS控制台 3. 配...

    darkerXi 评论0 收藏0
  • SpringBoot 整合 阿里OSS 存储服务,快来免费搭建一个自己的图床

    摘要:笔主很早就开始用阿里云存储服务当做自己的图床了。阿里云对象存储文档,本篇文章会介绍到整合阿里云存储服务实现文件上传下载以及简单的查看。 Github 地址:https://github.com/Snailclimb/springboot-integration-examples(SpringBoot和其他常用技术的整合,可能是你遇到的讲解最详细的学习案例,力争新手也能看懂并且能够在看完...

    邹强 评论0 收藏0
  • 解决博客图片存储——阿里OSS运用

    摘要:接下来讲解一下博客中用的图床阿里云对象存储,简称,虽然目前大家用的比较多的是七牛云之类的,但是因为我个人原因我还是选择了阿里云,后面我会简单说明下原因。 showImg(https://segmentfault.com/img/remote/1460000008838754?w=960&h=300); 前言 之前打算写一篇有关个人博客SEO优化的体验和一些自身体会,但是发现自己还没完全...

    Lowky 评论0 收藏0

发表评论

0条评论

whatsns

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<