1 上传头像-持久层
1.1 SQL语句的规划
将对应文件保存在操作系统上,然后再把这个文件路劲给记录下来,因为记录路径是非常便捷和方便,将来如果要打开这个文件可以依据这个路径去找到这个文件。在数据库中只需要保存这个文件的路径即可。将所有的静态资源(图片、文件、其他)放到某台电脑上,再将这台电脑作为一台单独的服务器使用。
对应的是一个更新用户avatar字段的SQL语句。
update t_user set avatar=?,modifier_user=?,modified_time=? where uid=?
1.2 设计接口和抽象方法
UserMapper接口中来定义个抽象方法用于修改用户的头像。
/**
* @Param("SQL映射文件中#{}占位符的变量名"):解决的问题,当SQL语句的占位符
* 和映射的接口方法参数名不一致时,需要将某个参数强行注入到某个占位符变量上时,
* 可以使用@Param这个注解来标注映射的关系
* 根据用户uid值来修改用户的头像
* @param 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语句。
<update id="updateAvatarByUid">
update t_user
set
avatar=#{avatar},
modified_user=#{modifiedUser},
modified_time=#{modifiedTime}
where
uid=#{uid}
</update>
在测试类中编写测试的方法。
@Test
public void updateAvatarByUid(){
userMapper.updateAvatarByUid(9,"upload/avatar.png","管理有",new Date());
}
2 上传头像-业务层
2.1 规划异常
1.用户数据不存在,找不到对应的用户数据
2.更新时候,各种未知异常产生。
无需重复开发
2.2 设计接口和抽象方法
/**
* 修改用户的头像
* @param uid 用户的id
* @param avatar 用户头像的路径
* @param username 用户的名称
*/
void changeAvatar(Integer uid,
String avatar,
String username
);
2.3 实现抽象方法
编写业务层的更新用户头像的方法。
public void changeAvatar(Integer uid, String avatar, String username) {
//查询当前的用户数据是否存在
User result = userMapper.findByUid(uid);
if(result==null||result.getIsDelete()==1){
throw new UsernameNotFoundException("用户没有被找到");
}
Integer rows = userMapper.updateAvatarByUid(uid, avatar, username, new Date());
if(rows !=1){
throw new UpdateException("更新数据异常");
}
}
测试业务层方法执行。
@Test
public void changeAvatar(){
userService.changeAvatar(9,"/adada","DB");
}
3上传头像-控制层
3.1 规划异常
文件异常的父类:
FileUploadException 泛指文件上传的异常(父类)继承 RunTimeException
父类是:FileUploadException
FileEmptyException 文件为空的异常
FileSizeException 文件大小超出限制
FileTypeException 文件类型异常
FileUploadIoException 文件读写的异常
FileStateException 文件状态异常
五个构造方法显示的声明出来,去继承相关的父类。
3.2 处理异常
在基类BaseController类中进行编写和统一处理 。
else if (e instanceof FileEmptyException) {
result.setState(6000);
result.setMessage("文件为空的异常");
}else if (e instanceof FileSizeException) {
result.setState(6001);
result.setMessage("文件大小的异常");
}else if (e instanceof FileTypeException){
result.setState(6002);
result.setMessage("文件类型的异常");
}else if (e instanceof FileStateException) {
result.setState(6003);
result.setMessage("文件状态的异常");
}else if (e instanceof FileUploadIoException) {
result.setState(6004);
result.setMessage("文件读写的异常");
}
在异常统一处理方法的参数列表上增加新的异常处理作为它的参数
@ExceptionHandler({ServiceException.class,FileUploadException.class})
3.3 设计请求
/users/change_avatar
POST(get提交数据为2Kb)
HttpSession session, MutipartFile file
JsonResult<String>
3.4 实现请求
/** 设置上传文件的最大值***/
public static final int AVATAR_MAX_SIZE= 10*1024*1024;
/** 限制上传文件的类型**/
public static final List<String> AVATAR_TYPE=new ArrayList<>();
static {
AVATAR_TYPE.add("images/jpeg");
AVATAR_TYPE.add("images/png");
AVATAR_TYPE.add("images/bmp");
AVATAR_TYPE.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)
{
//判断文件是否为null
if(file.isEmpty()){
throw new FileEmptyException("文件为空");
}
if(file.getSize()>AVATAR_MAX_SIZE){
throw new FileSizeException("文件超出限制");
}
//判断文件类型是否是我们规定的后缀类型
String contentType = file.getContentType();
//如果集合包含某个元素则返回值为true
if(!AVATAR_TYPE.contains(contentType)){
throw new FileTypeException("文件类型不支持");
}
//上传的文件../upload/文件.png
String parent = session.getServletContext().getRealPath("upload");
//File对象指向这个路径,file是否存在
File dir = new File(parent);
if(!dir.exists()){ //检测目录是否存在
dir.mkdirs();//创建当前目录
}
// 获取到这个文件名称,UUID工具来生成一个新的字符串作为文件名
String originalFilename = file.getOriginalFilename();
System.out.println("originalFilename"+originalFilename);
int index = originalFilename.lastIndexOf(".");
String suffix = originalFilename.substring(index);
String filename = UUID.randomUUID().toString().toUpperCase()+suffix;
File dest =new File(dir,filename); //是一个空文件
// 参数file中数据写入到这个空文件中
try {
file.transferTo(dest); //将file文件中数据写入到dest文件中,后缀需要一致
} catch (FileStateException e){
throw new FileStateException("文件状态异常");
}
catch (IOException e) {
throw new FileUploadIoException("文件读写异常");
}
Integer uid = getUidFromSession(session);
String username=getUsernameFromSession(session);
//返回头像的路径 /upload/test.png
String avatar="/upload/"+filename;
userService.changeAvatar(uid,avatar,username);
//返回用户头像的路径给前端页面,将来用于头像展示使用
return new JsonResult<>(OK,avatar);
}
4 上传头像-前端页面
在upload页面中编写上传头像的代码。
说明:如果直接使用表单进行文件的上传,需要给表单显示的添加一个属性enctype=”multipart/form-data“声明出来,不会将目标文件的数据结构做修改再上传,不同字符串。
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注解进行修改。MutipartConfigElement类型。
@Bean
public MultipartConfigElement getMultipartConfigElement(){
//创建一个配置的工厂类对象
MultipartConfigFactory factory=new MultipartConfigFactory();
// 设置需要创建的对象的相关信息。
factory.setMaxFileSize(DataSize.of(10, DataUnit.MEGABYTES));
factory.setMaxRequestSize(DataSize.of(15,DataUnit.MEGABYTES));
//通过工厂类来创建MultipartConfigElement对象
return factory.createMultipartConfig();
}
5.2 显示头像
在页面中通过ajax请求来提交文件,提交完成后返回了json串,解析出data中的数据,设置到img头像标签的src属性上就可以了。
·serialize():可以将表单数据自动拼接成key=value的结构进行提交给服务器,一般提交是普通的控件类型中的数据(text\password\radio\checkbox)等等
·FormData类:将表单中数据保持原有的结构进行数据的提交。
new FormData("#form")[0]; //文件类型的数据可以使用FormData对象进行存储
·ajax默认处理数据时按照字符串的形式进行处理,以及默认会采用字符串的形式进行提交数据,关闭这两个默认的功能。
processData:false,//处理数据的形式,关闭处理数据 contentType:false,//表示提交数据的形式,关闭默认提交数据的形式
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方法:
$.cookoe(key,value,time); // 单位,天
2.在upload.html页面先引入cookie.js文件
<script src="../bootstrap3/js/jquery.cookie.js" type="text/javascript" charset="utf-8"></script>
3.在upload.html页面通过ready()自动读取cookie中的数据。
$(document).ready(function () {
let avatar=$.cookie("avatar")
console.log(avatar);
//将cookie中的值获取出来设置到头像的src属性上
$("#img-avatar").attr(avatar)
})
5.4 显示最新头像
在更改完头像后,将最新的头像地址,再次保存到cookie中,同名保存会覆盖有cookie中值。