当前位置: 首页 > 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要便宜不少。

相关文章:

使用 Multer 上传图片到阿里云 OSS的两种方式

文件上传到哪里更好? 上传到服务器本地 上传到服务器本地,这种方法在现今商业项目中,几乎已经见不到了。因为服务器带宽,磁盘 IO 都是非常有限的。将文件上传和读取放在自己服务器上,并不是明智的选择。 上传到云储存…...

破解合同管理之痛,开启智能化管理新模式

合同管理是采购管理中的一项重要环节,涉及合同洽谈、草拟、签订、生效、履行、失效全过程。随着企业业务规模的发展壮大,企业与各类供应商之间的合作往来更加频繁,需要签署和管理的合同文件也不断增多,如何提升合同管理效率成为企…...

Linux-day06

第14章 进程管理(重点) 进程基本介绍 程序运行起来就是一个进程 1.程序和进程的关系 2.在Linux中有两种方式执行,一种叫前台,一种后台 ps指令详解 显示系统执行的进程 USER:进程执行用户 PID:进程号 …...

源码编译安装httpd 2.4,提供系统服务管理脚本并测试

总结需要安装的包 sudo yum groupinstall "Development Tools" -y #httpd的依赖包yum install tar -y #tar压缩包sudo yum install apr-devel apr-util-devel #APR库 提供跨平台接口的库sudo yum install pcre pcre-devel # PCRE库和 pcre-config工具--提供PCRE库…...

Linux固定ip

进入etc/sysconfig/network-scripts目录 cd /etc/sysconfig/network-scripts 编辑ifcfg-ens33文件 vi ifcfg-ens33 将BOOTPROTO的值改为“static”,在文档最后添加需要的固定IP BOOTPROTO"static" IPADDR192.168.132.136点击按键“esc”,…...

Java 输入输出流(上)

