Fork me on GitHub

Mockjs基本语法

Mock.js 基本语法与应用笔记

Mock.js 是一个基于 JavaScript 的模拟数据生成库,它可以帮助开发者在前端开发过程中模拟后端API,提供测试数据。Mock.js 的主要功能是生成各种类型的模拟数据,包括文本、数字、日期、数组等,同时也支持拦截请求和响应,使得前端可以在不依赖后端API的情况下进行开发和测试。

1. Mock 基本语法

生成指定字符

1
2
3
4
5
6
7
import Mock from 'mockjs';

const data = Mock.mock({
str: '🤣'
});

console.log(data);

指定字符和具体个数

1
2
3
const data = Mock.mock({
'str|3': '🤣'
});

指定字符和区间

1
2
3
const data = Mock.mock({
'str|3-5': '🤣'
});

生成随机字符

1
2
3
4
5
6
const data = Mock.mock({
// 一串字母
str: '@word'
// 一个汉字
// str: '@cword'
});

指定个数

1
2
3
4
5
const data = Mock.mock({
// 3 个中文汉字
'str|3': '@cword'
// str: '@cword(3)'
});

指定区间

1
2
3
4
5
const data = Mock.mock({
// 3 到 5 个中文字符
'str|3-5': '@cword'
// str: '@cword(3, 5)'
});

生成标题和句子

1
2
3
4
const data = Mock.mock({
title: '@ctitle',
sentence: '@csentence'
});

可以指定标题和句子的汉字长度和范围

1
2
3
4
5
// 长度
const data = Mock.mock({
title: '@ctitle(3)',
sentence: '@csentence(10)'
});
1
2
3
4
5
// 范围
const data = Mock.mock({
title: '@ctitle(3, 5)',
sentence: '@csentence(10, 15)'
});

生成段落

1
2
3
const data = Mock.mock({
content: '@cparagraph'
});

指定段落的个数和范围

1
2
3
4
5
// 注意这里 3 代表的是 3 个段落,而不是 3 个中文字符
// 1 个句号是 1 段
const data = Mock.mock({
content: '@cparagraph(3)'
});

生成数字

生成指定数字

1
2
3
4
5
const data = Mock.mock({
// number: 20,
// 如果是数值型的 value 将失去意义,最终都表示竖线右边的 20,一般写个 1
'number|20': 1
});

生成数字区间

1
2
3
const data = Mock.mock({
'number|1-10': 1
});

生成增量 ID

1
2
3
4
5
6
7
8
9
for (let i = 0; i < 10; i++) {
const data = Mock.mock({
// 默认 1,每次增加 1
// id: '@increment'
// 每次增加 10
id: '@increment(10)'
});
console.log(data);
}

身份证号、姓名、地址

1
2
3
4
5
const data = Mock.mock({
id: '@id',
name: '@cname',
address: '@city(true)'
});

生成图片

1
2
3
4
5
const data = Mock.mock({
// 大小、背景色、前景色、格式、文字
// 注意:颜色要是十六进制,不支持关键字,例如 red
image: "@image('200x200', '#f00', '#fff', 'jpg', 'H')"
});

生成时间

1
2
3
4
const data = Mock.mock({
time1: '@date', // 年-月-日
time2: '@date("yyyy-MM-dd HH:mm:ss")'
});

指定数组返回的长度和范围

1
2
3
4
5
6
7
const data = Mock.mock({
'list|1-3': [{
name: '@cname',
address: '@city(true)',
id: '@increment(1)'
}]
});

2. Mock 拦截请求

POST => /api/users

1
2
3
4
5
6
7
8
Mock.mock(/^\/api\/users/, 'post', (options) => {
const user = JSON.parse(options.body);
user.id = data.list.length ? data.list[data.list.length - 1].id + 1 : 1;
data.list.push(user);
return {
status: 200
};
});

DELETE => /api/users/:id

1
2
3
4
5
6
7
8
9
Mock.mock(/^\/api\/user\/.+/, 'delete', (options) => {
const id = options.url.split('/').pop();
// !注意 id 变成了字符串
const idx = data.list.findIndex((item) => item.id === +id);
data.list.splice(idx, 1);
return {
status: 200
};
});

PUT => /api/users/:id

1
2
3
4
5
6
7
8
Mock.mock(/^\/api\/users\/.+/, 'put', (options) => {
const user = JSON.parse(options.body);
const idx = data.list.findIndex((item) => item.id === +user.id);
data.list[idx] = user;
return {
status: 200
};
});

GET => /api/users

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
Mock.mock(/^\/api\/user/, 'get', (options) => {
const {
pagenum,
pagesize,
query,
id
} = qs.parse(options.url.split('?')[1]);
if (id) {
const user = data.list.find((item) => item.id === +id);
return {
status: 200,
user
};
}
// 1 10 0 ~ 10
// 2 10 10 ~ 20
const start = (pagenum - 1) * pagesize;
const end = pagenum * pagesize;
const total = data.list.length;
const totalPage = Math.ceil(data.list.length / pagesize);
let list = [];
if (pagenum > totalPage) {
list = [];
} else {
list = data.list.slice(start, end);
}
return {
status: 200,
list,
total
};
});

GET => /api/users/:id

1
2
3
4
5
6
7
8
Mock.mock(/^\/api\/users\/.+/, 'get', (options) => {
const id = options.url.split('/').pop();
const user = data.list.find((item) => item.id === +id);
return {
status: 200,
user
};
});

