SpringBoot上传下载文件及在线预览

SpringBoot上传下载文件及在线预览

今天大概就说说如何使用SpringBoot进行上传和下载以及在线预览文件
本篇主要介绍上传下载的功能,对于界面就简单一点,大致如下:

在这里插入图片描述

一、老规矩还是先看看小项目的目录结构:


在这里插入图片描述

二、添加对应的pom依赖

<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.1.1</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.47</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.12</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.1.21</version>
        </dependency>

        <dependency>
            <groupId>commons-fileupload</groupId>
            <artifactId>commons-fileupload</artifactId>
            <version>1.3.3</version>
        </dependency>

三、创建相应的配置

spring.application.name=files
server.port=8080
server.servlet.context-path=/files

spring.thymeleaf.cache=false
spring.thymeleaf.suffix=.html
spring.thymeleaf.encoding=UTF-8
spring.thymeleaf.prefix=classpath:/templates/
spring.resources.static-locations=classpath:/templates/,classpath:/static/,file:${upload.dir}


spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/files?characterEncoding=UTF-8
spring.datasource.username=root
spring.datasource.password=root

mybatis.mapper-locations=classpath:/com/baizhi/mapper/*.xml
mybatis.type-aliases-package=com.baizhi.entity

#控制台进行打印日志
logging.level.root=info
logging.level.com.baizhi.dao=debug

#上传和下载文件的路径
upload.dir=D:/idea_project/java/files/target/classes/static

四、先准备登陆界面的工作

1、创建实体类

@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
@Accessors(chain=true)
public class User {
    private Integer id;
    private String username;
    private String password;

}

2、创建对应的dao层

public interface UserDAO {

    User login(User user);
}

3、创建对应的mapper映射文件

<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.baizhi.dao.UserDAO">
    
    <!--login-->
    <select id="login" parameterType="User" resultType="com.baizhi.entity.User">
        select id,username,password
        from t_user
        where username=#{username}
        and password = #{password}
    </select>

</mapper>

4、创建业务层接口及实现类

public interface UserService {

    User login(User user);
}
@Service
@Transactional
public class UserServciceImpl  implements  UserService{

    @Autowired
    private UserDAO userDAO;

    @Override
    @Transactional(propagation = Propagation.SUPPORTS)
    public User login(User user) {
        return userDAO.login(user);
    }
}

5、创建控制器

@Controller
@RequestMapping("user")
@Slf4j
public class UserController {
    @Autowired
    private UserService userService;

    /**
     * 登录方法
     */
    @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 "redirect:/index";
        }
    }

}

6、创建登陆界面

<!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="username"/> <br>
    password: <input type="text" name="password"/> <br>
    <input type="submit" value="登录">
</form>

</body>
</html>

7、查看运行后对应的界面

4

五、进行主界面的操作

1、创建实体类

@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
@Accessors(chain=true)
public class UserFile {
    private Integer id;
    private String oldFileName;
    private String newFileName;
    private String ext;
    private String path;
    private String size;
    private String type;
    private String isImg;
    private Integer downcounts;
    private Date uploadTime;
    private Integer userId; //用户外键
}

2、创建对应的dao层

public interface UserFileDAO {

    //根据登录用户id获取用户的文件列表
    List<UserFile> findByUserId(Integer id);

    //保存用户的文件记录
    void save(UserFile userFile);

    //根据文件id获取文件信息
    UserFile findById(String id);

    //根据id更新下载次数
    void update(UserFile userFile);

    //根据id删除记录
    void delete(String id);
}

3、创建dao层对应的mapper映射文件

