1.准备工作
1.1.前端页面展示
1.2 数据库的建立
我们通过注册页面,考虑如何设计用户表数据库。
- 用户id,userId
- 用户名,唯一,username
- 用户密码,password(包括密码和确认密码ensurePssword【数据库没有该字段】)
- 同时还需要考虑是否需要将图片和用户进行分离
//我考虑的是将其合并在一起
//这样做的好处是直接和userId相对应
//省去了其余的操作
5.需要存储图片名字(picname)
6.需要存储图片地址(path)
-- 数据库
drop database if exists `chatroom`;
create database if not exists `chatroom` character set utf8;
-- 使用数据库
use `chatroom`;
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
`userId` INT PRIMARY KEY AUTO_INCREMENT,
`username` varchar(200) NOT NULL,
`password` varchar(200) NOT NULL,
`picname` varchar(200) NOT NULL,
`path` varchar(255) NOT NULL
);
2.前端代码
2.1 model
@Data
public class User {
private Integer userId;
private String username;
private String password;
private String picname;
private String path;
}
2.2 service
package com.example.demo.service;
import com.example.demo.mapper.UserMapper;
import org.springframework.beans.factory.annotation.Autowired;
@org.springframework.stereotype.Service
public class UserService {
@Autowired
private UserMapper userMapper;
public Boolean insertUserInfo(String username,String password,String picname,String path){
Integer influncefactor=userMapper.insertUserInfo(username,password,picname,path);
if(influncefactor>0){
return true;
}
return false;
}
}
2.3 controller
package com.example.demo.controller;
import com.example.demo.config.AppConfig;
import com.example.demo.config.Result;
import com.example.demo.constant.Constant;
import com.example.demo.mapper.UserMapper;
import com.example.demo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.io.IOException;
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
private UserService userService;
@Autowired
private BCryptPasswordEncoder bCryptPasswordEncoder;
@RequestMapping("/register")
public Result registerUser(@RequestParam MultipartFile file,String username,String password,String ensurePassword){
//当旧密码与新密码所输入的一样,且都不为空才会进行后续操作
if(!StringUtils.hasLength(username)||!StringUtils.hasLength(password)||!StringUtils.hasLength(ensurePassword)){
return Result.fail(Constant.RESULT_CODE_MESSAGENULL,"你所输入的信息为空,违规");
}
//两个密码必须一致才可以进行后续操作
if(!password.equals(ensurePassword)){
return Result.fail(Constant.RESULT_CODE_NOTSAMEPASSWORD,"两次密码输入不一致,违规");
}
String fileName=file.getOriginalFilename();
String path = Constant.SAVE_PATH +fileName;
File dest=new File(path);
//图片可以是同一张图片
//直接上传图片
try {
file.transferTo(dest);
} catch (IOException e) {
e.printStackTrace();
return Result.fail(Constant.RESULT_CODE_UPLOADPICFAIL,"图片上传失败");
}
//同时要将密码进行加密BCrypt
String newpassword=bCryptPasswordEncoder.encode(password);
//将所输入的数据存入数据库中
if(userService.insertUserInfo(username,newpassword,fileName,path)){
return Result.success(true);
}
return Result.fail(Constant.RESULT_CODE_FAIL,"上传数据库失败");
}
}
3.前端接口测试
每个if语句都需要判断一次
3.1 成功情况
3.2 用户名相同情况
3.3 有一个输入为空的情况
3.4 两次输入的密码不同
4.考虑的问题,图像为空
我们允许图像为空,故需要考虑将本地的文件转为MultipartFile。
数据库存入默认头像
根据文件路径获取 MultipartFile 文件_multipartfile他通过路径获取-CSDN博客
4.1 Controller更改
package com.example.demo.controller;
import com.example.demo.config.Method;
import com.example.demo.config.Result;
import com.example.demo.constant.Constant;
import com.example.demo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.io.IOException;
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
private UserService userService;
@Autowired
private BCryptPasswordEncoder bCryptPasswordEncoder;
@RequestMapping("/register")
public Result registerUser(@RequestParam(required = false) MultipartFile file, String username, String password, String ensurePassword) {
//当旧密码与新密码所输入的一样,且都不为空才会进行后续操作
if (!StringUtils.hasLength(username) || !StringUtils.hasLength(password) || !StringUtils.hasLength(ensurePassword)) {
return Result.fail(Constant.RESULT_CODE_MESSAGENULL, "你所输入的信息为空,违规");
}
//两个密码必须一致才可以进行后续操作
if (!password.equals(ensurePassword)) {
return Result.fail(Constant.RESULT_CODE_NOTSAMEPASSWORD, "两次密码输入不一致,违规");
}
//需要查询是否存在相同的username,若否则不能注册,让用户重命名
if (!userService.selectByUsername(username)) {
return Result.fail(Constant.RESULT_CODE_SAMEUSERNAME, "用户名不能相同,违规");
}
String fileName;
MultipartFile mfile;
String path;
//如果图片为空,则保存默认的图片
if (file == null) {
fileName = Constant.PIC;
path = Constant.SAVE_PATH +fileName;
mfile = Method.getMulFileByPath(path);
} else {
fileName = file.getOriginalFilename();
path = Constant.SAVE_PATH +fileName;
mfile=file;
}
File dest=new File(path);
try {
mfile.transferTo(dest);
} catch (IOException e) {
e.printStackTrace();
return Result.fail(Constant.RESULT_CODE_UPLOADPICFAIL,"图片上传失败");
}
//同时要将密码进行加密BCrypt
String newpassword=bCryptPasswordEncoder.encode(password);
//将所输入的数据存入数据库中
if(userService.insertUserInfo(username,newpassword,fileName,path)){
return Result.success(true);
}
return Result.fail(Constant.RESULT_CODE_FAIL,"上传数据库失败");
}
}
4.2 Method类
package com.example.demo.config;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileItemFactory;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.commons.CommonsMultipartFile;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;
public class Method {
public static MultipartFile getMulFileByPath(String picPath) {
FileItem fileItem = createFileItem(picPath);
MultipartFile mfile = new CommonsMultipartFile(fileItem);
return mfile;
}
public static FileItem createFileItem(String filePath) {
FileItemFactory factory = new DiskFileItemFactory(16, null);
String textFieldName = "textField";
int num = filePath.lastIndexOf(".");
String extFile = filePath.substring(num);
FileItem item = factory.createItem(textFieldName, "text/plain", true,
"MyFileName" + extFile);
File newfile = new File(filePath);
int bytesRead = 0;
byte[] buffer = new byte[8192];
try
{
FileInputStream fis = new FileInputStream(newfile);
OutputStream os = item.getOutputStream();
while ((bytesRead = fis.read(buffer, 0, 8192))
!= -1)
{
os.write(buffer, 0, bytesRead);
}
os.close();
fis.close();
}
catch (IOException e)
{
e.printStackTrace();
}
return item;
}
}
4.3 前端测试
5.前后端交互
5.1 register.html
</head>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>用户注册界面</title>
<link rel="stylesheet" href="css/common.css">
<link rel="stylesheet" href="css/register.css">
<link rel="stylesheet" href="css/upload.css">
</head>
<body>
<!-- 导航栏 -->
<div class="nav">
网页聊天
</div>
<div class="container">
<h1>新用户注册</h1>
<br>
<form enctype="multipart/form-data" id="file_upload" class="headForm" onsubmit="return false" action="##">
<div id="test-image-preview" class="iconfont icon-bianjitouxiang">
<input type="file" name="test" id="test-image-file" class="fileHead" accept="image/gif, image/jpeg, image/png, image/jpg" multiple="multiple">
</div>
<div class="headMain">
<span class="file">上传头像</span>
<p id="test-file-info" class="fileName"></p>
</div>
<br>
<div>
<span class="p">*</span>
<label for="username">用户名</label>
<input type="text" name="" id="username" placeholder="" class="register"><br><br>
<span class="q">*</span>
<label for="pwd">登录密码</label>
<input type="password" name="" id="pwd" class="register"><br><br>
<span class="q">*</span>
<label for="c_pwd">确认密码</label>
<input type="password" name="" id="c_pwd" class="register"><br><br>
<input type="checkbox" class="checkbox" name="">
<span style="font-size:15px">我已阅读并同意《用户注册协议》</span>
<br><br>
<input type="submit" name="" value="同意以上协议并注册" class="submit" onclick="register(this)"><br>
<a href="login.html" class="left">返回首页</a>
<a href="login.html" class="right">开始登录</a>
</form>
</div>
</div>
<script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script>
<script type="text/javascript" src="js/register.js"></script>
<script type="text/javascript" src="js/upload.js"></script>
<script>
</script>
</body>
</html>
5.2 register.js
var checkbox=document.getElementsByClassName('checkbox');
function register(btn){
if(checkbox[0].checked==true){
submitmessage();
}else{
alert("请先阅读并同意《用户注册协议》!")
}
}
function submitmessage(){
var formData = new FormData();
formData.append('file', $('.fileHead')[0].files[0]);
formData.append('username', $("#username").val());
formData.append('password', $("#pwd").val());
formData.append('ensurePassword', $("#c_pwd").val());
var name11 = formData.get("#username");
$.ajax({
type: 'post',
url: '/user/register',
processData: false,
async: false,
contentType: false,
cache: false,// 使用同步操作
timeout: 50000, //超时时间:50秒
data: formData,
success: function (result) { // 返回成功
// console.log(result);
console.log(name11);
if(result!=null&&result.status==200){
alert("注册账号成功,跳转到登陆页面,开始进行聊天吧!")
location.href="login.html"
return;
}else if(result!=null&&result.status==-10){
alert("用户名不能相同,违规");
}else if(result!=null&&result.status==-8){
alert("两次密码输入不一致,违规");
}else if(result!=null&&result.status==-6){
alert("你所输入的信息为空,违规");
}else{
alert("注册错误,请联系工作人员")
}
},
error: function () {
alert("接口错误"); // 返回失败
}
})
}
5.3 upload.js
var fileInput = document.getElementById('test-image-file'),//文件框,里面存的是文件,fileHead
info = document.getElementById('test-file-info'),//文件名
preview = document.getElementById('test-image-preview');//文件框,头像显示界面
dataBase64 = '',
preview.style.backgroundImage = 'url(../img/个人头像.png)'; //默认显示的图片
// 监听change事件:
fileInput.addEventListener('change', upImg);
// 头像上传逻辑函数
function upImg() {
preview.style.backgroundImage = ''; // 清除背景图片
if (!fileInput.value) { // 检查文件是否选择:(此时文件中什么都没选择)
$('#test-image-preview').addClass('icon-bianjitouxiang');
info.innerHTML = '没有选择文件';
} else {
$('#test-image-preview').removeClass('icon-bianjitouxiang');
info.innerHTML = '';//此时上传文件成功
}
var file = fileInput.files[0]; // 获取File引用
var size = file.size;
if (size >= 100 * 1024 * 1024) { //判断文件大小
info.innerHTML = '文件大于100兆不行!';
preview.style.backgroundImage = '';
$('#test-image-preview').addClass('icon-bianjitouxiang');
return false;
}
if (file.type !== 'image/jpeg' && file.type !== 'image/png' && file.type !== 'image/gif') { // 获取File信息:
info.innerHTML = '不是有效的图片文件!';
preview.style.backgroundImage = '';
$('#test-image-preview').addClass('icon-bianjitouxiang');
return;
}
// 读取文件:
var reader = new FileReader();
reader.onload = function (e) {
dataBase64 = e.target.result; // 'data:image/jpeg;base64,/9j/4AAQSk...(base64编码)...}'
preview.style.backgroundImage = 'url(' + dataBase64 + ') ';
preview.style.backgroundRepeat = 'no-repeat';
preview.style.backgroundSize = ' 100% 100%';
};
// 以DataURL的形式读取文件:
reader.readAsDataURL(file);
// console.log(file);
}
5.4 upload.css
.reHead{
margin: 15px 4%;
}
.headForm{
text-align: center;
padding: 40px 0 70px 0;
}
#test-image-preview {
position: relative;
display: inline-block;
width: 100px;
height: 100px;
border-radius: 50px;
background: #F5F5F5;
color: #fff;
font-size: 60px;
text-align: center;
line-height: 100px;
background-size: contain;
background-repeat: no-repeat;
background-position: center center;
margin-bottom: 26px;
}
.fileHead{
position: absolute;
width: 100px;
height: 100px;
right: 0;
top: 0;
opacity: 0;
}
.content-format {
font-size: 12px;
font-weight: 400;
color: rgba(153, 153, 153, 1);
}
.headMain{
height: 40px;
}
.file {
position: relative;
background: #fff;
color: #F39800;
font-weight:800;
}
.file input {
position: absolute;
font-size: 12px;
right: 0;
top: 0;
opacity: 0;
}
.fileName {
line-height: 28px;
font-size: 12px;
font-weight: 400;
color: rgba(51, 51, 51, 1);
}
.but{
text-align: center;
}
.orangeHead{
width: 40%;
height: 40px;
background: #f60;
border: none;
}
.orangeHead a{
color: #fff;
}
5.5 register.css
body{
background: url("../img/coolgirl.jpg");
background-size:100% 100%;
background-attachment: fixed;
}
.container{
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%,-50%);
}
img{
width: 4rem;
height: 4rem;
margin-left: 50%;
transform: translateX(-50%);
margin-top: 0.853rem;
border-radius: 50%;
}
#js_logo_img{
width: 4rem;
height: 4rem;
position: absolute;
left: 50%;
transform: translateX(-50%);
top: 30%;
opacity: 0;
}
h2{
color: #000066;
font-size: 0.853rem;
text-align: center;
margin: 0;
}
form{
width: 450px;
margin: 0 auto;
background: #FFF;
border-radius: 15px;
position: relative;
}
h1{
font-size: 28px;
text-align: center;
color: #FFF;
}
.p{
color: red;
margin-left: 33px;
display: inline-block;
/* 不占单独一行的块级元素 */
}
label{
font-size: 18px;
font-weight: bold;
}
.register{
height: 35px;
width: 300px;
}
.q{
color:red;
margin-left:17px;
display:inline-block;
}
.checkbox{
margin-left: 100px;
display: inline-block;
width: 15px;
height: 15px;
}
.submit{
border-radius: 7px;
margin-left: 150px;
height: 35px;
width: 150px;
background-color: #000;
border: none;
display: block;
padding: 0;
color: #FFF;
font-weight: bold;
cursor: pointer;
}
a{
text-decoration: none;
font-weight: bold;
}
.left,.right{
position: absolute;
bottom: 20px;
}
.left{
left: 20px;
}
.right{
right: 20px;
}
5.6 common.css
/* 放置页面的公共样式 */
/* 去除浏览器默认样式 */
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
/* 设定页面高度 */
html, body {
height: 100%;
}
/* 设定导航栏的样式 */
.nav {
height: 50px;
background-color: black;
color: rgb(255, 255, 255);
/* 使用弹性布局, 让导航栏内部的元素, 垂直方向居中 */
display: flex;
align-items: center;
/* 让里面的元素距离左侧边框, 有点间隙 */
padding-left: 20px;
}
5.7 测试
注册成功!!!