3. Vue 中测试

User.vue

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
<template>
<div class="users">
<el-card class="box-card">
<el-table :data="tableData" style="width: 100%">
<el-table-column prop="date" label="日期" width="180" />
<el-table-column prop="name" label="姓名" width="130" />
<el-table-column prop="address" label="地址" width="180" />
<el-table-column
fixed="right"
label="操作"
width="80"
align="right"
>
<template slot-scope="scope">
<el-button
@click="deleteRow(scope.row)"
type="text"
size="small"
>
删除
</el-button>
</template>
</el-table-column>
</el-table>
<el-pagination
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
:current-page="q.pagenum"
:page-sizes="[5, 10, 15, 20]"
:page-size="q.pagesize"
layout="total, sizes, prev, pager, next, jumper"
:total="total"
>
</el-pagination>
</el-card>
</div>
</template>

<script>
import axios from 'axios';
export default {
data() {
return {
tableData: [],
q: {
pagenum: 1,
pagesize: 5,
query: ''
},
total: 0
};
},
created() {
this.getUsers();
},
methods: {
async getUsers() {
const {
data: { list, total }
} = await axios.get('/api/users', {
params: this.q
});
this.tableData = list;
this.total = total;
},
handleSizeChange(pagesize) {
this.q.pagesize = pagesize;
this.getUsers();
},
handleCurrentChange(pagenum) {
this.q.pagenum = pagenum;
this.getUsers();
},
async deleteRow({ id }) {
const { data } = await axios.delete(`/api/user/${id}`)
if (data.status === 200) {
this.getUsers();
this.$message.success('删除成功');
}
}
}
};
</script>
<style>
.box-card {
margin: 40px auto 0;
width: 630px;
}
.clearfix:before,
.clearfix:after {
display: table;
content: '';
}
.clearfix:after {
clear: both;
}
.el-pagination {
margin-top: 15px;
}
</style>

4. 另一种使用方式

vue.config.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
const qs = require('querystring');
const Mock = require('mockjs');
const data = Mock.mock({
'list|27': [
{
id: '@increment(1)',
date: '@date(yyyy-MM-dd hh:mm:ss)',
name: '@cname',
address: '@city(true)'
}
]
});
module.exports = {
devServer: {
before(app) {
app.get('/api/users', (req, res) => {
const { pagenum, pagesize, query } = qs.parse(
req.url.split('?')[1]
);
const start = (pagenum - 1) * pagesize;
const end = pagenum * pagesize;
const total = data.list.length;
const totalPage = Math.ceil(data.list.length / pagesize);
let list = [];
if (pagenum > totalPage) {
list = [];
} else {
list = data.list.slice(start, end);
}
res.send({
status: 200,
list,
total
});
});
app.delete('/api/users/:id', (req, res) => {
const idx = data.list.findIndex(
(item) => item.id === +req.params.id
);
data.list.splice(idx, 1);
res.send({
status: 200
});
});
}
}
};

5. vite 项目

vite.config.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import { defineConfig } from "vite";
import vue from "@vitejs/plugin-vue";
import { viteMockServe } from "vite-plugin-mock";

// https://vitejs.dev/config/
export default defineConfig({
plugins: [
vue(),
viteMockServe({
mockPath: "mock",
localEnabled: true,
}),
],
});

mock/index.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
import Mock from 'mockjs'
// 内存模拟数据
const arr = []
for (let i = 0; i < 10; i++) {
arr.push({
id: Mock.mock('@guid'),
name: Mock.mock('@cname'),
place: Mock.mock('@county(true)'),
})
}
export default [
{
url: '/list',
method: 'get',
response: () => {
return arr
}
},
{
url: '/del',
method: 'delete',
response: ({query}) => {
const index = arr.findIndex((item) => item.id === query.id)
arr.splice(index, 1)
return { success: true }
}
}
]

CentOS如何安装jdk

CentOS上安装JDK的详细教程

在CentOS系统上安装Java开发工具包(JDK)是一个常见的任务,特别是对于需要运行Java应用程序的服务器。本教程将指导您如何在CentOS上安装JDK 8和JDK 17。

1. 卸载已有的Java版本(如果需要)

在安装新版本的JDK之前,您可能需要卸载系统上已安装的Java版本。

1
rpm -qa | grep java

上述命令将列出所有已安装的Java相关包。如果需要卸载,请使用以下命令:

1
rpm -e --nodeps <已安装的JDK包名>

2. 安装JDK 8

2.1 下载JDK 8

首先,下载JDK 8的安装包。这里我们使用华为云提供的JDK 8镜像。

1
wget https://repo.huaweicloud.com/java/jdk/8u202-b08/jdk-8u202-linux-x64.tar.gz

2.2 创建安装目录

创建一个目录用于存放JDK。

1
mkdir -p /usr/local/java

2.3 解压JDK到安装目录

将下载的JDK包解压到/usr/local/java目录。

1
tar -zxvf jdk-8u202-linux-x64.tar.gz -C /usr/local/java

2.4 配置环境变量

编辑/etc/profile文件,添加JDK的环境变量。

1
vim /etc/profile

在文件末尾添加以下内容:

1
2
3
4
export JAVA_HOME=/usr/local/java/jdk1.8.0_202
export JRE_HOME=${JAVA_HOME}/jre
export CLASSPATH=.:${JAVA_HOME}/lib:${JRE_HOME}/lib
export PATH=${JAVA_HOME}/bin:$PATH

