当前位置: 首页 > news >正文

使用 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

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、 安装依赖包
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

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

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

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。

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

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

1、生成随机文件

在开发之前,需要先处理点问题。前端去调用接口,直接上传文件到阿里云的时候。很有可能,多个人上传名字相同的文件。那么大家的文件就会互相覆盖,这真是一个大问题,必须要先想好怎么处理。

之前我们在使用multer的时候,只要上传文件,就会自动生成一个新的文件名。那它是怎么做的呢,可以看它里面的源码。

function getRandomFilename(req, file, cb) {crypto.pseudoRandomBytes(16, function (err, raw) {cb(err, err ? undefined : `${raw.toString('hex')}${Path.extname(file.originalname)}`);});
}

可以看到这里用了crypto,生成了随机字符。这个包我们在生成自己项目的秘钥的时候,也是用的它。这是一种生成随机字符的思路。

除此外,还有种常见的方法要介绍给大家,就是UUIDUUID的中文叫做:通用唯一识别码

在 Node.js 项目中使用它,需要先安装一个包,名叫就做:uuid。查看官方文档,只需要简单的引用它,调用一下它自带的函数,就可以生成唯一的随机字符了

const { v4: uuidv4 } = require('uuid');
uuidv4(); // ⇨ '1b9d6bcd-bbfd-4b2d-9b5d-ab8dfbbd4bed'

我们用它生成的这一串,可以避免用户上传的文件名相同,互相覆盖。直接复制命令,安装一下。完成后,启动项目。

npm i uuid

进入routes/uploads.js中,顶部增加uuid的引用。此外还要引用下moment.js,一会儿也会用到它。

const { v4: uuidv4 } = require('uuid');
const moment = require('moment');

上传完整代码

接着看阿里云直传的官方文档,这里直接给了我们示例代码,但是里面写的有些乱,而且也没有做安全方面的考虑。我们参考它,适当整理一下,就有了自己的代码。现在来看看实现的代码

/*** 获取直传阿里云 OSS 授权信息* GET /uploads/aliyun_direct*/
router.get('/aliyun_direct', async function (req, res, next) {// 有效期const date = moment().add(1, 'days');// 自定义上传目录及文件名const key = `uploads/${uuidv4()}`;// 上传安全策略const policy = {expiration: date.toISOString(),  // 限制有效期conditions:[['content-length-range', 0, 5 * 1024 * 1024], // 限制上传文件的大小为:5MB{ bucket: client.options.bucket }, // 限制上传的 bucket['eq', '$key', key], // 限制上传的文件名['in', '$content-type', ['image/jpeg', 'image/png', 'image/gif', 'image/webp']], // 限制文件类型],};// 签名const formData = await client.calculatePostSignature(policy);// bucket 域名(阿里云上传地址)const host =`https://${config.bucket}.${(await client.getBucketLocation()).location}.aliyuncs.com`.toString();// 返回参数const params = {expire: date.format('YYYY-MM-DD HH:mm:ss'),policy: formData.policy,signature: formData.Signature,accessid: formData.OSSAccessKeyId,host,key,url: host + '/' + key,};success(res, '获取阿里云 OSS 授权信息成功。', params);
});

详解

  • 现在要生成上传所需要的授权信息。但是需要给授权信息,增加一个有效期,总不能无限期使用吧。我这里设置的是 1 天,大家可以根据需要,自行调整。
  • 接着我们定义存储在阿里云 OSS 中的位置是uploads。后面又调用uuid,来生成一个唯一的文件名。
  • 这样指定好了以后,用户就必须按照接口指定的路径和文件名上传。就可以避免文件名相同,互相覆盖的问题。
  • 下面就是上传的安全策略了,分别配置了请求的有效期、文件的大小、指定的存储空间、指定的路径和文件名、必须是图片类型。
  • 这里用了eq和in。也就是等于,和在范围里的意思。除此外,还可以使用:starts-with和not-in。具体说明,大家可以参考官方文档。
  • 配置完成后,调用阿里云 SDK 的方法,生成签名。
  • 再生成一下上传到阿里云,要请求的地址。
  • 最后将上传所需要的参数,全都返回出去就完成了。这里有:有效期、上传安全策略、签名、accessid、阿里云 bucket 的域名、上传的路径和文件名。
  • 注意下,最后的url,它是阿里云 bucket 的域名拼接上路径和文件名。组合在一起,就是将来上传成功后,文件的完整URL地址。
2、使用 Apifox 上传

到这里接口已经全部开发完成了,我们来实际试试上传流程。阿里云上传分为两步:

  • 第一步,获取阿里云 OOS 授权信息。

  • 第二步,使用阿里云 OSS 授权信息,上传文件到阿里云 OSS。
    2.1. 第一步,获取阿里云 OOS 授权信息
    Apifox 中,新建一个接口

  • 请求方式:GET

  • 接口地址:/uploads/aliyun_direct

  • Headers:要带上登录成功的token。

点击发送后,就可以获取到,授权所需的信息了。
在这里插入图片描述
这里获取到的数据挺多的,一会儿调用阿里云的接口上传文件,这些都会用到的。接着点击后置操作,将获取到的值存储到环境变量中,这样可以方便后续使用。

  • AliyunPolicy:data.policy
  • AliyunSignature:data.signature
  • AliyunAccessid:data.accessid
  • AliyunKey:data.key
    在这里插入图片描述
    2.2. 第二步,上传到阿里云 OSS
    第二步,前端需要将文件,上传到返回的host地址中。我们接着继续调用第二个接口,
    在这里插入图片描述

