使用 Multer 上传图片到阿里云 OSS

文件上传到哪里更好?

  • 上传到服务器本地

上传到服务器本地,这种方法在现今商业项目中,几乎已经见不到了。因为服务器带宽,磁盘 IO

都是非常有限的。将文件上传和读取放在自己服务器上,并不是明智的选择。

  • 上传到云储存

上传到云存储,则无需担心带宽和磁盘问题,而且配置 CDN

也很简单。所以明智的选择,要用云存储,这里我们以阿里云的对象存储为例来学习如何实现上传。

阿里云对象存储阿里云oss



上传的两种方式

我们需要开发,专门用于阿里云上传的接口。开发上传接口,也有两种方案,分别是服务端代理上传和客户端直传。这两种方式在开发、使用上各有优劣。我们简单的做个对比:

服务端代理上传

服务端代理上传。使用这种方式,一张图片,先要上传到 Node 项目的服务器中,然后再由 Node 服务器上传到阿里云 OSS。

这样这张图片,要上传两次,会造成网络资源的浪费,增加服务器的开销。尤其是在访问量大的情况下,会对项目的稳定运行,造成很大的影响。

但这种方式也有优点,就是开发简单、前端使用非常方便。而且后端可以很方便的做记录,可以开发一个专门用来,管理用户附件的功能。

1、获取秘钥

使用代码来访问阿里云,需要两个用来认证的参数。点击阿里云网站右上角用户头像里的AccessKey管理

从这里创建自己的阿里云的AccessKey。页面还会弹出使用 RAM 用户 AccessKey

根据阿里云的提示,我们就选择使用 RAM 用户 AccessKey


然后通过验证

创建完成后,还需要对当前用户进行授权。勾选后,点击添加权限

关闭小窗口,回来看用户信息。这里还有两个非常关键的AccessKey IDAccessKey Secret。先不要关闭页面,马上就要用到它们。

记得保存好: AccessKey Secret 后续无法查看

当前项进行配置使其可以自由读 无需签名验证



2、配置环境变量

到这里为止,我们开发上传接口,所需要的东西已经全部拿到了。打开咱们开发的 Node.js 项目,找到.env文件,增加点配置。将自己的AccessKey IDAccessKey Secret值复制进来。

后面的ALIYUN_BUCKETALIYUN_REGION,可以在概览中找到,我这里分别是:wlyxw-ossoss-cn-chengdu。大家复制的时候,注意下,只要前面这一部分,后面的完整域名不需要。

.env

js 复制代码
NODE_ENV=development
PORT=3000
SECRET=


ALIYUN_ACCESS_KEY_ID=AccessKey 
ALIYUN_ACCESS_KEY_SECRET=AccessKey Secret
ALIYUN_BUCKET=wlyxw-oss
ALIYUN_REGION=oss-cn-chengdu

如果项目是启动状态,改完环境变量了,记得一定要重启服务。

3、 安装依赖包
js 复制代码
npm i ali-oss multer multer-aliyun-oss
  • ali-oss:是用来操作阿里云 OSS 的 SDK
  • multer:是专门用于上传文件的 node.js 中间件
  • multer-aliyun-oss,则是用来配合 multer,将文件上传到阿里云 OSS 的
4、实现上传代码

/routes目录中新建一个路由文件,就叫做uploads.js

uploads.js

js 复制代码
const express = require('express');
const router = express.Router();
const { success, failure } = require('../utils/responses');

/**
 * 阿里云 OSS 客户端上传
 * POST /uploads/aliyun
 */
router.post('/aliyun', function (req, res) {
  try {

  } catch (error) {
    failure(res, error);
  }
})

module.exports = router;

接着查看 multer-aliyun-oss的文档。可以看到这里的代码还是比较简单的,上面需要先做一个配置,然后调用方法就可以上传了。

但这里缺少对上传文件的验证,我们继续看multer的官方文档。看到这里可以通过参数限制文件大小和文件类型。在它们的基础上,我们做一个整合,就得到了这样一个配置文件。

因为这些配置,内容比较多,而且将来会在多个不同的路由文件中使用。考虑到代码的干净和复用,就不要将它们直接放在路由文件里了。可以在utils里,新建一个aliyun.js文件,将它们直接粘贴进去。

aliyun.js

js 复制代码
const multer = require('multer');
const MAO = require('multer-aliyun-oss');
const OSS = require("ali-oss");
const {BadRequest} = require('http-errors')

// 阿里云配置信息
const config = {
  region: process.env.ALIYUN_REGION,
  accessKeyId: process.env.ALIYUN_ACCESS_KEY_ID,
  accessKeySecret: process.env.ALIYUN_ACCESS_KEY_SECRET,
  bucket: process.env.ALIYUN_BUCKET,
};