保存并退出编辑器。

2.5 使环境变量生效

使环境变量立即生效。

1
source /etc/profile

2.6 验证安装

检查JDK版本。

1
java -version

3. 安装JDK 17

3.1 下载JDK 17

下载JDK 17的安装包。这里我们使用Oracle官方提供的JDK 17镜像。

1
wget https://download.oracle.com/java/17/latest/jdk-17_linux-x64_bin.tar.gz

3.2 解压JDK到安装目录

解压JDK 17到/usr/local/java目录。

1
tar -zxvf jdk-17_linux-x64_bin.tar.gz -C /usr/local/java

3.3 配置环境变量

编辑/etc/profile文件,添加JDK 17的环境变量。

1
vim /etc/profile

在文件末尾添加以下内容(请确保替换为正确的解压后的目录名):

1
2
3
export JAVA_HOME=/usr/local/java/jdk-17
export PATH=$PATH:$JAVA_HOME/bin
export PATH

保存并退出编辑器。

3.4 使环境变量生效

使环境变量立即生效。

1
source /etc/profile

3.5 验证安装

检查JDK版本。

1
java -version

4. Java后台运行命令

如果您需要在后台运行Java应用程序,可以使用以下命令:

1
nohup java -jar your-application.jar > output.log 2>&1 &

这将使Java应用程序在后台运行,并将输出重定向到output.log文件。

CentOS如何安装mysql

CentOS如何安装mysql

卸载MySQL

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
rpm -qa|grep -i mysql
rpm -ev --nodeps mysql-community-common-5.7.44-1.el7.x86_64
rpm -ev --nodeps mysql57-community-release-el7-10.noarch
rpm -ev --nodeps mysql-community-libs-5.7.44-1.el7.x86_64
rpm -ev --nodeps mysql-community-server-5.7.44-1.el7.x86_64
rpm -ev --nodeps mysql-community-client-5.7.44-1.el7.x86_64

find / -name mysql
rm -rf /var/lib/mysql
rm -rf /var/lib/mysql/mysql
rm -rf /usr/lib64/mysql
rm -rf /usr/share/mysql

rm -rf /etc/my.cnf
rpm -qa|grep -i mysql
rpm -qa | grep mariadb

安装5.7版本

下载

1
2
3
4
5
6
7
8
wget -i -c http://dev.mysql.com/get/mysql57-community-release-el7-10.noarch.rpm
yum -y install mysql57-community-release-el7-10.noarch.rpm
yum -y install mysql-community-server
rpm --import https://repo.mysql.com/RPM-GPG-KEY-mysql-2022
systemctl start mysqld.service
systemctl status mysqld.service
grep "password" /var/log/mysqld.log
mysql -uroot -p

配置远程连接

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
set global validate_password_policy=0;
set global validate_password_mixed_case_count=0;
set global validate_password_number_count=0;
set global validate_password_special_char_count=0;
set global validate_password_length=3;
ALTER USER 'root'@'localhost' IDENTIFIED BY 'password';

grant all privileges on *.* to 'root'@'%' identified by 'password' with grant option;
flush privileges;
exit

firewall-cmd --zone=public --add-port=3306/tcp --permanent
firewall-cmd --zone=public --add-port=8080/tcp --permanent
firewall-cmd --reload

service mysqld restart

status

[client]
default-character-set=utf8
datadir=/var/lib/mysql
socket=/var/lib/mysql/mysql.sock
character-set-server=utf8
collation-server=utf8_general_ci

set character_set_client=utf8;
set character_set_connection=utf8;
set character_set_database=utf8;
set character_set_results=utf8;
set character_set_server=utf8;

systemctl enable mysqld
systemctl daemon-reload

安装8.0版本

下载

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
sudo rpm -Uvh https://dev.mysql.com/get/mysql80-community-release-el7-3.noarch.rpm

sudo yum --enablerepo=mysql80-community install mysql-community-server

grep "A temporary password" /var/log/mysqld.log;

ALTER USER 'root'@'localhost' IDENTIFIED BY 'Password@123';
--可以使用以下命令进行修改
--密码验证策略低要求(0或LOW代表低级)
set global validate_password.policy=MEDIUM;
--密码至少要包含的小写字母个数和大写字母个数
set global validate_password.mixed_case_count=0;
--密码至少要包含的数字个数。
set global validate_password.number_count=0;
--密码至少要包含的特殊字符数
set global validate_password.special_char_count=0;
-- 密码长度
set global validate_password.length=8;

show variables like 'validate%';
ALTER USER 'root'@'localhost' IDENTIFIED BY 'password';

配置远程连接

1
2
3
4
5
6
7
use mysql;
select host, user, authentication_string, plugin from user;

CREATE USER 'root'@'%' IDENTIFIED BY 'password';
grant all privileges on *.* to 'root'@'%';
flush privileges;

检查运行状态

1
2
systemctl status mysqld
ps -ef|grep mysqld

解决前端项目中Node.js版本不一致导致的依赖安装错误

解决前端项目中Node.js版本不一致导致的依赖安装错误

在前端开发中,我们经常会遇到Node.js版本不一致导致的问题,尤其是在团队协作或者在不同的环境中部署应用时。本文将详细介绍如何诊断和解决由于Node.js版本不一致导致的依赖安装错误。

问题分析

当您在项目中运行npm install时,可能会遇到以下错误:

1
2
npm ERR! code ENOPEERINVALID
npm ERR! peer invalid! Failed to install xxx@yyy. Please install the companion package zzz.

这个错误通常意味着您尝试安装的某个包与当前项目中的其他包存在版本冲突。这可能是因为您的项目依赖了不同版本的同一个包,或者您的Node.js版本与某些包不兼容。

解决方法

1. 确认Node.js版本

首先,确认您的Node.js版本。在命令行中运行:

1
node -v

记下当前的版本号,然后检查您的项目是否指定了Node.js版本。在项目的package.json文件中,查看是否有engines字段,它定义了项目兼容的Node.js版本范围。

2. 使用--legacy-peer-deps选项

如果您确定Node.js版本不是问题的根本原因,那么您可以尝试使用--legacy-peer-deps选项来忽略peer依赖的版本冲突。这个选项在npm 7及以上版本中可用。

1
npm install --legacy-peer-deps

这将允许您安装依赖,即使它们之间存在版本冲突。但是,请注意,这可能会导致运行时错误,因为某些包可能不兼容。

3. 手动解决依赖冲突

如果--legacy-peer-deps选项无法解决问题,您可能需要手动检查和解决依赖冲突。这通常涉及到以下步骤:

  1. 查看package.jsonpackage-lock.json文件,找出存在冲突的包。
  2. 在npm的官网上查找这些包的版本历史和兼容性信息。
  3. 更新package.json中的依赖版本,以解决冲突。您可能需要与团队成员协商,以确保所有环境都使用兼容的版本。
  4. 重新运行npm install

4. 使用nvm管理Node.js版本

为了避免版本不一致的问题,您可以使用nvm(Node Version Manager)来管理不同项目的Node.js版本。这样,您可以为每个项目设置特定的Node.js版本。

安装nvm后,您可以使用以下命令切换Node.js版本:

1
nvm use <version>

在项目目录中运行此命令,然后再次尝试安装依赖。

Vue命令行式组件的封装

ue命令行式组件的封装

一. 组件封装

1.解决js文件中如何书写渲染html
1
2
3
4
5
6
7
8
9
10
render(ctx) {
const {$props, $emit} = ctx;
return <DivModal>
<DivBox>
<h4>{$props.msg}</h4>
<DivButton click={$emit('onClick')}>确认</DivButton>
</DivBox>
</DivModal>;
}
/*使用render方法将html元素渲染到页面上,在showMsg函数中,创建了一个div元素,使用createApp方法创建一个vue实例app,将div挂载到app应用中。*/
2.解决js文件中如何将html元素加上样式
1
2
//package.json
"@styils/vue": "^1.1.6",
1
2
3
4
5
6
7
8
9
10
//showMsg.js
const DivModal = styled('div', {
position: 'fixed',
width: '100%',
height: '100%',
top: '0',
left: '0',
background: 'rgba(0,0,0,.4)',
})
/*参考react中的方法,使用styiles第三方库实现,将需要加样式的元素声明为一个对象代替,将对象赋予上样式完成。*/

使用时div 标签由 DivModal 代替:

1
2
3
4
5
6
7
8
//showMsg.js
render(ctx) {
const {$props, $emit} = ctx;
//div 标签由 DivModal 代替
return <DivModal>
...
</DivModal>;
}
3.showMsg.js 组件全部代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
//showMsg.js
// import MessageBox from "@/components/MessageBox.vue";
import {createApp} from "vue";
import {styled} from "@styils/vue";

const DivModal = styled('div', {
position: 'fixed',
width: '100%',
height: '100%',
top: '0',
left: '0',
background: 'rgba(0,0,0,.4)',
})

const DivBox = styled('div', {
position: 'fixed',
width: '300px',
height: '100px',
top: '40%',
left: 'calc(50% - 150px)',
background: 'white',
borderRadius: '10px',
border: '2px solid #707070',
color: '#000',
display: 'flex',
flexDirection: 'column',
justifyContent: 'center',
alignItems: 'center',
})

const DivButton = styled('el-button', {
cursor: 'pointer',
borderRadius: '5px',
padding: '5px 10px',
background: '#409eff',
color: 'white',
fontSize: '14px',
})


const MessageBox = {
props: {
msg: {
type: String,
required: true,
}
},
render(ctx) {
const {$props, $emit} = ctx;
return <DivModal>
<DivBox>
<h4>{$props.msg}</h4>
<DivButton click={$emit('onClick')}>确认</DivButton>
</DivBox>
</DivModal>;
}
}

function showMsg(msg, clickHandle) {
//创建一个div元素
const div = document.createElement('div')
document.body.appendChild(div)
//创建app应用
const app = createApp(MessageBox, {
msg, onClick() {
clickHandle & clickHandle(() => {
//卸载app应用
app.unmount(div);
//移除div元素
div.remove();
})
}
});
//将div挂载到app应用上
app.mount(div);
}

export default showMsg;

二. 如何使用组件

1.导入
1
2
//MessageView.vue
import showMsg from '@/components/showMsg';
2.使用
1
2
3
4
5
6
7
//MessageView.vue
const clickHandle = () => {
showMsg('我是弹出框,点击确认隐藏', (close) => {
console.log('点击了确认')
close();
});
}
3.示例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//MessageView.vue
<template>
<div>
<el-button type="primary" @click="clickHandle">显示</el-button>
</div>
</template>