接口地址:是上一步返回的host

  • Body:要选择form-data,这样才能上传文件。
  • 其他所需参数,都是上一步返回的,已经存入环境变量中的参数,大家自行填好即可:
  • key:{{AliyunKey}}
  • policy:{{AliyunPolicy}}
  • OSSAccessKeyId: {{AliyunAccessid}}
  • signature:{{AliyunSignature}}
  • file: 要把类型改为fille,然后随意选择一个图片。

点击发送,返回状态码204,但是没有返回任何提示信息。其实这已经成功了,回到阿里云 OSS 里,刷新一下,可以看到刚上传的文件了。

在这里插入图片描述
2.3. 获取图片完整的访问路径

上传成功后,阿里云没有返回给我们任何信息,那前端如何获取图片完整的访问路径呢?

其实用第一步生成url,就是完整的访问地址。复制这个地址,就可以直接用浏览器打开,当然也可以放在img标签的src中显示。

2.4. 错误情况
再来看看上传错误的情况。例如,如果不按指定的key来上传,自己非要叫做1.jpg

在这里插入图片描述
阿里云,就会用xml格式,提示错误,并且返回403状态码。

如果上传了个zip文件,而不是图片,也会返回对应的错误提示。
在这里插入图片描述
这种错误格式,由于用了xml,而不是json。将来前端来处理,又得费点劲了。

3. 跨域设置

还有个问题,我们这里是用 Apifox 上传的,但是在Vue或者React项目里上传,会出现跨域错误。需要在阿里云 OSS 里做个设置,

在这里插入图片描述

点击跨域设置,创建规则,

  • 来源:*
  • 允许 Methods:POST
  • 允许 Headers:*
    确定就好,这样在前端页面上操作,就不会出问题了。
4. 总结一下
  • 上传有两种方式。使用直传,是效率最高的方式。先调用 Node 接口,获取直传所需要的授权参数。前端开发,就可以直接将文件上传到阿里云 OSS 了。
  • 直传方式,一定要在策略中,限制好文件的类型、大小、所在路径和文件名。
  • 我们这里用uuid生成的唯一文件名,并且没有额外加上扩展名。扩展名其实并不是必须的,无论有没有,都完全不影响img标签显示图片。
  • 在网页上直传图片,阿里云OSS需要配置跨域。
5. 额外的尝试

关于 OSS 还有更多可玩的内容,大家可以尝试一下:

  • 图片处理:在图片路径后加上指定参数,可以显示各种处理后的图片,例如可以实现裁剪、加水印等操作。

  • 防盗链:可以在阿里云 OSS 里设置防盗链,这样除了自己的站点可以访问外,其他站点引用或者直接访问,就无法打开图片了。
    还有一种使用策略,大家可以试试:

  • 私有:可以设置成禁止公开访问,并设置为私有。

  • 自定义域名:然后配置自定义域名,并加上SSL证书。

  • CDN:接着开启CDN加速,并配置好CDN缓存时间。
    配置CDN后,阿里云会在全国各地机房都缓存OSS中的文件。让用户访问就近的机房,访问速度就会大大的提高。

因为设置了禁止公开访问,用户就必须通过CDN访问。就无法不经过授权,通过OSS直接访问图片。大家要知道,访问CDN的费用,可比直接访问OSS要便宜不少。

http://www.lryc.cn/news/521424.html

相关文章:

  • 破解合同管理之痛,开启智能化管理新模式
  • Linux-day06
  • 源码编译安装httpd 2.4,提供系统服务管理脚本并测试
  • Linux固定ip
  • Java 输入输出流(上)
  • mysql、oracle、sqlserver的区别
  • Java+Maven+GDAL
  • 初识算法和数据结构P1:保姆级图文详解
  • 【Go】Go Gorm 详解
  • 【IDEA版本升级JDK21报错方法引用无效 找不到符号】
  • Node.js 版本管理工具完全指南
  • JavaSE学习心得(多线程与网络编程篇)
  • 平均精确率均值(mAP)
  • VUE学习笔记1__创建VUE实例
  • Inxpect毫米波安全雷达:精准检测与动态保护,工业自动化可靠选择
  • 基于禁忌搜索算法的TSP问题最优路径搜索matlab仿真
  • C51交通控制系统的设计与实现
  • 深度学习的超参数
  • 网络安全面试题及经验分享
  • 【Golang 面试题】每日 3 题(三十一)
  • 微服务架构:挑战与机遇并存
  • Vue语音播报功能
  • 【Java设计模式-4】策略模式,消灭if/else迷宫的利器
  • citrix netscaler13.1 重写负载均衡响应头(基础版)
  • 【AI学习】地平线首席架构师苏箐关于自动驾驶的演讲
  • QILSTE H11-D212HRTCG/5M高亮红绿双色LED灯珠 发光二极管LED
  • 2️⃣java基础进阶——多线程、并发与线程池的基本使用
  • RAG多路召回
  • 复杂 C++ 项目堆栈保留以及 eBPF 性能分析
  • 网安——计算机网络基础