目录 1.Java 输入输出流 2.Java File类 3.Java File类目录 1.创建目录 2.列出目录中的文件 4.Java File类文件 1.文件的创建与删除 2.运行可执行文件 5.Java 文件字节输入流(1) 6.Java 文件字节输入流(2) 1.使用输入流读取字节 2.关闭流 7.Java 文件字节输出流(1…...

mysql、oracle、sqlserver的区别

一、保存数据的持久性: MySQL:是在数据库更新或者重启,则会丢失数据。 Oracle:把提交的sql操作线写入了在线联机日志文件中,保持到了磁盘上,可以随时恢复。 SqlServer:2…...

Java+Maven+GDAL

下载已经编译好的压缩包,下载地址 解压 jar 包 release-1930-x64-dev.zip\release-1930-x64\bin\gdal\java 目录下 打成Maven依赖 mvn install:install-file -Dfilegdal-3.10.1.jar -DgroupIdorg.gdal -DartifactIdgdal -Dversion3.10.1 -Dpackagingjar -Dgener…...

初识算法和数据结构P1:保姆级图文详解

文章目录 前言1、算法例子1.1、查字典(二分查找算法)1.2、整理扑克(插入排序算法)1.3、货币找零(贪心算法) 2、算法与数据结构2.1、算法定义2.2、数据结构定义2.3、数据结构与算法的关系2.4、独立于编程语言…...

【Go】Go Gorm 详解

1. 概念 Gorm 官网:https://gorm.io/zh_CN/docs/ Gorm:The fantastic ORM library for Golang aims to be developer friendly,这是官网的介绍,简单来说 Gorm 就是一款高性能的 Golang ORM 库,便于开发人员提高效率 那…...

【IDEA版本升级JDK21报错方法引用无效 找不到符号】

java: 方法引用无效 找不到符号 符号: 方法 getFirst() 位置: 接口 java.util.List 升级JDK21版本遇到问题,报错找不到符号 但是点进去又能发现这个函数,证明能够找到这个方法,但是就是报错 java: 方法引用无效 找不到符号 符号: …...

Node.js 版本管理工具完全指南

Node.js 版本管理工具完全指南 目录 1. nvm (Node Version Manager)2. n (Node Package Manager)3. fnm (Fast Node Manager)4. Volta5. 工具对比 1. nvm (Node Version Manager) 1.1 安装指南 macOS/Linux # 使用 curl 安装 curl -o- https://raw.githubusercontent.com…...

JavaSE学习心得(多线程与网络编程篇)

多线程-网络编程 前言 多线程&JUC 多线程三种实现方式 第一种实现方式 第二种实现方式 第三种实现方式 常见成员方法 买票引发的安全问题 同步代码块 同步方法 Lock锁 生产者和消费者 常见方法 等待唤醒机制 练习 抢红包 抽奖 多线程统计并求最…...

平均精确率均值(mAP)

mAP(mean Average Precision,平均精确率均值) 并不是传统意义上的“精度”(Accuracy),而是一种专门用于评估目标检测、图像分割或信息检索等任务的性能指标。它更全面地反映了模型在不同类别和不同置信度阈…...

VUE学习笔记1__创建VUE实例

核心步骤 <div id"app"><!-- 这里存放渲染逻辑代码 --><h1>{{ msg }}</h1><a href"#">{{count}}</a> </div><!-- 引入在线的开发版本核心包 --> <!-- 引入核心包后全局可使用VUE构造函数 --> <…...

Inxpect毫米波安全雷达:精准检测与动态保护,工业自动化可靠选择

Inxpect毫米波安全雷达具备“精准检测、动态区域保护、环境适应性”三大核心功能。在工业自动化和机器人系统里&#xff0c;这些功能发挥着重要作用,有助于提升安全性与效率。Inxpect雷达运用毫米波技术&#xff0c;在诸如存在灰尘、烟雾或碎屑等复杂环境中&#xff0c;也能保持…...

基于禁忌搜索算法的TSP问题最优路径搜索matlab仿真

目录 1.程序功能描述 2.测试软件版本以及运行结果展示 3.核心程序 4.本算法原理 5.完整程序 1.程序功能描述 基于禁忌搜索算法的TSP问题最优路径搜索&#xff0c;旅行商问题&#xff08;TSP&#xff09;是一个经典的组合优化问题。其起源可以追溯到 19 世纪初&#xff0c;…...

C51交通控制系统的设计与实现

实验要求&#xff1a; 本题目拟设计一个工作在十字路口的交通信号灯控制系统&#xff0c;设东西方向为主干道A&#xff0c;南北方向为辅助干道B。要求&#xff1a;&#xff08;1&#xff09;用发光二极管模拟交通灯信号&#xff1b;&#xff08;2&#xff09;灵活控制主、辅干…...

深度学习的超参数

1. 引言 1.1 什么是超参数&#xff1f; 在机器学习和深度学习中&#xff0c;超参数&#xff08;Hyperparameter&#xff09; 是在模型训练前由开发者设置的参数&#xff0c;这些参数决定了模型的训练过程和模型的结构。例如&#xff1a; 神经网络的层数和每层神经元的数量。…...

网络安全面试题及经验分享

本文内容是i春秋论坛面向专业爱好者征集的关于2023年面试题目和答案解析&#xff0c;题目是真实的面试经历分享&#xff0c;具有很高的参考价值。 shiro反序列化漏洞的原理 Shiro反序列化漏洞的原理是攻击者通过精心构造恶意序列化数据&#xff0c;使得在反序列化过程中能够执…...

新能源汽车霍尔线束介绍

新能源汽车作为传统燃油车的重要替代方案&#xff0c;其核心驱动系统依赖于高效、精准的电子控制技术。在这一体系中&#xff0c;霍尔线束作为关键传感器组件&#xff0c;承担着电机转速、位置信号的实时采集与传输任务&#xff0c;其性能直接影响整车动力输出的稳定性和能量利…...

split_conversion将json转成yolo训练用的txt,在直接按照8:1:1的比例分成训练集,测试集,验证集

第一章 使用说明 类别自己在代码中改&#xff0c;其他四个参数 --json-folder:json文件夹路径 --txt-folder&#xff1a;转换成功后txt的存放路径 --images-dir&#xff1a;图片文件夹路径 --save-dir&#xff1a;转换完成分割后所有文件的路径 终端命令行&#xff1a;p…...

Linux研学-环境搭建

一 概述 1 Linux 概述 Linux系统由内核、Shell、文件系统、应用程序及系统库等关键部分组成。内核作为核心&#xff0c;管理硬件资源与系统服务&#xff1b;Shell提供用户与系统交互的命令行界面&#xff0c;让用户能便捷执行操作&#xff1b;文件系统负责数据的存储、组织与管…...

数据绑定页面的完整的原理、逻辑关系、实现路径是什么?页面、表格、字段、属性、值、按钮、事件、模型、脚本、服务编排、连接器等之间的关系又是什么?

目录 一、核心概念:什么是数据绑定页面? 二、涉及的组件及其逻辑关系 页面(Page): 表格(Table): 字段(Field): 属性(Property): 值(Value): 按钮(Button): 事件(Event): 模型(Model): 脚本(Script): 服务(Service): 服务编排(Se…...

Express教程【001】:Express创建基本的Web服务器

文章目录 1、初识express1.1 什么是Express1.2 主要特点1.3 Express的基本使用1.3.1 安装1.3.2 创建基本的Web服务器 1、初识express 目标&#xff1a; 能够使用express.static()快速托管静态资源能够使用express路由精简项目结构能够使用常见的express中间件能够使用express创…...

c/c++的opencv椒盐噪声

在 C/C 中实现椒盐噪声 椒盐噪声&#xff08;Salt-and-Pepper Noise&#xff09;&#xff0c;也称为脉冲噪声&#xff08;Impulse Noise&#xff09;&#xff0c;是数字图像中常见的一种噪声类型。它的特点是在图像中随机出现纯白色&#xff08;盐&#xff09;或纯黑色&#x…...

STM32G4 电机外设篇(二) VOFA + ADC + OPAMP

目录 一、STM32G4 电机外设篇&#xff08;二&#xff09; VOFA ADC OPAMP1 VOFA1.1 VOFA上位机显示波形 2 ADC2.1 用ADC规则组对板载电压和电位器进行采样 3 OPAMP&#xff08;运放&#xff09;3.1 结合STM32内部运放和ADC来完成对三相电流的采样3.2 运放电路分析 附学习参考…...

eBest智能价格引擎系统 助力屈臣氏饮料落地「价格大脑」+「智慧通路」数字基建​

从价格策略到终端执行&#xff0c;数字化正在重构饮料行业竞争壁垒&#xff01; 近日&#xff0c;eBest为屈臣氏饮料提供的智能价格引擎系统已正式上线并投入运营。同时&#xff0c;基于eBest SFA方案且与屈臣氏饮料业务场景深度耦合的Smart Field Operation智慧通路项目正式启…...

我的3种AI写作节奏搭配模型,适合不同类型写作者

—不用内耗地高效写完一篇内容&#xff0c;原来可以这样搭配AI ✍️ 开场&#xff1a;为什么要“搭配节奏”写作&#xff1f; 很多人以为用AI写作&#xff0c;就是丢一句提示词&#xff0c;然后“等它写完”。 但你有没有遇到这些情况&#xff1a; AI写得很快&#xff0c;学境…...

【工作笔记】 WSL开启报错

【工作笔记】 WSL开启报错 时间&#xff1a;2025年5月30日16:50:42 1.现象 Installing, this may take a few minutes... WslRegisterDistribution failed with error: 0x80370114 Error: 0x80370114 ??????????????????Press any key to continue......