<script setup>
import showMsg from '@/components/showMsg';

const clickHandle = () => {
showMsg('我是弹出框,点击确认隐藏', (close) => {
console.log('点击了确认')
close();
});
}

</script>

<style scoped>

</style>

Vue工程使用vuex

Vue工程使用vuex

1.vuex简介

  1. Vuex 可以帮助我们管理共享状态,并附带了更多的概念和框架。这需要对短期和长期效益进行权衡。
    如果您不打算开发大型单页应用,使用 Vuex 可能是繁琐冗余的。确实是如此——如果您的应用够简单,您最好不要使用 Vuex。一个简单的 store 模式就足够您所需了。但是,如果您需要构建一个中大型单页应用,您很可能会考虑如何更好地在组件外部管理状态,Vuex 将会成为自然而然的选择。引用 Redux 的作者 Dan Abramov 的话说就是:

2.下载vuex

2.

1
2
npm install vuex@next --save
yarn add vuex@next --save

3.新建vuex配置文件

  1. 在src目录下新建目录命名为store,在store目录下新建index.js文件,写入如下配置。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import {createStore} from "vuex";
const $store = createStore({
//state可以看作是全局公共的数据,需要的组件可以调用
state: {
count: 0,
},
getters: {},
//mutations可以看作是调用state的事件,在组件中可以通过commit方法调用和响应
mutations: {
increment(state, payload) {
state.count = state.count + 1
console.log('payload:' + payload)
},
},
actions: {},
modules: {}
})

export default $store;

4.导入配置文件

  1. 在main.js中导入刚刚写好的文件
1
2
import $store from '@/store';
app.use($store);

5.用例

5.在需要使用的组件同样写入

1
import $store from '@/store';

就可以开始使用了

1

Spring Boot工程集成验证码生成与验证功能教程

[TOC]

Spring Boot工程集成验证码生成与验证功能教程

验证码是一种常见的安全机制,用于防止自动化工具(如爬虫)对网站进行恶意操作。在Web应用中,验证码通常以图像的形式出现,要求用户输入图像中显示的字符。本文将介绍如何在Spring Boot工程中实现一个随机生成验证码的功能。

1. 创建验证码工具类

首先,我们需要创建一个工具类VerifyCodeUtils,用于生成随机验证码并输出为图像。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Arrays;
import java.util.Random;

public class VerifyCodeUtils {
//使用到Algerian字体,系统里没有的话需要安装字体,字体只显示大写,去掉了1,0,i,o几个容易混淆的字符
public static final String VERIFY_CODES = "23456789ABCDEFGHJKLMNPQRSTUVWXYZ";
private static Random random = new Random();


/**
* 使用系统默认字符源生成验证码
*
* @param verifySize 验证码长度
* @return
*/
public static String generateVerifyCode(int verifySize) {
return generateVerifyCode(verifySize, VERIFY_CODES);
}

/**
* 使用指定源生成验证码
*
* @param verifySize 验证码长度
* @param sources 验证码字符源
* @return
*/
public static String generateVerifyCode(int verifySize, String sources) {
if (sources == null || sources.length() == 0) {
sources = VERIFY_CODES;
}
int codesLen = sources.length();
Random rand = new Random(System.currentTimeMillis());
StringBuilder verifyCode = new StringBuilder(verifySize);
for (int i = 0; i < verifySize; i++) {
verifyCode.append(sources.charAt(rand.nextInt(codesLen - 1)));
}
return verifyCode.toString();
}

/**
* 生成随机验证码文件,并返回验证码值
*
* @param w
* @param h
* @param outputFile
* @param verifySize
* @return
* @throws IOException
*/
public static String outputVerifyImage(int w, int h, File outputFile, int verifySize) throws IOException {
String verifyCode = generateVerifyCode(verifySize);
outputImage(w, h, outputFile, verifyCode);
return verifyCode;
}

/**
* 输出随机验证码图片流,并返回验证码值
*
* @param w
* @param h
* @param os
* @param verifySize
* @return
* @throws IOException
*/
public static String outputVerifyImage(int w, int h, OutputStream os, int verifySize) throws IOException {
String verifyCode = generateVerifyCode(verifySize);
outputImage(w, h, os, verifyCode);
return verifyCode;
}

/**
* 生成指定验证码图像文件
*
* @param w
* @param h
* @param outputFile
* @param code
* @throws IOException
*/
public static void outputImage(int w, int h, File outputFile, String code) throws IOException {
if (outputFile == null) {
return;
}
File dir = outputFile.getParentFile();
if (!dir.exists()) {
dir.mkdirs();
}
try {
outputFile.createNewFile();
FileOutputStream fos = new FileOutputStream(outputFile);
outputImage(w, h, fos, code);
fos.close();
} catch (IOException e) {
throw e;
}
}

/**
* 输出指定验证码图片流
*
* @param w
* @param h
* @param os
* @param code
* @throws IOException
*/
public static void outputImage(int w, int h, OutputStream os, String code) throws IOException {
int verifySize = code.length();
BufferedImage image = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);
Random rand = new Random();
Graphics2D g2 = image.createGraphics();
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
Color[] colors = new Color[5];
Color[] colorSpaces = new Color[]{Color.WHITE, Color.CYAN,
Color.GRAY, Color.LIGHT_GRAY, Color.MAGENTA, Color.ORANGE,
Color.PINK, Color.YELLOW};
float[] fractions = new float[colors.length];
for (int i = 0; i < colors.length; i++) {
colors[i] = colorSpaces[rand.nextInt(colorSpaces.length)];
fractions[i] = rand.nextFloat();
}
Arrays.sort(fractions);

g2.setColor(Color.GRAY);// 设置边框色
g2.fillRect(0, 0, w, h);

Color c = getRandColor(200, 250);
g2.setColor(c);// 设置背景色
g2.fillRect(0, 2, w, h - 4);

//绘制干扰线
Random random = new Random();
g2.setColor(getRandColor(160, 200));// 设置线条的颜色
for (int i = 0; i < 20; i++) {
int x = random.nextInt(w - 1);
int y = random.nextInt(h - 1);
int xl = random.nextInt(6) + 1;
int yl = random.nextInt(12) + 1;
g2.drawLine(x, y, x + xl + 40, y + yl + 20);
}

// 添加噪点
float yawpRate = 0.05f;// 噪声率
int area = (int) (yawpRate * w * h);
for (int i = 0; i < area; i++) {
int x = random.nextInt(w);
int y = random.nextInt(h);
int rgb = getRandomIntColor();
image.setRGB(x, y, rgb);
}

shear(g2, w, h, c);// 使图片扭曲

g2.setColor(getRandColor(100, 160));
int fontSize = h - 4;
Font font = new Font("Algerian", Font.ITALIC, fontSize);
g2.setFont(font);
char[] chars = code.toCharArray();
for (int i = 0; i < verifySize; i++) {
AffineTransform affine = new AffineTransform();
affine.setToRotation(Math.PI / 4 * rand.nextDouble() * (rand.nextBoolean() ? 1 : -1), (w / verifySize) * i + fontSize / 2, h / 2);
g2.setTransform(affine);
g2.drawChars(chars, i, 1, ((w - 10) / verifySize) * i + 5, h / 2 + fontSize / 2 - 10);
}

g2.dispose();
ImageIO.write(image, "jpg", os);
}

