【电商项目实战】上传头像(详细篇)
创始人
2024-03-15 11:34:12

在这里插入图片描述
🍁博客主页:👉@不会压弯的小飞侠
欢迎关注:👉点赞👍收藏留言
系列专栏:👉SpringBoot电商项目实战
学习社区: 👉不会压弯的小飞侠
知足上进,不负野心。
🔥欢迎大佬指正,一起学习!一起加油!

在这里插入图片描述

目录

  • 🍁上传头像-持久层
    • 🔥 接口与抽象方法
    • 🔥 配置SQL映射
    • 🔥 测试
  • 🍁上传头像-业务层
    • 🔥 接口与抽象方法
    • 🔥 实现抽象方法
    • 🔥 测试
  • 🍁上传头像-控制层
    • 🔥 异常
    • 🔥 用户提交的请求
    • 🔥 处理请求
  • 🍁上传头像-前端页面
    • 🔥设置上传文件大小
    • 🔥 前端页面BUG解决
    • 🔥 登录后显示头像
    • 🔥 显示最新头像


🍁上传头像-持久层

  • 上传文件的操作其实是:先将用户上传的文件保存到服务器端的某个位置,然后将保存文件的路径记录在数据库中。当后续需要使用该文件时,从数据库中读出文件的路径,即可实现在线访问该文件。
  • 在持久层处理数据库中的数据时,只需要关心如何记录头像文件的路径,并不需要考虑上传时保存文件的过程

🔥 接口与抽象方法

  • 在UserMapper接口中添加updateAvatarByUid()抽象方法。
 /*** 根据uid更新用户的头像* @param uid 用户的id* @param avatar 新头像的路径* @param modifiedUser 修改执行人* @param modifiedTime 修改时间* @return 受影响的行数*/Integer updateAvatarByUid(@Param("uid") Integer uid,@Param("avatar") String avatar,@Param("modifiedUser") String modifiedUser,@Param("modifiedTime") Date modifiedTime);

🔥 配置SQL映射

  • 在UserMapper.xml中配置updateAvatarByUid()抽象方法的映射。
 UPDATEt_userSETavatar = #{avatar},modified_user = #{modifiedUser},modified_time = #{modifiedTime}WHEREuid = #{uid}

🔥 测试

  • 在UserMapperTests中编写并执行单元测试。
@Testpublic void updateAvatarByUid() {Integer uid = 11;String avatar = "/upload/avatar.png";String modifiedUser = "管理员";Date modifiedTime = new Date();Integer rows = userMapper.updateAvatarByUid(uid, avatar, modifiedUser, modifiedTime);System.out.println("rows=" + rows);}
  • 测试结果

在这里插入图片描述

🍁上传头像-业务层

🔥 接口与抽象方法

  • 在IUserService中添加changeAvatar(Integer uid, String username, String avatar)抽象方法。
 /*** 修改用户头像* @param uid 当前登录的用户的id* @param username 当前登录的用户名* @param avatar 用户的新头像的路径*/void changeAvatar(Integer uid, String username, String avatar);

🔥 实现抽象方法

  • 在UserServiceImpl类中实现changeAvatar(Integer uid, String username, String avatar)方法。
