文件上传,也称为upload,是指将本地图片、视频、音频等文件上传到服务器上,可以供其他用户浏览或下载的过程。文件上传在项目中应用非常广泛,我们经常发微博、发微信朋友圈都用到了文件上传功能。
服务端要接收客户端页面上传的文件,通常都会使用Apache的两个组件:
commons-fileupload
commons-io
Spring框架在spring-web包中对文件上传进行了封装,大大简化了服务端代码,我们只需要在Controller的方法中声明一个MultipartFile类型的参数即可接收上传的文件,例如:
Sql:
CREATE TABLE `sys_file` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT 'id',
`name` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '文件名称',
`type` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '文件类型',
`size` bigint(20) DEFAULT NULL COMMENT '文件大小(kb)',
`url` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '下载链接',
`md5` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '文件md5',
`is_delete` tinyint(1) DEFAULT '0' COMMENT '是否删除',
`enable` tinyint(1) DEFAULT '1' COMMENT '是否禁用链接',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=23 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
其中is_delete和enable的默认值分别是0和1
增加一个控制层类FileController.java:
在配置文件application.yml添加文件上传到的位置:
files:
upload:
path: F:/后台管理系统/files/
文件磁盘路径:
/**
* @author hj
*/
@Slf4j
@RestController
@RequestMapping("/file")
public class FileController {
//文件磁盘路径
@Value("${files.upload.path}")
private String fileUploadPath;
@PostMapping("/upload")
public Result upload(@RequestParam MultipartFile file) throws IOException {
//获取文件原始名称
String originalFilename = file.getOriginalFilename();
//获取文件的类型
String type = FileUtil.extName(originalFilename);
log.info("文件类型是:" + type);
//获取文件大小
long size = file.getSize();
//获取文件
File uploadParentFile = new File(fileUploadPath);
//判断文件目录是否存在
if(!uploadParentFile.exists()) {
//如果不存在就创建文件夹
uploadParentFile.mkdirs();
}
//定义一个文件唯一标识码(UUID)
String uuid = UUID.randomUUID().toString();
File uploadFile = new File(fileUploadPath + uuid + StrUtil.DOT + type);
//将临时文件转存到指定磁盘位置
file.transferTo(uploadFile);
return Result.success("");
}
}
1. 将临时文件转存到指定位置
//将临时文件转存到指定位置
file.transferTo(new File(basePath + fileName));
2. 注意:文件后缀(类型)要加上
//获取文件的类型
String type = FileUtil.extName(originalFilename);
log.info("文件类型是:" + type);
StrUtil.DOT表示"."
FileUtil
类是一个工具类,需要配置hutool包的依赖
<dependency>
<groupId>cn.hutoolgroupId>
<artifactId>hutool-allartifactId>
<version>5.7.20version>
dependency>
我们现在apifox里面进行测试:
@Getter
@Setter
@TableName("sys_file")
@ApiModel(value = "File对象", description = "")
public class File implements Serializable {
private static final long serialVersionUID = 1L;
@ApiModelProperty("id")
@TableId(value = "id", type = IdType.AUTO)
private Integer id;
@ApiModelProperty("文件名称")
private String name;
@ApiModelProperty("文件类型")
private String type;
@ApiModelProperty("文件大小")
private Long size;
@ApiModelProperty("下载链接")
private String url;
@ApiModelProperty("是否删除")
private Boolean isDelete;
@ApiModelProperty("是否禁用链接")
private Boolean enable;
}
/**
* 文件上传接口
* @param file
* @return
* @throws IOException
*/
@PostMapping("/upload")
public String upload(@RequestParam MultipartFile file) throws IOException {
//获取文件原始名称
String originalFilename = file.getOriginalFilename();
//获取文件的类型
String type = FileUtil.extName(originalFilename);
log.info("文件类型是:" + type);
//获取文件大小
long size = file.getSize();
//文件存储的磁盘
File uploadParentFile = new File(fileUploadPath);
//判断文件目录是否存在
if(!uploadParentFile.exists()) {
//如果不存在就创建文件夹
uploadParentFile.mkdirs();
}
//定义一个文件唯一标识码(UUID)
String uuid = UUID.randomUUID().toString();
String fileUUID = uuid + StrUtil.DOT + type;
File uploadFile = new File(fileUploadPath + fileUUID);
//将临时文件转存到指定磁盘位置
file.transferTo(uploadFile);
//设置下载的文件路径
String url = "http://localhost:9090/file/" + fileUUID;
//存储至数据库
Files saveFile = new Files();
saveFile.setName(originalFilename);
saveFile.setType(type);
saveFile.setSize(size/1024);//转成kb
saveFile.setUrl(url);
fileMapper.insert(saveFile);
return url;
}
由于我们上传上去的图片有重复的,所以我们需要去重,只让他们共享一个图像
去重的思路为:
将文件的二进制流转换为MD5编码,每当我们上传一个文件就将其二进制流MD5与数据库当中已保存的文件的二进制流MD5进行比较,相同就舍弃,不相同就将文件的信息保存至数据库,文件内容上传至文件夹。
/**
* 文件上传接口
* @param file
* @return
* @throws IOException
*/
@PostMapping("/upload")
public String upload(@RequestParam MultipartFile file) throws IOException {
//获取文件原始名称
String originalFilename = file.getOriginalFilename();
//获取文件的类型
String type = FileUtil.extName(originalFilename);
log.info("文件类型是:" + type);
//获取文件大小
long size = file.getSize();
//文件存储的磁盘
File uploadParentFile = new File(fileUploadPath);
//判断文件目录是否存在
if(!uploadParentFile.exists()) {
//如果不存在就创建文件夹
uploadParentFile.mkdirs();
}
//定义一个文件唯一标识码(UUID)
String uuid = UUID.randomUUID().toString();
String fileUUID = uuid + StrUtil.DOT + type;
File uploadFile = new File(fileUploadPath + fileUUID);
String url;
// 获取文件的md5
String md5 = SecureUtil.md5(file.getInputStream());
// 从数据库查询是否存在相同的记录
Files dbFiles = fileService.getFileByMd5(md5);
if (dbFiles != null) { // 文件已存在
url = dbFiles.getUrl();
} else {
// 上传文件到磁盘
file.transferTo(uploadFile);
// 数据库若不存在重复文件,则不删除刚才上传的文件
url = "http://localhost:9090/file/" + fileUUID;
}
//存储至数据库
Files saveFile = new Files();
saveFile.setName(originalFilename);
saveFile.setType(type);
saveFile.setSize(size/1024);//转成kb
saveFile.setUrl(url);
saveFile.setMd5(md5);
fileService.save(saveFile);
return url;
}
FileServiceImpl.java
@Service
public class FileServiceImpl extends ServiceImpl<FileMapper, Files> implements IFileService {
/**
* 根据MD5查询文件
* @param md5
* @return
*/
@Override
public Files getFileByMd5(String md5) {
LambdaQueryWrapper<Files> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(Files::getMd5, md5);
List<Files> list = this.list(queryWrapper);
return list.size() == 0 ? null : list.get(0);
}
}