来学习一下文件上传和下载
一、页面开发
整体思路
登录页
主页
二、库表设计
SET FOREIGN_KEY_CHECKS=0;
-- ----------------------------
-- Table structure for t_files
-- ----------------------------
DROP TABLE IF EXISTS `t_files`;
CREATE TABLE `t_files` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`oldFileName` varchar(255) DEFAULT NULL,
`newFileName` varchar(255) DEFAULT NULL,
`ext` varchar(255) DEFAULT NULL,
`path` varchar(255) DEFAULT NULL,
`size` varchar(255) DEFAULT NULL,
`type` varchar(255) DEFAULT NULL,
`isImg` int(11) NOT NULL,
`downCounts` varchar(255) DEFAULT NULL,
`uploadTime` datetime DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- ----------------------------
-- Records of t_files
-- ----------------------------
-- ----------------------------
-- Table structure for t_user
-- ----------------------------
DROP TABLE IF EXISTS `t_user`;
CREATE TABLE `t_user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(255) DEFAULT NULL,
`password` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- ----------------------------
-- Records of t_user
-- ----------------------------
三、环境搭建
四、用户登录开发
这次新建了一个工程,因为
Lombok
用得很习惯,但以前的话,一般只用了@Data
,@AllArgsConstructor
,@EqualsAndHashCode
等常规注解;那这个Accessors(chain = true)
注解是干嘛的呢?https://www.cnblogs.com/grey-wolf/p/11812528.html
五、用户文件列表
<tr th:each="f:${file}">
<td th:text="${f.id}"></td>
<td th:text="${f.oldFileName}"></td>
<td th:text="${f.newFileName}"></td>
<td th:text="${f.ext}"></td>
<td th:text="${f.path}"></td>
<td th:text="${f.size}"></td>
<td th:text="${f.type}"></td>
<td th:text="${f.isImg}"></td>
<td th:text="${f.downCounts}"></td>
<td th:text="${#dates.format(f.uploadTime,'yyyy-MM-dd')}"></td>//日期格式化
<td><a href="">下载</a> <a href="">在线打开</a> <a href="">删除</a></td>
</tr>
六、用户文件上传
1、文件上传
记录旧文件名、新文件名、文件后缀、文件大小、文件路径、文件类型、下载次数、下载时间、是否图片
/**
* 上传文件并保存到数据库中
* 文件名lujiahong要和表单中的name属性一一对应
* 上传的文件存在static里面
* @param file
* @return
*/
@PostMapping("/upload")
public String upload(@RequestParam("lujiahong") MultipartFile file,HttpSession session) throws IOException {
//1、获取文件原始名称
String oldFileName = file.getOriginalFilename();
//2、按旧文件名获取文件后缀(创建新文件名名获取文件后缀 ,需要引入依赖包commons-fileupload)
String extension ="."+ FilenameUtils.getExtension(oldFileName);
//3、生成新的文件名称(当前时间+UUID+后缀)——UUID里面有符号"-",替换为""
String newFileName = new SimpleDateFormat("yyyyMMddHHmmss").format(new Date() )+ UUID.randomUUID().toString().replace("-","")+extension;
//4、文件大小
Long size = file.getSize();
//5、文件类型
String type = file.getContentType();
//6、根据日期生成目录
//通过工具类ResourceUtils去获取静态文件下指定文件的真实路径
String realPath = ResourceUtils.getURL("classpath:").getPath() + "/static/files";
//生成动态日期路径
String dataFormat = new SimpleDateFormat("yyyy-MM-dd").format(new Date());
String dateDirPath = realPath + "/" + dataFormat;
//得到日期目录
File dateDir = new File(dateDirPath);
if(!dateDir.exists()) {
dateDir.mkdirs();
}
//7、处理文件上传
file.transferTo(new File(dateDir,newFileName));
//8、文件相关数据存入数据库
Files files = new Files();
files.setUserId(((User)session.getAttribute("user")).getId());
files.setOldFileName(oldFileName);
files.setNewFileName(newFileName);
files.setExt(extension);
files.setType(type);
files.setSize(size);
files.setPath("/files/"+dataFormat);
files.setDownCounts("0");
files.setUploadTime(new Date());
files.setIsImg(0);
this.iFilesService.save(files);
return "redirect:/file/showAll";
}
2、配置文件上传大小、以及一次性可上传文件大小
spring:
servlet:
multipart:
max-file-size: 50MB #设置单个文件大小
max-request-size: 50MB #设置单次请求文件的总大小
@Configuration
public class MultipartConfig {
@Bean
public MultipartConfigElement multipartConfigElement(){
MultipartConfigFactory factory = new MultipartConfigFactory();
factory.setMaxFileSize(DataSize.parse("50MB"));//允许上传的文件最大值
factory.setMaxRequestSize(DataSize.parse("50MB"));// 设置总上传数据总大小
return factory.createMultipartConfig();
}
}
3、参考
- 参考一
- 参考二
- 参考三
七、用户上传文件是否是图片判断
1、后台响应前台
public class ResBack {
public static void response(String msg, HttpServletResponse response){
try {
response.setContentType("text/html; charset=UTF-8"); //转码
PrintWriter out = response.getWriter();
out.flush();
out.println("<script>");
out.println("alert('"+msg+"');");
out.println("history.back();");
out.println("</script>");
} catch (IOException e) {
e.printStackTrace();
}
}
}
2、后台在线播放
servletContext.contextPath用来获取文件项目中的地址,后面是保存在项目static里面的路径的拼接
<td th:switch="${f.isImg ne ''}">
<img th:case="${f.isImg eq '图片'}" th:src="${#servletContext.contextPath+f.path+'/'+f.newFileName}" style="width: 100px;height: 100px">
<video th:case="${f.isImg eq '视频'}" th:src="${#servletContext.contextPath+f.path+'/'+f.newFileName}" controls="controls" style="width: 100px;height: 100px"></video>
<audio th:case="${f.isImg eq '音频'}" th:src="${#servletContext.contextPath+f.path+'/'+f.newFileName}" controls="controls" style="width: 100px;height: 100px"></audio>
<p th:case="${f.isImg eq '未知类型'}">未知类型</p>
</td>
八、用户下载文件
@GetMapping("/download")
public void down(@RequestParam("id") int id,HttpServletResponse response) throws IOException {
//1、获取文件信息
Files files = this.iFilesService.getById(id);
//2、更新下载次数
files.setDownCounts(files.getDownCounts()+1);
this.iFilesService.updateById(files);
//2、根据文件存储路径path和文件名获取文件输出流
String downPath = ResourceUtils.getURL("classpath:").getPath() + "/static" + files.getPath();
//3、获取文件输入流
FileInputStream fileInputStream = new FileInputStream(new File(downPath,files.getNewFileName()));
//4、附件格式下载设置头部( URLEncoder.encode(files.getOldFileName(),"utf-8") 设置文件名编码风格,避免乱码)
response.setHeader("content-disposition","attachment;fileName="+ URLEncoder.encode(files.getOldFileName(),"utf-8"));
//4、获取响应输出流
ServletOutputStream outputStream = response.getOutputStream();
//5、文件拷贝
IOUtils.copy(fileInputStream,outputStream);
//6、关闭输入输出流
IOUtils.closeQuietly(fileInputStream);
IOUtils.closeQuietly(outputStream);
}
九、用户删除文件
1、前台请求url的写法——thymeleaf
@{/file/download(id=${f.id},openStyle=‘inline’)}相当于file/download?id=30&openStyle=inline
<a th:href="@{/file/download(id=${f.id})}" target="_blank">下载</a>
<a th:href="@{/file/download(id=${f.id},openStyle='inline')}" target="_blank">在线打开</a>
<a th:href="@{/file/delFile(id=${f.id})}">删除</a><
2、在线展示数据
下载还是在线打开(逻辑判断,设置不同请求头即可实现;attachment——下载;inline——在线打开)
response.setHeader(“content-disposition”,“attachment;fileName=文件名”);//下载头部设置
response.setHeader(“content-disposition”,“inline;fileName=文件名”);//预览头部设置
@GetMapping("/download")
public void down(String openStyle,@RequestParam("id") int id,HttpServletResponse response) throws IOException {
//获取打开方式——下载还是在线打开(共用一个接口,逻辑判断,设置不同请求头即可实现;attachment——下载;inline——在线打开)
openStyle = openStyle == null? "attachment":openStyle;
//1、获取文件信息
Files files = this.iFilesService.getById(id);
//2、更新下载次数
if("attachment".equals(openStyle)){
files.setDownCounts(files.getDownCounts()+1);
this.iFilesService.updateById(files);
}
//2、根据文件存储路径path和文件名获取文件输出流
String downPath = ResourceUtils.getURL("classpath:").getPath() + "/static" + files.getPath();
//3、获取文件输入流
FileInputStream fileInputStream = new FileInputStream(new File(downPath,files.getNewFileName()));
//4、附件格式下载设置头部( URLEncoder.encode(files.getOldFileName(),"utf-8") 设置文件名编码风格,避免乱码)
response.setHeader("content-disposition",openStyle+";fileName="+ URLEncoder.encode(files.getOldFileName(),"utf-8"));
//4、获取响应输出流
ServletOutputStream outputStream = response.getOutputStream();
//5、文件拷贝
IOUtils.copy(fileInputStream,outputStream);
//6、关闭输入输出流
IOUtils.closeQuietly(fileInputStream);
IOUtils.closeQuietly(outputStream);
}
3、删除文件
@GetMapping("/delFile")
public String del(int id) throws FileNotFoundException {
//1、删除本地文件数据
//获取删除路径
Files files = this.iFilesService.getById(id);
String delPath = ResourceUtils.getURL("classpath:").getPath() + "static/" + files.getPath();
File file = new File(delPath,files.getNewFileName());
if(file.exists()){
file.delete();
}
//2、删除数据库数据
this.iFilesService.delFile(id);
return "redirect:/file/showAll";
}
十、定时更新下载次数
前端定时调度接口刷新下载次数
<script type="application/javascript" th:src="@{/static/js/jquery-3.5.1.min.js}"></script>
<script>
$(()=>{
var time ;
$('#start').click(()=>{
time = setInterval(()=>{
$.get("[[@{/file/showAllJson}]]",(res)=>{
$.each(res,(index,file)=>{
$("#"+file.id).text(file.downCounts)
})
})
},3000);
})
$("#stop").click(()=>{
clearInterval(time)
})
})
</script>
<body>
<button id="start">开启定时更新</button>
<button id="stop">关闭定时更新</button>
</body>
十一、对上面功能添加
作者一:愿有清风不解语i
我将老师的项目添加了一些小功能:
1、使用Shiro安全框架来做登录认证和拦截;
2、使用SemanticUi来稍微美化了一下界面;
3、在登录界面添加了JS代码,对表单预验证;
4、修复了在没有选择上传文件的情况下,能够上传文件的一个小bug;
5、对密码使用了MD5加密
https://github.com/HaiTang-8/File-Upload/tree/master
作者二:灵器冰
在老师的基础上,
1.增加了多用户登录注册,
2.整合layui框架美化界面和响应事件刷新表格,
3.使用shiro安全框架对页面访问进行控制。
4.为数据表格增加了分页功能。
https://github.com/sdifv/fileStorage