上传头像
1. 上传头像-持久层
1.1 SQL语句的规划
将对应的文件保存在操作系统上,然后再把这个文件的路径记录下来,因为在记录路径的时候是非常方便的,将来如果要打开这个文件可以依据这个路径去找到这个文件。在数据库中需要保存这个文件路径即可。将所有的静态资源(图片、文件、其他资源文件)放到某台电脑上,在把这台电脑作为一台单独的服务器使用。
对应是一个更新用户avatar字段的SQL语句。
update t_user set avatar=?,modified_user=?,modified_time=? where uid=?
1.2 设计接口和抽象方法
UserMapper接口中来定义一个抽象方法用于修改用户的头像。
/** * @Param("SQL映射文件#{}占位符的变量名"): * 解决的问题:当SQL语句的占位符和映射接口方法参数名不一致时,需要将某个参数强行注入到某个占位符变量上时, * 可以使用@Param这个注解映射的关系 * * 根据用户的uid表示修改用户的头像 * @param uid 用户的uid * @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);
1.3 接口的映射
UserMapper.xml中编写映射的SQL语句
<!-- pathVariable("user") String username --> <!-- param("user") String username --> <update id="updateAvatarByUid"> UPDATE t_user SET avatar=#{avatar}, modified_user=#{modifiedUser}, modified_time=#{modifiedTime} WHERE uid=#{uid} </update>
UserMapper–updateAvatarByUid
测试
在测试类中编写测试的方法
@Test public void updateAvatarByUid(){ Integer rows = userMapper.updateAvatarByUid(8,"/upload/avatar.png","管理员",new Date()); System.out.println(rows); }
2. 上传头像-业务层
2.1 规划异常
1.用户数据不存在,找不到对应的用户的数据
2.更新的时候,各种未知的异常产生
无需重复开发
2.2 设计接口和抽象方法
/** * 修改用户的头像 * @param uid 用户的uid * @param avatar 用户的头像的路径 * @param username 用户的名称 */ void changeAvatar(Integer uid, String avatar, String username);
2.3 实现抽象方法
编写业务层的更新用户头像的方法
@Override public void changeAvatar(Integer uid, String avatar, String username) { //查询当前的用户的数据是否存在 User result = userMapper.findByUid(uid); if (result==null||result.getIsDelete()==1){ throw new UserNotFoundException("用户数据不存在"); } Integer rows = userMapper.updateAvatarByUid(uid, avatar, username, new Date()); if (rows!=1){ throw new UpdateException("更新数据时产生未知的异常"); } }
IUserService–changeAvatar
测试
测试业务层方法执行
@Test public void changeAvatar(){ userService.changeAvatar(8,"/upload/test.png","test02"); }
UserServiceTests–changeAvatar
3. 上传头像-控制层
3.1 规划异常
文件异常的父类 FileUploadException 泛指文件上传的异常(父类)继承RuntimeException 父类是:FileUploadException FileEmptyException 文件为空的异常 FileSizeException 文件大小超出异常 FileStateException 上传文件状态异常 FileTypeException 文件类型异常 FileUploadIOException 文件读写的异常
五个构造方法显示的声明出来,再去继承相关的父类。
controller | ex
controller–ex–文件上传
3.2 处理异常
在基类BaseController类中进行编写和统一处理。
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); }
在异常统一处理方法的参数列表增加新的异常作为它的参数。
@ExceptionHandler({ServiceException.class,FileUploadException.class})
3.3 设计请求
/users/change_avatar POST (get请求提交数据2KB) HttpSession session,MutipartFile file JsonResult<String>
3.4 实现请求
/** 设置上传文件的最大值 byte */ public static final int AVATAR_MAX_SIZE = 10 * 1024 * 1024; /** 限制上传文件的类型*/ public static final List<String> AVATAR_TYPES =new ArrayList<>(); static { AVATAR_TYPES.add("images/jpeg"); AVATAR_TYPES.add("images/png"); AVATAR_TYPES.add("images/bmp"); AVATAR_TYPES.add("images/gif"); } /** * MultipartFile 接口是由springMVC提供的接口, * 这个接口为我们包装了获取文件类型的数据(任何类型的file都可以接受), * SpringBoot它整合了SpringMVC,只需要在处理请求的方法参数列表上 * 声明一个参数类型为MultipartFile的参数, * 然后SpringBoot自动将传递给服务的文件的数据 * 赋值给这个参数 * * @RequestParam() 表示请求中的参数, * 将请求中的参数注入到请求处理方法的某个参数上, * 如果名称不一致则可以使用@RequestParam() 进行映射 * * @param session * @param file * @return */ @RequestMapping("change_avatar") public JsonResult<String> changeAvatar(HttpSession session, @RequestParam("file") MultipartFile file){ // 判断上传的文件是否为空 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); } //上传文件.../upload/文件.png // 获取当前项目的绝对磁盘路径 String parent = session.getServletContext().getRealPath("upload"); // 保存头像文件的文件夹 File dir = new File(parent); if (!dir.exists()) { dir.mkdirs(); } //获取到这个文件名称,UUID工具来将生成一个新的字符串作为文件名 //例如:avatar01.png // 保存的头像文件的文件名 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);//将file文件中的数据写入到dest文件中 } catch (IllegalStateException e) { // 抛出异常 throw new FileStateException("文件状态异常,可能文件已被移动或删除"); } catch (IOException e) { // 抛出异常 throw new FileUploadIOException("上传文件时读写错误,请稍后重尝试"); } // 返回头像路径/upload/test.png String avatar = "/upload/" + filename; // 从Session中获取uid和username Integer uid = getuidFromSession(session); String username = getUsernameFromSession(session); // 将头像写入到数据库中 userService.changeAvatar(uid, avatar,username); // 返回成功头像路径给前端页面,将来用于头像展示使用 return new JsonResult<String>(OK, avatar); }
UserController–changeAvatar
4. 上传头像-前端页面
在upload页面编写上传头像的代码
说明:如果直接使用表单进行文件的上传,需要给表单显示的添加一个属性 enctype="multipart/from-data"声明出来, 不会将目标文件的数据结构做修改在上传,不同于字符串
upload
<script type="text/javascript"> //cookie获取头像 $(document).ready(function () { console.log("cookie中的avatar=" + $.cookie("avatar")); //将cookie值获取处理设置到头像的src属性上 $("#img-avatar").attr("src", $.cookie("avatar")); }); $("#btn-change-avatar").click(function() { $.ajax({ url: "/users/change_avatar", type: "POST", data: new FormData($("#form-change-avatar")[0]), dataType: "JSON", processData: false, // processData处理数据 contentType: false, // contentType发送数据的格式 success: function(json) { if (json.state == 200) { alert("头像修改成功") //将服务器返回的头像地址设置img标签的src属性上 //attr(属性,属性值):给某个属性设置某个值 $("#img-avatar").attr("src", json.data); //显示最新头像 $.cookie("avatar", json.data, {expires: 7}); } else { alert("修改失败!" + json.message); } }, error: function(xhr) { alert("您的登录信息已经过期,请重新登录!HTTP响应码:" + xhr.status); location.href = "login.html"; } }); }); </script>
5.解决Bug
5.1 更默认的大小限制
SpringMVC默认为1MB文件进行上传,手动去修改SpringMVC默认上传文件的大小。
方式1:直接在配置文件中进行配置
#上传文件默认大小 spring.servlet.multipart.max-file-size=10MB spring.servlet.multipart.max-request-size=15MB
方式2:需要采用Java代码的形式来设置文件的上传大小的限制。主类中进行配置,可以定义一个方法,必须使用@Bean修饰。在类的前面添加一个@Configuration注解进行修改类。MultipartConfigElement类型。
@Bean public MultipartConfigElement getMultipartConfigElement() { //创建一个配置的工厂类对象 MultipartConfigFactory factory = new MultipartConfigFactory(); // DataSize dataSize = DataSize.ofMegabytes(10); // 设置文件最大10M,DataUnit提供5中类型B,KB,MB,GB,TB factory.setMaxFileSize(DataSize.of(10, DataUnit.MEGABYTES)); // 设置总上传数据总大小10M factory.setMaxRequestSize(DataSize.of(15, DataUnit.MEGABYTES)); // 通过工厂类来创建MultipartConfigElement对象 return factory.createMultipartConfig(); }
application–优化
5.2 显示头像
在页面中提供ajax请求来提交文件,提交完成后返回了JSON串,解析出data中数据,设置的img头像标签的src属性。
- serialize(): 可以将表单数据自动拼接成key=value的结构进行提交给服务器,一般提交是普通的控件类型中的数据(text\password\radio\checkbook)等等
- FormData类:将表单中的数据保持原有的结构进行数据的提交。
new FormData($("#form")[0]);//文件类型的数据可以使用FormData对象进行存储
- ajax默认处理数据时按照字符串的形式进行处理,以及默认会采用字符串的形式进行提交数据。关闭这两个默认的功能。
processData: false, // processData处理数据的形式,关闭默认形式 contentType: false, // contentType提交数据的格式,关闭默认形式
5.3 登录后显示头像
可以在更新头像成功后,将服务器返回的头像路径保存在客户端cookie对象,然后每次检测到用户打开上传头像页面,在这个页面中通过ready()方法来自动检查去读取cookie中头像并到src属性上。
1.设置cookie中的值:
导入cookie.js文件
<script src="../bootstrap3/js/jquery.cookie.js" type="text/javascript" charset="utf-8"></script>
调用cookie方法
login.html
//$.cookie(key,valye,time);//单位:天 $.cookie("avatar", json.data.avatar, {expires: 7})
2.在upload.html页面先引入cookie.js文件
<script src="../bootstrap3/js/jquery.cookie.js" type="text/javascript" charset="utf-8"></script>
3.在upload.html页main通过ready()自动读取cookie中的数据。
//cookie获取头像 $(document).ready(function () { console.log("cookie中的avatar=" + $.cookie("avatar")); //将cookie值获取处理设置到头像的src属性上 $("#img-avatar").attr("src", $.cookie("avatar")); });
5.4 显示最新头像
在更改完头像后,将最新的头像地址,再一次保存到cookie,同名保存会覆盖原有cookie的值。
//显示最新头像 $.cookie("avatar", json.data, {expires: 7});
upload.html
测试
READ–上传头像