@Overridepublic void changeAvatar(Integer uid, String username, String avatar) {// 调用userMapper的findByUid()方法,根据参数uid查询用户数据User result = userMapper.findByUid(uid);// 检查查询结果是否为nullif (result == null) {// 是:抛出UserNotFoundExceptionthrow new UserNotFoundException("用户数据不存在");}// 检查查询结果中的isDelete是否为1if (result.getIsDelete().equals(1)) {// 是:抛出UserNotFoundExceptionthrow new UserNotFoundException("用户数据不存在");}// 创建当前时间对象Date now = new Date();// 调用userMapper的updateAvatarByUid()方法执行更新,并获取返回值Integer rows = userMapper.updateAvatarByUid(uid, avatar, username, now);// 判断以上返回的受影响行数是否不为1if (rows != 1) {// 是:抛出UpdateExceptionthrow new UpdateException("更新用户数据时出现未知错误,请联系系统管理员");}}

🔥 测试

  • 在UserServiceTests类中进行单元测试。
@Testpublic void changeAvatar() {Integer uid = 11;String username = "lll";String avatar = "/upload/change.png";iUserService.changeAvatar(uid, username, avatar);}
  • 测试结果

在这里插入图片描述

🍁上传头像-控制层

🔥 异常

  • 在处理上传文件的过程中,用户可能会选择错误的文件上传,此时就应该抛出对应的异常并进行处理。所以需要创建文件上传相关异常的基类,即在com.jkj.controller.ex包下创建FileUploadException类,并继承自RuntimeException类。
/** 文件上传相关异常的基类 */
public class FileUploadException extends RuntimeException {public FileUploadException() {super();}public FileUploadException(String message) {super(message);}public FileUploadException(String message, Throwable cause) {super(message, cause);}public FileUploadException(Throwable cause) {super(cause);}protected FileUploadException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {super(message, cause, enableSuppression, writableStackTrace);}
}
  • 在处理上传的文件过程中,经分析可能会产生以下异常。这些异常类都需要继承自FileUploadException类。
// 上传的文件为空
com.jkj.controller.ex.FileEmptyException
// 上传的文件大小超出了限制值
com.jkj.store.controller.ex.FileSizeException
// 上传的文件类型超出了限制
com.jkj.store.controller.ex.FileTypeException
// 上传的文件状态异常
com.jkj.store.controller.ex.FileStateException
// 上传文件时读写异常
com.jkj.store.controller.ex.FileUploadIOException
  • 按照如下规则编写上面这五个异常类
  • 创建FileEmptyException异常类,并继承FileUploadException类。
  • 创建FileSizeException异常类,并继承FileUploadException类。
  • 创建FileTypeException异常类,并继承FileUploadException类。
  • 创建FileStateException异常类,并继承FileUploadException类。
  • 创建FileUploadIOException异常类,并继承FileUploadException类。
/** 上传的文件为空的异常,例如没有选择上传的文件就提交了表单,或选择的文件是0字节的空文件 */
public class FileEmptyException extends FileUploadException {public FileEmptyException() {super();}public FileEmptyException(String message) {super(message);}public FileEmptyException(String message, Throwable cause) {super(message, cause);}public FileEmptyException(Throwable cause) {super(cause);}protected FileEmptyException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {super(message, cause, enableSuppression, writableStackTrace);}
}
  • 在BaseController的handleException()的@ExceptionHandler注解中添加FileUploadException.class异常的处理;最后在方法中处理这些异常。
@ExceptionHandler({ServiceException.class, FileUploadException.class})
public JsonResult handleException(Throwable e) {JsonResult result = new JsonResult(e);if (e instanceof UsernameDuplicateException) {result.setState(4000);} else if (e instanceof UserNotFoundException) {result.setState(4001);} else if (e instanceof PasswordNotMatchException) {result.setState(4002);} else if (e instanceof InsertException) {result.setState(5000);} else if (e instanceof UpdateException) {result.setState(5001);} else if (e instanceof FileEmptyException) {result.setState(6000);} else if (e instanceof FileSizeException) {result.setState(6001);} else if (e instanceof FileTypeException) {result.setState(6002);} else if (e instanceof FileStateException) {result.setState(6003);} else if (e instanceof FileUploadIOException) {result.setState(6004);}return result;
}

🔥 用户提交的请求

  • 请求路径:/users/change_avatar
  • 请求参数:MultipartFile file, HttpSession session
  • 请求类型:POST
  • 响应结果:JsonResult

🔥 处理请求

  • 在UserController类中添加处理请求的changeAvatar(@RequestParam(“file”) MultipartFile file, HttpSession session)方法。
 /** 头像文件大小的上限值(10MB) */public static final int AVATAR_MAX_SIZE = 10 * 1024 * 1024;/** 允许上传的头像的文件类型 */public static final List AVATAR_TYPES = new ArrayList();/** 初始化允许上传的头像的文件类型 */static {AVATAR_TYPES.add("image/jpeg");AVATAR_TYPES.add("image/png");AVATAR_TYPES.add("image/bmp");AVATAR_TYPES.add("image/gif");}@PostMapping("change_avatar")public JsonResult changeAvatar(@RequestParam("file") MultipartFile file, HttpSession session) {// 判断上传的文件是否为空if (file.isEmpty()) {// 是:抛出异常throw new FileEmptyException("上传的头像文件不允许为空");}// 判断上传的文件大小是否超出限制值if (file.getSize() > AVATAR_MAX_SIZE) { // getSize():返回文件的大小,以字节为单位// 是:抛出异常throw new FileSizeException("不允许上传超过" + (AVATAR_MAX_SIZE / 1024) + "KB的头像文件");}// 判断上传的文件类型是否超出限制String contentType = file.getContentType();// public boolean list.contains(Object o):当前列表若包含某元素,返回结果为true;若不包含该元素,返回结果为false。if (!AVATAR_TYPES.contains(contentType)) {// 是:抛出异常throw new FileTypeException("不支持使用该类型的文件作为头像,允许的文件类型:\n" + AVATAR_TYPES);}// 获取当前项目的绝对磁盘路径String parent = session.getServletContext().getRealPath("upload");// 保存头像文件的文件夹File dir = new File(parent);if (!dir.exists()) {dir.mkdirs();}// 保存的头像文件的文件名String suffix = "";String originalFilename = file.getOriginalFilename();int beginIndex = originalFilename.lastIndexOf(".");if (beginIndex > 0) {suffix = originalFilename.substring(beginIndex);}String filename = UUID.randomUUID().toString() + suffix;// 创建文件对象,表示保存的头像文件File dest = new File(dir, filename);// 执行保存头像文件try {file.transferTo(dest);} catch (IllegalStateException e) {// 抛出异常throw new FileStateException("文件状态异常,可能文件已被移动或删除");} catch (IOException e) {// 抛出异常throw new FileUploadIOException("上传文件时读写错误,请稍后重尝试");}// 头像路径String avatar = "/upload/" + filename;// 从Session中获取uid和usernameInteger uid = getUidFromSession(session);String username = getUsernameFromSession(session);// 将头像写入到数据库中userService.changeAvatar(uid, username, avatar);// 返回成功头像路径return new JsonResult(OK, avatar);}
  • 启动项目,打开浏览器先登录,再访问http://localhost:8080/web/upload.html进行测试。

在这里插入图片描述

🍁上传头像-前端页面

🔥设置上传文件大小

  • SpringBoot中默认MultipartResolver的最大文件大小值为1M。如果上传的文件的大小超过1M,会抛FileSizeLimitExceededException异常。
  • 如果需要调整上传的限制值,直接在启动类中添加getMultipartConfigElement()方法,并且在启动类之前添加@Configuration注解。
@Beanpublic MultipartConfigElement getMultipartConfigElement() {MultipartConfigFactory factory = new MultipartConfigFactory();// DataSize dataSize = DataSize.ofMegabytes(10);// 设置文件最大10M,DataUnit提供5中类型B,KB,MB,GB,TBfactory.setMaxFileSize(DataSize.of(10, DataUnit.MEGABYTES));factory.setMaxRequestSize(DataSize.of(10, DataUnit.MEGABYTES));// 设置总上传数据总大小10Mreturn factory.createMultipartConfig();}
  • 还可以通过在application.properties中添加配置来实现。
spring.http.multipart.max-file-size=10MB
spring.http.multipart.max-request-size=10MB

🔥 前端页面BUG解决

  • 头像上传成功后,显示上传的头像。在upload.html页面中,是使用img标签来显示头像图片的。首先确定img标签是否添加有id="img-avatar"属性,便于后续访问该标签;而img标签是通过src属性来决定显示哪张图片的,所以修改src该属性的值即可设置需要显示的图片。修改表单添加id="form-change-avatar"属性。修改input标签,添加id="btn-change-avatar"和type="button"属性。
  • 在upload.html页面中body标签内部的最后,添加script标签用于编写JavaScript程序。
    • processData:处理数据。默认情况下,processData的值是true,其代表以对象的形式 上传的数据都会被转换为字符串的形式上传。而当上传文件的时候,则不需要把其转换为字符串,因此要改成false。
    • contentType:发送数据的格式。其代表的是前端发送数据的格式,默认值application/x-www-form-urlencoded。代表的是ajax的 data是以字符串的形式传递,使用这种传数据的格式,无法传输复杂的数据,比如多维数组、文件等。把contentType设置为false就会改掉之前默认的数据格式,在上传文件时就不会报错。

🔥 登录后显示头像

  • 当用户登录成功后,将服务器返回的头像路径存储到本地的Cookie中,在打开“上传头像”页面时,从本地的Cookie中读取头像路径并显示即可。在登录login.html页面中,当登录成功后,将用户头像路径保存到Cookie中。
$("#btn-login").click(function() {$.ajax({url: "/users/login",type: "POST",data: $("#form-login").serialize(),dataType: "json",success: function(json) {if (json.state == 200) {alert("登录成功!");$.cookie("avatar", json.data.avatar, {expires: 7});console.log("cookie中的avatar=" + $.cookie("avatar"));location.href = "index.html";} else {alert("登录失败!" + json.message);}}});
});
  • 语法:$.cookie(名称,值,[option])。[option]参数说明:

    • expires:有限日期,可以是一个整数或一个日期(单位天)。如果不设置这个值,默认情况下浏览器关闭之后此Cookie就会失效。
    • path:表示Cookie值保存的路径,默认与创建页路径一致。
    • domin:表示Cookie域名属性,默认与创建页域名一样。要注意跨域的概念,如果要主域名二级域名有效则要设置“.xxx.com”。
    • secrue:布尔类型的值,表示传输Cookie值时,是否需要一个安全协议。
  • 在upload.html页面中,默认并没有引用jqueyr.cookie.js文件,因此无法识别$.cookie()函数;所以需要在upload.html页面head标签内添加jqueyr.cookie.js文件。


  • 在打开页面时自动读取显示用户图像。获取Cookie中头像的路径,然后将获取到的头像路径设置给img标签的src属性以显示头像。在upload.html页面中的script标签的内部添加自动读取用户图像的jquery代码。
$(document).ready(function () {console.log("cookie中的avatar=" + $.cookie("avatar"));$("#img-avatar").attr("src", $.cookie("avatar"));
});

🔥 显示最新头像

  • 每次打开页面时,读取Cookie中的头像并显示”,如果此时重新上传用户头像,而Cookie中所保存的头像还是之前上传的头像路径值,无法显示最新的用户头像。所以当用户重新上传头像后,还应把新头像的路径更新到Cookie中。
  • 在upload.html页面中,用户头像修改成功后,并将新的用户头像路径保存到Cookie中。
$.cookie("avatar", json.data, {expires: 7});
  • 最终代码

  • 启动项目进行测试
    在这里插入图片描述

学习视频:

【SpringBoot项目实战完整版】SpringBoot+MyBatis+MySQL电脑商城项目实战-哔哩哔哩】
https://b23.tv/qGh9x9L

在这里插入图片描述

相关内容

热门资讯

应用未安装解决办法 平板应用未... ---IT小技术,每天Get一个小技能!一、前言描述苹果IPad2居然不能安装怎么办?与此IPad不...
长白山自助游攻略 吉林长白山游... 昨天介绍了西坡的景点详细请看链接:一个人的旅行,据说能看到长白山天池全凭运气,您的运气如何?今日介绍...
脚上的穴位图 脚面经络图对应的... 人体穴位作用图解大全更清晰直观的标注了各个人体穴位的作用,包括头部穴位图、胸部穴位图、背部穴位图、胳...
demo什么意思 demo版本... 618快到了,各位的小金库大概也在准备开闸放水了吧。没有小金库的,也该向老婆撒娇卖萌服个软了,一切只...
猫咪吃了塑料袋怎么办 猫咪误食... 你知道吗?塑料袋放久了会长猫哦!要说猫咪对塑料袋的喜爱程度完完全全可以媲美纸箱家里只要一有塑料袋的响...
世界上最漂亮的人 世界上最漂亮... 此前在某网上,选出了全球265万颜值姣好的女性。从这些数量庞大的女性群体中,人们投票选出了心目中最美...
埃菲尔铁塔在哪 中国仿建埃菲尔... 2019年4月26日,广西南宁市,街头惊现一座巨型山寨版埃菲尔铁塔,高约20米,白色塔身,造型逼真,...
北京的名胜古迹 北京最著名的景... 北京从元代开始,逐渐走上帝国首都的道路,先是成为大辽朝五大首都之一的南京城,随着金灭辽,金代从海陵王...
苗族的传统节日 贵州苗族节日有... 【岜沙苗族芦笙节】岜沙,苗语叫“分送”,距从江县城7.5公里,是世界上最崇拜树木并以树为神的枪手部落...
应用未安装解决办法 平板应用未... ---IT小技术,每天Get一个小技能!一、前言描述苹果IPad2居然不能安装怎么办?与此IPad不...
demo什么意思 demo版本... 618快到了,各位的小金库大概也在准备开闸放水了吧。没有小金库的,也该向老婆撒娇卖萌服个软了,一切只...
长白山自助游攻略 吉林长白山游... 昨天介绍了西坡的景点详细请看链接:一个人的旅行,据说能看到长白山天池全凭运气,您的运气如何?今日介绍...
脚上的穴位图 脚面经络图对应的... 人体穴位作用图解大全更清晰直观的标注了各个人体穴位的作用,包括头部穴位图、胸部穴位图、背部穴位图、胳...
猫咪吃了塑料袋怎么办 猫咪误食... 你知道吗?塑料袋放久了会长猫哦!要说猫咪对塑料袋的喜爱程度完完全全可以媲美纸箱家里只要一有塑料袋的响...