private static Color getRandColor(int fc, int bc) {
if (fc > 255)
fc = 255;
if (bc > 255)
bc = 255;
int r = fc + random.nextInt(bc - fc);
int g = fc + random.nextInt(bc - fc);
int b = fc + random.nextInt(bc - fc);
return new Color(r, g, b);
}

private static int getRandomIntColor() {
int[] rgb = getRandomRgb();
int color = 0;
for (int c : rgb) {
color = color << 8;
color = color | c;
}
return color;
}

private static int[] getRandomRgb() {
int[] rgb = new int[3];
for (int i = 0; i < 3; i++) {
rgb[i] = random.nextInt(255);
}
return rgb;
}

private static void shear(Graphics g, int w1, int h1, Color color) {
shearX(g, w1, h1, color);
shearY(g, w1, h1, color);
}

private static void shearX(Graphics g, int w1, int h1, Color color) {

int period = random.nextInt(2);

boolean borderGap = true;
int frames = 1;
int phase = random.nextInt(2);

for (int i = 0; i < h1; i++) {
double d = (double) (period >> 1)
* Math.sin((double) i / (double) period
+ (6.2831853071795862D * (double) phase)
/ (double) frames);
g.copyArea(0, i, w1, 1, (int) d, 0);
if (borderGap) {
g.setColor(color);
g.drawLine((int) d, i, 0, i);
g.drawLine((int) d + w1, i, w1, i);
}
}

}

private static void shearY(Graphics g, int w1, int h1, Color color) {

int period = random.nextInt(40) + 10; // 50;

boolean borderGap = true;
int frames = 20;
int phase = 7;
for (int i = 0; i < w1; i++) {
double d = (double) (period >> 1)
* Math.sin((double) i / (double) period
+ (6.2831853071795862D * (double) phase)
/ (double) frames);
g.copyArea(i, 0, 1, h1, 0, (int) d);
if (borderGap) {
g.setColor(color);
g.drawLine(i, (int) d, i, 0);
g.drawLine(i, (int) d + h1, i, h1);
}

}

}
}

2. 控制层实现

在Spring Boot的控制器中,我们需要提供一个接口来生成验证码并将其发送给客户端。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// VerifyController.java
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class VerifyController {

@GetMapping("/generateImageCode")
public void generateImageCode(HttpSession session, HttpServletResponse response) throws IOException {
//随机生成四位随机数
String code = VerifyCodeUtils.generateVerifyCode(4);
//保存到session域中
session.setAttribute("code", code);
//根据随机数生成图片,reqponse响应图片
response.setContentType("image/png");
ServletOutputStream os = response.getOutputStream();
VerifyCodeUtils.outputImage(130, 60, os, code);
}
}

3. 客户端展示验证码

在Web页面中,我们需要添加一个图像标签来展示验证码。

1
2
<!-- 在HTML中添加验证码图像 -->
<img src="/generateImageCode" alt="验证码" onclick="this.src='/generateImageCode?'+Math.random();" />

4. 验证验证码

在用户提交表单时,我们需要验证用户输入的验证码是否正确。这通常在后端进行,通过比较用户输入的验证码与Session中保存的验证码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 在登录方法中验证验证码
@PostMapping("/login")
public ResponseEntity<?> login(@RequestParam String username, @RequestParam String password, HttpSession session) {
// 获取用户输入的验证码
String userInputCode = request.getParameter("code");
// 获取Session中保存的验证码
String sessionCode = (String) session.getAttribute("code");
// 验证验证码
if (!sessionCode.equals(inputCode)) {
// 验证码错误
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body("验证码错误");
}
// 验证码正确,继续登录逻辑
// ...
}

