流程展示
数据库表
图1 跳转登录Controller (因为用到了thymeleaf,需要从接口进入,为了初始化页面数据)
```java
/**
* 跳转到登录页面
*/
@Controller
public class IndexController {
@GetMapping("index")
public String toLogin() {
return "login";
}
}
登录Controller
@Controller
@RequestMapping("/user")
public class UserController {
@Autowired
private UserService userService;
/**
* 用户登录方法
*
* @return
*/
@PostMapping("login")
public String login(User user, HttpSession session) {
User userDb = userService.login(user);
if (userDb != null) {
session.setAttribute("user", userDb);
return "redirect:/file/showAll";
} else {
return "login";
}
}
}
文件上传Controller
@Controller
@RequestMapping("/file")
public class FileController {
@Autowired
private FileService fileService;
/**
* 根据用户id,查询对应用户所有的上传的文件
*
* @param session
* @param model
* @return
*/
@GetMapping("/showAll")
public String findAll(HttpSession session, Model model) {
//用户登录之后就会用用户信息储存在Session中,可以从session中取出用户的id进行文件查询
User user = (User) session.getAttribute("user");
List<File> files = fileService.findFiles(user.getId());
if (files != null) {
//将查到的文件放在model中
model.addAttribute("files", files);
}
return "showAll";
}
/**
* 上传文件 并保存文件信息到数据库
*
* @param file
*/
@PostMapping("/upload")
public String uploadFile(MultipartFile file, HttpSession session) throws IOException {
fileService.saveFile(file, session);
return "redirect:/file/showAll";
}
/**
* 下载对应用户下的指定文件
* 在线打开图片也是用这个借口,下载的时候 响应头中设置 类型为 inline则为在线打开 attachment为 附件下载
* @param id 文件id
*/
@GetMapping("/download")
public void downloadFile(HttpServletResponse response, String openStyle, Integer id) throws IOException {
fileService.findByFileId(id, openStyle, response);
/*//因为下载 会更新下载次数,操作之后,重新加载列表
return "redirect:/file/showAll";*/
}
/**
* 删除文件
* @param id 文件id
*/
@GetMapping("/delete")
public String deleteFile(Integer id) throws FileNotFoundException {
fileService.deleteFile(id);
//删除完之后重新查询列表
return "redirect:/file/showAll";
}
/**
* ajax请求传所有的文件信息,设置3秒执行一次,去更新下载次数
* @param session session 为了从session中获取到用户
* @return
*/
@GetMapping("/showAllJson")
@ResponseBody
public List<File> showAllJson(HttpSession session) {
//用户登录之后就会用用户信息储存在Session中,可以从session中取出用户的id进行文件查询
User user = (User) session.getAttribute("user");
//根据用户id查询 文件列表
List<File> files = fileService.findFiles(user.getId());
if (files != null) {
//返回查询的文件列表
return files;
}
return null;
}
}
用户登录Service接口
/**
* 用户登录信息查询
*/
public interface UserService {
User login(User user);
}
文件上传Service接口
/**
* 文件上传的一些接口
*/
public interface FileService {
List<File> findFiles(Integer userId);
void saveFile(MultipartFile file, HttpSession session) throws IOException;
void findByFileId(Integer id,String openStyle, HttpServletResponse response) throws IOException;
void deleteFile(Integer id) throws FileNotFoundException;
}
用户登录查询用户信息ServiceImpl实现类
@Service
@Transactional
public class UserServiceImpl implements UserService {
@Autowired
private UserMapper userMapper;
/**
* 用户登录查询用户信息
* @param user
* @return
*/
@Override
@Transactional(propagation = Propagation.SUPPORTS) //使查询也支持事务
public User login(User user) {
User userDb = userMapper.login(user);
return userDb;
}
}
文件上传ServiceImpl实现类
@Service
@Transactional
public class FileServiceImpl implements FileService {
@Autowired
private FileMapper fileMapper;
/**
* 根据用户的id查询当前用户下的所有上传的文件
*
* @param userId
*/
@Override
public List<File> findFiles(Integer userId) {
List<File> files = fileMapper.findFiles(userId);
return files;
}
/**
* 根据用户id保存用户上传的文件信息
*
* @param file
*/
@Override
public void saveFile(MultipartFile file, HttpSession session) throws IOException {
User user = (User) session.getAttribute("user");
//获取文件的名称 旧名称
String oldFileName = file.getOriginalFilename();
//文件的后缀名, 用到FilenameUtils工具类 ,获取的扩展名
String extensionName = "." + FilenameUtils.getExtension(oldFileName);
//文件的新名称 时间戳加上随机数 加上后缀名
String newFileName = new SimpleDateFormat("yyyyMMddHHmmss").format(new Date()) + UUID.randomUUID().toString().replace("-", "") + extensionName;
//文件大小
long fileSize = file.getSize();
//文件类型
String fileType = file.getContentType();
//文件储存的路径 在resorce目录下的static/files
String realPath = ResourceUtils.getURL("classpath:").getPath() + "/static/files";
//根据日期生成文件夹储存文件
String dateFormat = new SimpleDateFormat("yyyy-MM-dd").format(new Date());
String dateDirPath = realPath + "/" + dateFormat;
java.io.File dateDir = new java.io.File(dateDirPath);
//如果日文文件夹不存在就创建文件夹
if (!dateDir.exists()) {
dateDir.mkdirs();
}
//储存复制文件,储存
file.transferTo(new java.io.File(dateDir, newFileName));
//文件信息储存到数据库
File files = new File();
files.setOldFileName(oldFileName);
files.setNewFileName(newFileName);
files.setExt(extensionName);
files.setPath("/files/" + dateFormat);
files.setSize(String.valueOf(fileSize));
files.setType(fileType);
//是否为图片,文具文件类型获取,如果是图片的话,文件类型是以image开头的
String isImage = fileType.startsWith("image") ? "是" : "否";
files.setIsImage(isImage);
//上传日期
files.setUploadTime(new Date());
//用户id
files.setUserId(user.getId());
fileMapper.saveFile(files);
}
/**
* 文件下载
*
* @param id 文件id
*/
@Override
public void findByFileId(Integer id, String openStyle, HttpServletResponse response) throws IOException {
//判断是否携带openStyle,如果没有下载就是附件下载,有携带的话就是携带的inline ,也可都携带参数,此处做判断即可
String download_or_Inline = openStyle == null ? "attachment" : openStyle;
File fileDb = fileMapper.findByFileId(id);
if (fileDb != null) {
//先获取项目所在绝对路径 //获取文件储存的文件夹 以及文件的名称
String realPath = ResourceUtils.getURL("classpath:").getPath() + "/static" + fileDb.getPath();
//通过新文件名得到文件
String newFileName = fileDb.getNewFileName();
//给用户的是之前的文件名
String oldFileName = fileDb.getOldFileName();
//输入流
FileInputStream is = new FileInputStream(new java.io.File(realPath, newFileName));
//做判断,如果是图片才能在线打开,不是图片只能下载
if (download_or_Inline.equals("inline") && !fileDb.getType().startsWith("image")) {
//附件只能进行下载
response.setHeader("content-disposition", "attachment;filename=" + URLEncoder.encode(oldFileName, "utf-8"));
} else {
//附件下载 inline是在线打开 attachment 是附件下载
response.setHeader("content-disposition", download_or_Inline+";filename=" + URLEncoder.encode(oldFileName, "utf-8"));
}
//文件输出,响应回去的,添加response
ServletOutputStream os = response.getOutputStream();
//文件复制 调用IOUtils工具类直接复制
IOUtils.copy(is, os);
//在线打开,不计下载次数
if (download_or_Inline.equals("attachment")) {
//文件复制之后,下载次数 + 1
fileDb.setDownCounts(fileDb.getDownCounts() + 1);
//将修改后的下载数量跟新到数据库
fileMapper.updateDownloadCounts(fileDb);
}
//关闭流 释放资源
IOUtils.closeQuietly(is);
IOUtils.closeQuietly(os);
}
}
/**
* 根据id删除文件
* @param id
*/
@Override
public void deleteFile(Integer id) throws FileNotFoundException {
//删除文件,要先查到文件信息,删除储存的文件,然后删除数据库文件信息
File fileDb = fileMapper.findByFileId(id);
//获取文件的位置 文件储存的位置: resource(本项目下) /sttic/siles/xxxxxx.jsp
String realPath = ResourceUtils.getURL("classpath:").getPath() + "static";
//获取文件
java.io.File file = new java.io.File((realPath), fileDb.getNewFileName());
//判断如果文件存在就删除,不存在不要重复操作
if (file.exists()) {
file.delete();
}
//删除数据库中的文件信息
fileMapper.deleteFile(id);
}
}
用户登录信息查询mapper
/**
* 查询用户信息
*/
@Mapper
public interface UserMapper {
User login(User user);
}
文件上传mapper
public interface FileMapper {
//根据用户id查询文件列表
List<File> findFiles(Integer userId);
//根据用户id,保存上传的文件信息
void saveFile(File file);
//根据文件id查找文件
File findByFileId(Integer id);
//更新下载次数
void updateDownloadCounts(File fileDb);
//删除文件
void deleteFile(Integer id);
}
用户登录查询用户信息mapper.xml
<mapper namespace="com.test.mapper.UserMapper">
<select id="login" resultType="User">
select id,`name`,password
from `user` where `name` =#{name} and password = #{password}
</select>
文件上传mapper.xml
<mapper namespace="com.test.mapper.FileMapper">
<!--根据用户id查询 用户所有上传的文件-->
<select id="findFiles" resultType="File">
select id,oldFileName,newFileName,ext,path,size,type,isImage,downCounts,uploadTime,userId
from files where userId = #{userId}
</select>
<!--储存用户上传的文件-->
<insert id="saveFile" parameterType="File">
insert into files values(#{id}, #{oldFileName}, #{newFileName}, #{ext}, #{path}, #{size}, #{type}, #{isImage}, #{downCounts}, #{uploadTime}, #{userId})
</insert>
<!--根据文件id查询文件-->
<select id="findByFileId" resultType="File">
select id,oldFileName,newFileName,ext,path,size,type,isImage,downCounts,uploadTime,userId
from files where id = #{id}
</select>
<!--更新下载次数-->
<update id="updateDownloadCounts" parameterType="File" >
update files set downCounts = #{downCounts} where id = #{id}
</update>
<!--删除文件-->
<delete id="deleteFile" >
delete from files where id = #{id}
</delete>
用户 实体类
@Data
public class User {
private Integer id;
private String name;
private String password;
}
文件信息实体类
@Data
public class File {
/* `id` int(8) NOT NULL AUTO_INCREMENT COMMENT '用户id',
`oldFileName` varchar(200) DEFAULT NULL COMMENT '旧文件名称',
`newFileName` varchar(300) DEFAULT NULL COMMENT '新文件名称',
`ext` varchar(20) DEFAULT NULL COMMENT '扩展名',
`path` varchar(300) DEFAULT NULL COMMENT '路径',
`size` varchar(200) DEFAULT NULL COMMENT '文件大小',
`type` varchar(120) DEFAULT NULL COMMENT '文件类型',
`isImage` varchar(8) DEFAULT NULL COMMENT '是否为图片',
`downCounts` int(6) DEFAULT NULL COMMENT '下载次数',
`uploadTime` datetime DEFAULT NULL COMMENT '上传时间',
PRIMARY KEY (`id`)*/
private Integer id;
private String oldFileName;
private String newFileName;
private String ext;
private String path;
private String size;
private String type;
private String isImage;
private Integer downCounts = 0;
private Date uploadTime;
private Integer userId;
}
启动器
@SpringBootApplication
@MapperScan("com.test.mapper")
public class FilesUploadApplication {
public static void main(String[] args) {
SpringApplication.run(FilesUploadApplication.class, args);
}
}
pom.xml依赖
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.5.0</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.test</groupId>
<artifactId>files_upload</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>files_upload</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.2.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<!-- mysql 数据库连接 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
<!--lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.20</version>
</dependency>
<!--单元测试-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!--文件上传的工具类,可以获取文件后缀等数据-->
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.4</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
application.yml配置文件
server:
port: 8000
spring:
application:
name: files # jwt
datasource: #数据库相关配置
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:3306/test?characterEncoding=UTF-8
username: root
password: root
thymeleaf: #静态页相关配置
cache: false
suffix: .html
encoding: utf-8
prefix: classpath:/templates/
mybatis:
type-aliases-package: com.test.entity #包映射
mapper-locations: com.test.mapper/*.xml #xml与mapper的映射
configuration:
map-underscore-to-camel-case: true #驼峰
前端文件放置位置;注意要添加jquery到resource/static/js目录下,
前端页面用到了thymeleaf需要在页面上引入<html lang=“en” xmlns:th="http://www.thymeleaf.org>"
![在这里插入图片描述](https://img-blog.csdnimg.cn/20210601220941446.jpg?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80NjY0OTA1NA==,size_16,color_FFFFFF,t_70#pic_center
前端页面结构
用户登录页面
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>用户登录页面</title>
</head>
<body>
<h1>用户登录:</h1>
<form th:action="@{/user/login}" method="post">
username: <input type="text" name="name"><br>
password: <input type="text" name="password"><br>
<input type="submit" value="登录">
</form>
</body>
</html>
文件列表展示页面
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>展示文件的列表</title>
<!--js文件引入 也可用thymeleaf 引入 <script th:src="@{/js/jquery-3.3.1.js}"></script>-->
<script th:src="@{/js/jquery-3.3.1.min.js}"></script>
<script>
/*发送ajax请求,修改下载次数,运用场景具体结合,异步请求 定时3秒发一次ajax请求
* [[@{/file/findAllJson}]] 为thymeleaf 异步请求url的方式
* */
$(function () {
/*start 点击事件开启定时更新下载次数 定时任务赋值给time 可以添加关闭定时任务操作*/
var time;
$("#start").click(function () {
console.log("开启定时更新下载次数...")
time = setInterval(function () {
$.get("[[@{/file/showAllJson}]]",function (result) {
/*控制台打印回调数据*/
/*console.log(result);*/
/*给下载次数设置一个id,值为当前文件id ,为了,将异步获取到的所有数据,进行遍历,得到指定的id,然后进行下载次数的定时 修改*/
$.each(result,function (index, file) {
$("#" + file.id).text(file.downCounts);
})
});
}, 3000);
});
$("#stop").click(function () {
console.log("结束定时更新下载次数...")
clearInterval(time);
});
});
</script>
</head>
<body>
<h1>欢迎: <span th:if="${session.user!=null}" th:text="${session.user.name}"></span></h1>
<h3>文件列表 :</h3>
<button id="start" >开启定时更新</button>
<button id="stop" >结束定时更新</button>
<table border="1px">
<tr>
<th>ID</th>
<th>文件原始名称</th>
<th>文件新名称</th>
<th>文件后缀</th>
<th>储存路径</th>
<th>文件大小</th>
<th>类型</th>
<th>是否是图片</th>
<th>下载次数</th>
<th>上传时间</th>
<th>操作</th>
</tr>
<tr th:each="file,fileStat:${files}">
<td><span th:text="${file.id}"/></td>
<td><span th:text="${file.oldFileName}"/></td>
<td><span th:text="${file.newFileName}"/></td>
<td><span th:text="${file.ext}"/></td>
<td><span th:text="${file.path}"/></td>
<td><span th:text="${file.size}"/></td>
<td><span th:text="${file.type}"/></td>
<!--是图片就将图片显示出来,不是图片就显示否-->
<td>
<!--<span th:text="${file.isImage}"/>-->
<img style="width: 100px; height: 100px" th:if="${file.isImage} == '是'"
th:src="${#servletContext.contextPath} + ${file.path} +'/' + ${file.newFileName}" alt="">
<span th:if="${file.isImage} != '是'" th:text="${file.isImage}"></span>
</td>
<td><span th:id="${file.id}" th:text="${file.downCounts}"/></td>
<td><span th:text="${#dates.format(file.uploadTime,'yyyy-MM-dd')}"/></td>
<td>
<a th:href="@{/file/download(id=${file.id})}">下载</a>
<!--传递参数后台进行判断,如果携带openStyle,就赋值给打开方式,inline是在线打开-->
<a th:href="@{/file/download(id=${file.id},openStyle='inline')}">在线打开</a>
<a th:href="@{/file/delete(id=${file.id})}">删除</a>
</td>
</tr>
</table>
<h3>上传文件:</h3>
<form th:action="@{/file/upload}" method="post" enctype="multipart/form-data">
<input type="file" name="file"> <input type="submit" value="上传文件">
</form>
</body>
</html>