23. 用户-上传头像-控制器层
在控制器层中,关于上传头像的代码:
/**
* 允许上传的文件大小的上限值,以字节为单位
*/
private static final long AVATAR_MAX_SIZE = 101 * 1024;
/**
* 允许上传的文件类型的集合
*/
private static final List<String> AVATAR_TYPES = new ArrayList<>();
static {
AVATAR_TYPES.add("image/jpeg");
AVATAR_TYPES.add("image/png");
AVATAR_TYPES.add("image/gif");
AVATAR_TYPES.add("image/bmp");
}
@PostMapping("avatar/change")
public JsonResult<String> changeAvatar(
MultipartFile file, HttpSession session) {
// 日志
System.err.println("UserController.changeAvatar()");
// 判断文件是否为空
boolean isEmpty = file.isEmpty();
System.err.println("\tisEmpty=" + isEmpty);
if (isEmpty) {
// 上传的文件为空,则抛出异常
throw new FileEmptyException(
"上传失败!请选择您要上传的文件!");
}
// 获取文件大小
long size = file.getSize();
System.err.println("\tsize=" + size);
if (size > AVATAR_MAX_SIZE) {
throw new FileSizeException(
"上传失败!不允许上传超过" +
(AVATAR_MAX_SIZE / 1024) + "KB的文件!");
}
// 获取文件的MIME类型
String contentType = file.getContentType();
System.err.println("\tcontentType=" + contentType);
// 判断上传的文件类型是否符合:image/jpeg,image/png,image/gif,image/bmp
if (!AVATAR_TYPES.contains(contentType)) {
throw new FileTypeException(
"上传失败!仅允许上传以下类型的文件:" + AVATAR_TYPES);
}
// 获取原始文件名(客户端设备中的文件名)
String originalFilename
= file.getOriginalFilename();
System.err.println("\toriginalFilename=" + originalFilename);
// 将文件上传到哪个文件夹
String parent = session
.getServletContext().getRealPath("upload");
System.err.println("\tupload path=" + parent);
File dir = new File(parent);
if (!dir.exists()) {
dir.mkdirs();
}
// 保存上传的文件时使用的文件名
String filename = ""
+ System.currentTimeMillis()
+ System.nanoTime();
String suffix = "";
int beginIndex = originalFilename
.lastIndexOf(".");
if (beginIndex >= 1) {
suffix = originalFilename
.substring(beginIndex);
}
String child = filename + suffix;
// 将客户端上传的文件保存到服务器端
File dest = new File(parent, child);
try {
file.transferTo(dest);
} catch (IllegalStateException e) {
throw new FileUploadStateException(
"上传失败!您的文件的状态异常!");
} catch (IOException e) {
throw new FileUploadIOException(
"上传失败!读写文件时出现错误,请重新上传!");
}
// 将保存的文件的路径记录到数据库中
String avatar = "/upload/" + child;
System.err.println("\tavatar path=" + avatar);
Integer uid = getUidFromSession(session);
String username = getUsernameFromSession(session);
userService.changeAvatar(uid, avatar, username);
// 返回
return new JsonResult<>(OK, avatar);
}
24. 用户-上传头像-前端页面
如果需要使用$.ajax()
函数实现上传,在配置该函数的参数时,参数JSON对象的使用相对一般的数据提交有所区别,在于:
-
data
属性的值不再是$("表单").serialize()
,而必须是new FormData(表单)
; -
添加配置
contentType
属性,取值为false
; -
添加配置
processData
属性,取值为false
。
为了使得打开页面时,就显示新上传的头像,应该将上传成功后得到的头像路径存储在本地!则需要使用Cookie技术!
在登录页面login.html的顶部引用了基于jQuery操作Cookie的文件:
<script src="../bootstrap3/js/jquery.cookie.js" type="text/javascript" charset="utf-8"></script>
而在上传头像页面upload.html中默认并没有引用该文件,所以,需要先在upload.html中补充引用以上文件!
在上传头像成功后,需要将头像路径保存到Cookie中:
// 将新图片的路径保存到Cookie中
$.cookie("avatar", json.data, {"expires":7});
并且,在该页面打开时,就应该读取保存的头像并显示到控件中:
$(document).ready(function() {
// 尝试从Cookie中读取用户的头像
let avatar = $.cookie("avatar");
console.log("读取Cookie中的头像路径:" + avatar);
// 将页面中的图片控件显示头像
if (avatar != null) {
$("#img-avatar").attr("src", avatar);
}
});
最后,还应该在登录页面login.html中,当登录成功时,将用户的头像保存到Cookie中:
if (json.data.avatar == undefined) {
$.cookie("avatar", null, {"expires":-1});
} else {
$.cookie("avatar", json.data.avatar, {"expires":7});
}
25. 关于上传文件的补充
关于一次性上传多个文件
首先,应该考虑上传的文件的数量和文件的定位是否能直接确定,例如“上传身份证的正反两张照片”就属性是可确定的,而“发朋友圈”要上传的图片的数量就是无法确定的!
如果是可以确定的,则在页面中添加多个<input type="file">
控件,然后,在控制器,声明多个MultipartFile
类的参数即可!
如果是无法确定的,则在页面的控件<input type="file">
中添加multiple="multiple"
属性,则该控件是可以选择多个文件的,当用户操作时,按住键盘的Ctrl键就可以选择多个文件!由于只使用了1个控件,所以,多个文件提交到服务器后,会得到一个MultipartFile[]
格式的数据,然后,检查数据并遍历数组即可处理!
关于在SpringMVC框架中处理文件上传
在使用SpringBoot框架时,已经添加了常用的依赖,并完成了常规的配置,所以,可以直接处理上传!在使用普通的SpringMVC框架时,首先,需要自行添加依赖:
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.4</version>
</dependency>
然后,在Spring的配置文件中添加配置:
<bean id="multipartResolver" class="xx.xx.xx.CommonsMultipartResolver">
<!-- 按需配置上传文件的大小限制 -->
<!-- 按需配置字符编码 -->
</bean>
注意:以上配置中,<bean>
节点的id
必须是multipartResolver
!
最后,在处理请求时,参数列表中的MultipartFile file
参数需要添加@RequestParam("file")
注解!
26. 收货地址-创建数据表
CREATE TABLE t_address (
aid INT AUTO_INCREMENT COMMENT '收货地址id',
uid INT NOT NULL COMMENT '用户id',
receiver VARCHAR(20) COMMENT '收货人',
province_name VARCHAR(15) COMMENT '省名称',
province_code CHAR(6) COMMENT '省代号',
city_name VARCHAR(15) COMMENT '市名称',
city_code CHAR(6) COMMENT '市代号',
area_name VARCHAR(15) COMMENT '区名称',
area_code CHAR(6) COMMENT '区代号',
zip CHAR(6) COMMENT '邮政编码',
address VARCHAR(50) COMMENT '详细地址',
phone VARCHAR(20) COMMENT '手机',
tel VARCHAR(20) COMMENT '固话',
tag VARCHAR(10) COMMENT '地址类型',
is_default INT(1) COMMENT '是否默认:0-非默认,1-默认',
created_user VARCHAR(20) COMMENT '创建人',
created_time DATETIME COMMENT '创建时间',
modified_user VARCHAR(20) COMMENT '最后修改人',
modified_time DATETIME COMMENT '最后修改时间',
PRIMARY KEY (aid)
) DEFAULT CHARSET=utf8mb4;
27. 收货地址-创建实体类
在cn.tedu.store.entity
包中创建Address
类,继承自BaseEntity
类:
/**
* 收货地址数据的实体类
*/
public class Address extends BaseEntity {
private static final long serialVersionUID = 6788614546638401469L;
private Integer aid;
private Integer uid;
private String receiver;
private String provinceName;
private String provinceCode;
private String cityName;
private String cityCode;
private String areaName;
private String areaCode;
private String zip;
private String address;
private String phone;
private String tel;
private String tag;
private Integer isDefault;
// SET/GET/hashCode/equals/toString
}