Spring boot工程实现图片上传的两种方法

[TOC]

Spring Boot中实现图片上传功能的两种策略

摘要

在现代Web应用程序中,图片上传是一个常见的功能。本文将介绍如何在Spring Boot项目中实现图片上传,包括将图片保存到阿里云OSS和本地文件系统两种方法。我们将通过代码示例和详细注释,帮助读者理解这两种方法的实现过程。

1. 保存到阿里云OSS

1.1 依赖添加

首先,我们需要在项目的pom.xml文件中添加阿里云OSS的SDK依赖,以及用于文件操作的commons-iocommons-beanutils库。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<!-- 阿里云OSS SDK -->
<dependency>
<groupId>com.aliyun.oss</groupId>
<artifactId>aliyun-sdk-oss</artifactId>
<version>3.16.1</version>
</dependency>
<!-- 文件操作工具类 -->
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.11.0</version>
</dependency>
<dependency>
<groupId>commons-beanutils</groupId>
<artifactId>commons-beanutils</artifactId>
<version>1.9.4</version>
</dependency>

1.2 配置OSS客户端

创建一个工具类uploadUtil,用于配置OSS客户端并实现图片上传功能。我们需要设置OSS的域名、访问密钥ID和密钥,以及OSS的地域节点。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClientBuilder;
import org.apache.commons.io.FilenameUtils;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.util.UUID;

public class UploadUtil {
// OSS域名,注意替换为实际的域名
public static final String ALI_DOMAIN = "https://czh123-text.oss-cn-guangzhou.aliyuncs.com";

// 上传图片到OSS的方法
public static String uploadImage(MultipartFile file) throws IOException {
// 获取原始文件名和扩展名
String originalFilename = file.getOriginalFilename();
String ext = "." + FilenameUtils.getExtension(originalFilename);
// 生成新的文件名,包含UUID以避免重名
String uuid = UUID.randomUUID().toString().replace("-", "");
String fileName = uuid + ext;

// OSS配置信息
String endpoint = "http://oss-cn-guangzhou.aliyuncs.com"; // 地域节点
String accessKeyId = "LTAI5tGOUpuc5EwDcJ9"; // 访问密钥ID
String accessKeySecret = "fYy0DdFrrFBwky"; // 访问密钥Secret

// 创建OSS客户端
OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
// 上传文件到OSS
ossClient.putObject("czh123-text", fileName, file.getInputStream());
// 关闭客户端
ossClient.shutdown();
// 返回图片的URL
return ALI_DOMAIN + fileName;
}
}

1.3 控制层实现

在控制器中,我们创建一个upImg方法,用于处理图片上传请求。该方法调用uploadUtil类中的uploadImage方法,并返回图片的URL。

1
2
3
4
@PostMapping("/upImg")
public String upImg(MultipartFile file) throws IOException {
return UploadUtil.uploadImage(file);
}

2. 保存到本地文件系统

2.1 控制层实现

另一种方法是将图片保存到本地文件系统。在控制器中,我们创建一个upload方法,用于处理上传请求并将图片保存到指定的本地目录。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
@PostMapping("/upload")
public String upload(MultipartFile file) {
if (file.isEmpty()) {
return "图片为空";
}
// 获取原始文件名和扩展名
String originalFilename = file.getOriginalFilename();
String fileNamePrefix = new SimpleDateFormat("yyyyMMddHHmmssSSS").format(new Date());
String fileNameSuffix = "." + originalFilename.split("\\.")[1];
// 生成新的文件名
String fileName = fileNamePrefix + fileNameSuffix;

// 获取项目根目录的绝对路径
ApplicationHome applicationHome = new ApplicationHome(this.getClass());
String pre = applicationHome.getDir().getParentFile().getParentFile().getAbsolutePath() +
"/src/main/resources/static/images/";
String path = pre + fileName;

try {
// 将上传的文件保存到本地
file.transferTo(new File(path));
// 返回文件的本地路径
return path;
} catch (IOException e) {
e.printStackTrace();
}
return "图片上传失败";
}

🎉 结语

本文介绍了在Spring Boot项目中实现图片上传的两种方法:保存到阿里云OSS和保存到本地文件系统。通过这两种方法,开发者可以根据项目需求和资源情况选择合适的图片存储策略。阿里云OSS提供了高可用性和扩展性,适合需要远程访问和高并发的场景;而本地文件系统则适合小型项目或对数据安全性要求不高的场景。在实际开发中,开发者应根据实际情况灵活选择。

感谢你的访问,期待与你在技术的道路上相遇!👋🌟🚀

如何在Ubuntu编写运行C语言

如何在Ubuntu编写运行C语言

1.准备工作(检查网络&更新源)

1
2
3
4
5
6
7
8
ping www.baidu.com

services.msc

VMware DHCP Service
VMware NAT Service

sudo apt update

2.ssh

1
2
3
4
5
6
7
8
9
10
11
12
sudo ps -e |grep ssh

sudo apt install openssh-server

sudo ps -e |grep ssh

service ufw stop

sudo apt install net-tools

ifconfig

3.gcc g++

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
sudo apt install gcc

sudo apt install build-essential

sudo apt install g++
````

## 4.vim

```bat
sudo apt install vim
````

## 5.测试

```bat
vim hello.c