const client = new OSS(config);

// multer 配置信息
const upload = multer({
  storage: MAO({
    config: config,
    destination: 'uploads'  // 自定义上传目录
  }),
  limits: {
    fileSize: 5 * 1024 * 1024, // 限制上传文件的大小为:5MB
  },
  fileFilter: function (req, file, cb) {
    // 只允许上传图片
    const fileType = file.mimetype.split('/')[0];
    const isImage = fileType === 'image';

    if (!isImage) {
      return cb(new BadRequest('只允许上传图片。'));
    }

    cb(null, true);
  }
});

//  单文件上传,指定表单字段名为 file
const singleFileUpload = upload.single('file');
// 多文件上传 指定传输字段为files
const multipleFilesUpload = upload.array('files');
module.exports = {
  config,
  client,
  singleFileUpload,
  multipleFilesUpload
}
  • 上面的config,都是阿里云相关的配置,直接读取刚才定义的环境变量。
  • 下面的uploadmulter中间件相关的配置,我们这里自定义了上传的目录,限制了文件大小和类型。
  • 接着,限定了只允许单文件上传。并指定上传表单的名字叫做:file。
  • 最后,导出它们,需要用到singleFileUpload

接着就要来完善路由,实现上传操作了:
uploads.js

js 复制代码
const { config, client, singleFileUpload, multipleFilesUpload } = require('../utils/aliyun');
const { BadRequest } = require('http-errors')

/**
 * 阿里云 OSS 客户端上传
 * POST /uploads/aliyun
 */
router.post('/aliyun', function (req, res) {
  try {
    singleFileUpload(req, res, async function (error) {
      if (error) {
        return failure(res, error);
      }

      if (!req.file) {
        return failure(res, new BadRequest('请选择要上传的文件。'));
      }
      // 记录附件信息
      await Attachment.create({
        ...req.file,
        userId: req.userId,
        fullpath: req.file.path + '/' + req.file.filename,
      })

      success(res, '上传成功。', {file: req.file.url});
    });
  } catch (error) {
    failure(res, error);
  }
})

// 多文件上传
router.post('/aliyunMultiple', function (req, res) {
    try {
      multipleFilesUpload(req, res, async function (error) {
        if (error) {
          return failure(res, error);
        }

        if (req.files.length === 0) {
          return failure(res, new BadRequest('请选择要上传的文件。'));
        }
        // 记录附件信息
        req.files.map(async item => {
          await Attachment.create({
            ...item,
            userId: req.userId,
            fullpath: item.path + '/' + item.filename,
          })

        })
        success(res, '上传成功。', {files: req.files});
      });
    } catch (error) {
      failure(res, error);
    }
  }
)
  • 顶部,引用一下刚才定义的那些上传配置。
  • 接着非常简单的调用一下方法,如果报错了,就提示错误。
  • 还要判断下,用户是否上传了文件。有的用户可能根本没选文件,就直接提交表单了。
  • 如果没有出错,就显示已经上传的文件信息。文件信息被存储在req.file里了。
5、app.js添加路由引用

客户端直传

客户端直传。客户端,只需要请求 Node 接口,获取上传阿里云所需的授权信息。拿到这些授权信息后,再由客户端直接上传到阿里云 OSS。

这样图片不需要经过服务器中转,服务器的开销非常小,上传速度也会快很多。

对应的缺点就是,在开发上,代码麻烦点。在使用上,前端要调用两次接口,操作比较繁琐。

相关推荐
蓝黑202011 小时前
阿里云短信验证码服务
阿里云·验证码·sms
创思通信21 小时前
4G模块 EC200通过MQTT协议连接到阿里云
数据库·物联网·mqtt·阿里云·at·ec200a
蓝黑20202 天前
VSCode远程连接阿里云ECS服务器
服务器·vscode·阿里云
蓝黑20203 天前
阿里云ECS服务器搭建ThinkPHP环境
服务器·阿里云·thinkphp
tanxiaomi3 天前
阿里云 OSS 前端直传实战:表单上传 + Policy 模式详解
前端·阿里云·云计算
曾哥嵌入式4 天前
Stm32通过ESP8266 WiFi连接阿里云平台
stm32·嵌入式硬件·阿里云
阿里云云原生4 天前
用通义灵码渐进式开发 0->1 实现高考志愿规划项目题文档
阿里云·高考·通义灵码
智慧源点4 天前
阿里云RDS MySQL数据归档全攻略:方案选择指南
阿里云·云计算
m0_748254094 天前
阿里云详解:与 AWS、GCP 的全方位比较
阿里云·云计算·aws
阿里云大数据AI技术5 天前
鹰角网络基于阿里云 EMR Serverless StarRocks 的实时分析工程实践
starrocks·clickhouse·阿里云·emr·实时分析