<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.baizhi.dao.UserFileDAO">
    
    <!--根据用户id查询当前用户的文件信息-->
    <select id="findByUserId" parameterType="Integer" resultType="com.baizhi.entity.UserFile">
        select  id,oldFileName,newFileName,ext,path,size,type,isImg,downcounts,uploadTime,userId
        from t_files
        where userId=#{id}
    </select>


    <!--保存文件信息-->
    <insert id="save" parameterType="com.baizhi.entity.UserFile" useGeneratedKeys="true" keyProperty="id">
      insert into t_files
        values (#{id},#{oldFileName},#{newFileName},
                #{ext}, #{path},#{size},#{type},
                #{isImg},#{downcounts}, #{uploadTime},#{userId})
    </insert>


    <!--根据id获取文件信息-->
    <select id="findById" parameterType="String" resultType="com.baizhi.entity.UserFile">
        select id,oldFileName,newFileName,ext,path,size,type,isImg,downcounts,uploadTime,userId
        from t_files
        where id = #{id}
    </select>

    <!--更新下载次数-->
    <update id="update" parameterType="com.baizhi.entity.UserFile" >
        update t_files set downcounts=#{downcounts} where id=#{id}
    </update>

    <!--根据id删除记录-->
    <delete id="delete" parameterType="String">
        delete from t_files where id=#{id}
    </delete>

</mapper>

4、创建对应的业务层接口及实现类

public interface UserFileService {

    List<UserFile> findByUserId(Integer id);

    void save(UserFile userFile);

    UserFile findById(String id);

    void update(UserFile userFile);

    void delete(String id);
}

@Service
@Transactional
public class UserFileServiceImpl implements  UserFileService {

    @Autowired
    private UserFileDAO userFileDAO;

    @Override
    public List<UserFile> findByUserId(Integer id) {
        return userFileDAO.findByUserId(id);
    }

    @Override
    public void delete(String id) {
        userFileDAO.delete(id);
    }

    @Override
    public void update(UserFile userFile) {
        userFileDAO.update(userFile);
    }

    @Override
    public UserFile findById(String id) {
        return userFileDAO.findById(id);
    }

    @Override
    public void save(UserFile userFile) {
        //userFile.setIsImg()?  //是否是图片 解决方案: 当类型中含有image时说明当前类型一定为图片类型
        String isImg = userFile.getType().startsWith("image")?"是":"否";
        userFile.setIsImg(isImg);
        userFile.setDowncounts(0);
        userFile.setUploadTime(new Date());
        userFileDAO.save(userFile);
    }
}

5、创建控制器(重点)

  1. 上传文件(且保存到i数据库中)
@PostMapping("upload")
    public String upload(MultipartFile aaa, HttpSession session) throws IOException {
        //获取上传文件用户id
        User user = (User) session.getAttribute("user");
        //获取文件原始名称
        String oldFileName = aaa.getOriginalFilename();
        //获取文件后缀
        String extension = "." + FilenameUtils.getExtension(aaa.getOriginalFilename());
        //生成新的文件名称
        String newFileName = new SimpleDateFormat("yyyyMMddHHmmss").format(new Date()) + UUID.randomUUID().toString().replace("-", "") + extension;
        //文件大小
        Long size = aaa.getSize();
        //文件类型
        String type = aaa.getContentType();
        //处理根据日期生成目录
        //String realPath = ResourceUtils.getURL("classpath:").getPath() + "/static/files";
        String dateFormat = new SimpleDateFormat("yyyy-MM-dd").format(new Date());
        String dateDirPath = uploadPath + "/files/" + dateFormat;
        File dateDir = new File(dateDirPath);
        if (!dateDir.exists()) dateDir.mkdirs();
        //处理文件上传
        aaa.transferTo(new File(dateDir, newFileName));
        //将文件信息放入数据库保存
        UserFile userFile = new UserFile();
        userFile.setOldFileName(oldFileName).setNewFileName(newFileName).setExt(extension).setSize(String.valueOf(size))
                .setType(type).setPath("/files/" + dateFormat).setUserId(user.getId());
        userFileService.save(userFile);
        return "redirect:/file/showAll";
    }
  1. 下载文件(在线预览)
@GetMapping("download")
    public void download(String openStyle, String id, HttpServletResponse response) throws IOException {
        //获取打开方式
        openStyle = openStyle == null ? "attachment" : openStyle;
        //获取文件信息
        UserFile userFile = userFileService.findById(id);
        //点击下载链接更新下载次数
        if ("attachment".equals(openStyle)) {
            userFile.setDowncounts(userFile.getDowncounts() + 1);
            userFileService.update(userFile);
        }
        //根据文件信息中文件名字 和 文件存储路径获取文件输入流
        String realpath = ResourceUtils.getURL("classpath:").getPath() + "/static" + userFile.getPath();
        //获取文件输入流
        FileInputStream is = new FileInputStream(new File(realpath, userFile.getNewFileName()));
        //附件下载
        response.setHeader("content-disposition", openStyle + ";fileName=" + URLEncoder.encode(userFile.getOldFileName(), "UTF-8"));
        //获取响应输出流
        ServletOutputStream os = response.getOutputStream();
        //文件拷贝
        IOUtils.copy(is, os);
        IOUtils.closeQuietly(is);
        IOUtils.closeQuietly(os);
    }
  1. 展示所有文件信息
@GetMapping("showAll")
    public String findAll(HttpSession session, Model model) {
        //在登录的session中获取用户的id
        User user = (User) session.getAttribute("user");

        //根据用户id查询有的文件信息
        List<UserFile> userFiles = userFileService.findByUserId(user.getId());
        //存入作用域中
        model.addAttribute("files", userFiles);
        return "showAll";
    }
  1. 删除文件(及数据库中的)
@GetMapping("delete")
    public String delete(String id) throws FileNotFoundException {

        //根据id查询信息
        UserFile userFile = userFileService.findById(id);

        //删除文件
        String realPath = ResourceUtils.getURL("classpath:").getPath() + "/static" + userFile.getPath();
        File file = new File(realPath, userFile.getNewFileName());
        if(file.exists())file.delete();//立即删除

        //删除数据库中记录
        userFileService.delete(id);

        return "redirect:/file/showAll";
    }
  1. 返回当前的文件列表(json格式数据)
@GetMapping("findAllJSON")
    @ResponseBody
    public List<UserFile> findAllJSON(HttpSession session, Model model) {
        //在登录的session中获取用户的id
        User user = (User) session.getAttribute("user");
        //根据用户id查询有的文件信息
        List<UserFile> userFiles = userFileService.findByUserId(user.getId());
        return userFiles;
    }

6、创建对应的界面

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>用户文件列表页面</title>
    <script th:src="@{/js/jquery-3.4.1.min.js}"></script>
    <script>
        $(function(){


            var time;
            $("#start").click(function(){
                console.log("开启定时更新.........");
                time = setInterval(function () {
                    $.get("[[@{/file/findAllJSON}]]", function (res) {
                        //遍历
                        $.each(res, 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.username}"/></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>
            <img th:if="${file.isImg}=='是'" style="width: 100px;height: 40px;" th:src="${#servletContext.contextPath}+${file.path}+'/'+${file.newFileName}" alt="">
            <span th:if="${file.isImg}!='是'" th:text="${file.isImg}"/>
        </td>
        <td th:id="${file.id}"><span th:text="${file.downcounts}"/></td>
        <td><span th:text="${#dates.format(file.uploadTime,'yyyy-MM-dd HH:mm:ss')}"/></td>
        <td>
            <a th:href="@{/file/download(id=${file.id})}">下载</a>
            <a th:href="@{/file/download(id=${file.id},openStyle='inline')}">在线打开</a>
            <a th:href="@{/file/delete(id=${file.id})}">删除</a>
        </td>
    </tr>
</table>
<hr>

<h3>上传文件:</h3>

<form th:action="@{/file/upload}" method="post" enctype="multipart/form-data">
    <input type="file" name="aaa"> <input type="submit" value="上传文件">
</form>

</body>
</html>

六、创建拦截器

public class LoginInterceptor  implements HandlerInterceptor {
    
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        HttpSession session = request.getSession();
        Object user = session.getAttribute("user");
        if(user!=null) return true;
        response.sendRedirect(request.getContextPath()+"/index");
        return false;
    }
}

七、创建过滤器

@Configuration
public class InterceptorConfig extends WebMvcConfigurationSupport {

    @Value("${upload.dir}")
    private String upload;

    @Override
    protected void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new LoginInterceptor())
                .addPathPatterns("/file/**")
                .excludePathPatterns("/css/**")
                .excludePathPatterns("/js/**");//放行静态资源 静态资源被认为是一个控制器请求
    }

    @Override
    protected void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/**") //代表以什么样的请求路径访问静态资源
                .addResourceLocations("classpath:/static/")
                .addResourceLocations("classpath:/templates/")
                .addResourceLocations("file:"+upload);//本地资源路径必须放在最上面

    }
}

八、进行测试

  1. 初始化


    6
  2. 上传后


    7
  3. 下载


    8
  4. 在线预览


    9
  5. 删除
    变为初始化的样子
    有需要源码的可以联系我!

还是要说说我的个人博客,希望大家多多访问,谢谢!

天涯志

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,921评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,635评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,393评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,836评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,833评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,685评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,043评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,694评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,671评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,670评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,779评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,424评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,027评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,984评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,214评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,108评论 2 351
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,517评论 2 343

推荐阅读更多精彩内容