gcc -Wall hello.c -o hello

gcc hello.c -o hello

./hello

6.切换用户

1
2
3
4
5
6
7
8
9
10
sudo passwd root

su root

sudo adduser he

sudo deluser he

w

1
2
3
4
5
6
#include<stdio.h>
int main(void)
{
printf("hello world!\n");
return 0;
}

Vue工程setup函数详解

Vue工程setup函数详解

一、简介

Vue3 的一大特性函数 —- setup
  1、setup函数是处于 生命周期函数 beforeCreate 和 Created 两个钩子函数之间的函数 也就说在 setup函数中是无法 使用 data 和 methods 中的数据和方法的
  2、setup函数是 Composition API(组合API)的入口
  3、在setup函数中定义的变量和方法最后都是需要 return 出去的 不然无法再模板中使用

二、代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
<template>
<div>
<div>{{ readersNumber }}</div>
<el-button type="primary" @click="onClick">自定义点击事件</el-button>
</div>
</template>

<script>
/* 方式一:(script 右边写上 setup)*/
// import {onBeforeMount, ref} from "vue";
// const readersNumber = ref('绿色回收')
// const onClick = () => {
// console.log(readersNumber.value)
// if (typeof readersNumber.value === "string") {
// console.log('string')
// } else {
// console.log('other')
// }
// // alert(readersNumber)
// }
// onBeforeMount(() => {
// console.log('数据渲染前')
// })


/* 方式二:*/
// import {onBeforeMount, ref} from "vue";
// export default {
// name: "OrderForm_BP",
// setup() {
// const readersNumber = ref('OrderForm_BP')
// const onClick = () => {
// console.log(readersNumber.value)
// if (typeof readersNumber.value === "string") {
// console.log('string')
// } else {
// console.log('other')
// }
// // alert(readersNumber)
// }
// onBeforeMount(()=>{
// console.log('数据渲染前')
// })
// return {
// onClick,
// readersNumber,
// }
// }
// }

/* 方式三:*/
import {reactive, toRefs} from "vue";

export default {
name: "SetupDemo",
setup() {
const data = reactive({
readersNumber: "",
userList: [],
tableList: [],
})
const onClick = function () {
console.log(data.readersNumber, '自定义点击事件')
data.readersNumber = 'readersNumber数据'
if (data.readersNumber === "readersNumber") {
console.log('string')
} else {
console.log('other')
}
}
return {
//解构
...toRefs(data),
onClick
}
}
}

</script>

<style scoped>

</style>

三、说明

NO.1 setup写在script标签内。

使用ref绑定数据,无需export,import的组件页面内可以直接使用,但需要反复const,ref数据。调用数据{数据名}.value。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<script setup>
import {onBeforeMount, ref} from "vue";
const readersNumber = ref('setup写在script标签内')
const onClick = () => {
console.log(readersNumber.value)
if (typeof readersNumber.value === "string") {
console.log('string')
} else {
console.log('other')
}
// alert(readersNumber)
}
onBeforeMount(() => {
console.log('数据渲染前')
})

</script>

NO.2 setup写在js中。

使用ref绑定数据,事件和数据都需要return出去。调用数据{数据名}.value。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<script>
import {ref} from "vue";
export default {
name: "SetupDemo",
setup() {
const readersNumber = ref('setup写在js中')
const onClick = () => {
console.log(readersNumber.value)
if (typeof readersNumber.value === "string") {
console.log('string')
} else {
console.log('other')
}
// alert(readersNumber)
}
return {
onClick,
readersNumber,
}
}
}
</script>

NO.3 setup写在js中,使用reactive打包页面内数据,再使用toRefs解构。

避免数据反复const和return,事件需要return出去。调用数据data.{数据名}。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<script>
import {reactive, toRefs} from "vue";

export default {
name: "SetupDemo",
setup() {
//data可以自定义名字
const data = reactive({
readersNumber: "setup写在js中"
})
const onClick = function () {
console.log(data.readersNumber, '自定义点击事件')
data.readersNumber = 'setup写在js中'
if (data.readersNumber === "readersNumber") {
console.log('string')
} else {
console.log('other')
}
}
return {
//解构
...toRefs(data),
onClick
}
}
}
</script>

四、PS:关于vue3生命周期介绍

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
<script>
import {onBeforeMount, onBeforeUnmount, onBeforeUpdate, onMounted, onUnmounted, onUpdated, reactive, toRefs} from "vue";

export default {
name: "SetupDemo",
setup() {
const data = reactive({
readersNumber: "",
})
const onClick = function () {
console.log(data.readersNumber, '自定义点击事件')
data.readersNumber = 'readersNumber'
if (data.readersNumber === "readersNumber") {
console.log('string')
} else {
console.log('other')
}
}
//生命周期
onBeforeMount(() => {
console.log('数据渲染前')
})
onMounted(() => {
console.log('渲染结束')
})
onBeforeUpdate(() => {
console.log('页面Dom更新之前')
})
onUpdated(() => {
console.log('页面Dom更新之后')
})
onBeforeUnmount(() => {
console.log('Vue实例销毁之前')
})
onUnmounted(() => {
console.log('Vue实例销毁之后')
})
return {
//解构
...toRefs(data),
onClick
}
}
}
</script>
  • Copyrights © 2022-2024 何福海
  • 访问人数: | 浏览次数:

请我喝杯奶茶吧~

支付宝
微信