SpringBoot+MyBatis+MYSQL项目实战五(上传头像)
项目源码地址:
电脑商城实战
一:上传头像——持久层
1.1SQL语句的规划
update t_user set avatar=?,modified_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") String modifiedTime);
1.3接口映射
<update id="updateAvatarByUid">
UPDATE t_user
SET
avatar = #{avatar},
modified_user = #{modifiedUser},
modified_time = #{modifiedTime}
WHERE
uid = #{uid}
</update>
1.4测试
@Test
public void updateAvatarByUid(){
Integer rows = userMapper.updateAvatarByUid(10, "123", "管理者", new Date());
System.out.println(rows);
}
二:上传头像——业务层
2.1规划异常
在修改头像值前先检查用户数据状态,可能抛UserNotFoundException异常;由于最终执行的是修改操作还可能抛UpdateException异常。
2.2设计接口和抽象方法
/**
* 修改用户的头像
* @param uid 用户的id
* @param avatar 用户的图片
* @param modifiedUser 修改者
*/
void updateAvatarByUid(Integer uid,String avatar,String modifiedUser);
@Override
public void updateAvatarByUid(Integer uid, String avatar, String modifiedUser) {
User result = userMapper.findByUid(uid);
if (result ==null || result.getIsDelete().equals(1)){
throw new UserNotFoundException("用户数据不存在");
}
Integer rows = userMapper.updateAvatarByUid(uid, avatar, modifiedUser, new Date());
if (rows != 1){
throw new UpdateException("更新用户头像产生未知的异常");
}
}
2.3编写测试类
@Test
public void updateAvatarByUid(){
UserService.updateAvatarByUid(10,"/static/images/","管理者");
}
三:上传头像——控制层
3.1规划异常
FileUploadException 泛指文件上传的异常(父类) 继承RuntimeException
父类是FileUploadException
FileEmptyException 文件为空的异常
FileSizeException 文件大小超出限制
FileTypeException 文件类型异常
FileUploadException 文件读写的异常
五个构造方法声明出来,再去继承。
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 FileUploadException){
result.setState(6004);
}
在异常统一处理方法的参数列表上增加新的异常处理作为他的参数
@ExceptionHandler({ServiceException.class,FileUploadException.class})
3.3设计请求
设计用户提交的请求,并设计响应的方式:
请求路径:/users/change_avatar
请求参数:MultipartFile file, HttpSession session
请求类型:POST
响应结果:JsonResult<String>
3.4实现请求
/**
* MultipartFile 接口是SPingMvc接口,这个接口为我们包装了获取文件类型的数据(任何类型的file都可以接收)
* springboot它整合了springmvc,只需要在处理请求的方法参数列表上声明一个参数类型MultipartFile的
* 参数,然后springboot会自动传递给服务的文件数据复制给这个参数
*
* @RequestParam 表示请求中的参数,将请求中的参数注入请求处理方法的某个参数上,如果名称不一致则可以使用
* @RequestParam如果名称不一致则可以使用@RequestParam
* @param session
* @param file
* @return
*/
@RequestMapping(value = "/change_avatar",method = RequestMethod.POST)
public JsonResult<String> upload(HttpSession session,
@RequestParam("file") MultipartFile file){
return new JsonResult<>(OK);
}
@RequestMapping(value = "/change_avatar",method = RequestMethod.POST)
public JsonResult<String> upload(HttpSession session,
@RequestParam("file") MultipartFile file) throws IOException {
// 判断文件是否为空
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/
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);
// 返回头像的路径
String avatar = "/upload/" + filename;
iUserService.updateAvatarByUid(uid,avatar,username);
// 返回用户头像路径给其阿奴但,将来用户头像头像展示使用
return new JsonResult<>(OK,avatar);
}
四:上传头像——前端页面
注意;action="/users/change_avatar" method="post" enctype="multipart/form-data"
<form class="form-horizontal" action="/users/change_avatar"
method="post" enctype="multipart/form-data" role="form">
<div class="form-group">
<label class="col-md-2 control-label">选择头像:</label>
<div class="col-md-5">
<img id="img-avatar" src="../../static/images/index/user.jpg" class="img-responsive" />
</div>
<div class="clearfix"></div>
<div class="col-md-offset-2 col-md-4">
<input type="file" name="file">
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
<input type="submit" class="btn btn-primary" value="上传" />
</div>
</div>
</form>
五解决Bug
5.1更改默认的大小限制
SpringMVC默认为1MB文件可以进行上传,手动的去修改SpringMvC默认上传文件的大小
方式一:直接在配置文件中配置
spring
servlet:
multipart:
max-file-size: 15MB
max-request-size: 20MB
方式二:采用java代码的形式设置文件的上传大小的限制,主类中进行配置,可以定义一个方法,必须使用@bean修饰,在类的前面添加@Configration注解进行修改
@Bean
public MultipartConfigElement getMultipartConfigElement(){
//创建一个配置的工厂类对象
MultipartConfigFactory factory = new MultipartConfigFactory();
factory.setMaxFileSize(DataSize.of(10, DataUnit.MEGABYTES));
factory.setMaxRequestSize(DataSize.of(15,DataUnit.MEGABYTES));
return factory.createMultipartConfig();
}
- serialize():可以将表单数据自动拼接成key=value的结构进行提交给服务器,已办提交是普通的空间类型的数据(text/password\radio\checkboc)
- FormData类:将表单数据保持原有的结构进行数据的提交。
new FormData($("#form")[0]);
//为文件类型的数据可以是同FormData对象进行存储 - ajax默认处理数据是按照字符串的形式进行处理,以及采用默认字符串的形式进行提交。关闭这两个默认的功能。
processData: false, //关闭处理数据 contentType: false, //提交数据的形式
5.2登录后显示头像
可以更新头像成功后,将服务器的头像返回路径保存在客户端cookie对象,然后每次检测到用户打开头像页面,在这个页面通过ready()方法来自动检测去读取cookie中头像并设到src属性上。
1.设置cookie中的值
导入cookie的js文件
$.cookie(key,value,time); //单位为天
在登录的时候// 将头像保存在cookie中
$.cookie("avatar",json.data.avatar, {expires: 7});
在上传图片的页面使用$(document).ready(function () {}
$(document).ready(function () {
let avatar = $.cookie("avatar");
$("#img-avatar").attr("src",avatar);
})
最后在用户上传图片之后更新cookie的值
$("#btn-change-avatar").click(function () {
$.ajax({
url: "/users/change_avatar",
type: "POST",
data : new FormData($("#form-change-avatar")[0]),
processData: false, //关闭处理数据
contentType: false, //提交数据的形式
dataType: "JSON",
success: function (json) {
if(json.state == 200){
alert("头像修改成功")
// attr(s属性,属性值):给某个属性设置某个值
$("#img-avatar").attr("src",json.data);
$.cookie("avatar",json.data,
{expires: 7});
location.href = "/upload";
}
else{
alert("头像修改失败")
}
},
error: function (xhr) {
alert("修改头像时产生位置的异常"+xhr.message);
}
})
})