基于SpringBoot实现文件的上传、下载和在线预览(SpringBootDemo)

在java领域中SpringBoot是一个非常好用的框架,可以快速地构建web项目,这里记录一下使用SpringBoot来实现文件的上传、下载和在线预览功能。

1.创建数据库和数据库表

本文主要用到用户和用户所属的文件,所以这里就只需要设计用户表和文件表即可。

用户表t_user

CREATE TABLE `t_user` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `username` varchar(80) DEFAULT NULL,
  `password` varchar(80) DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC

insert into t_user(username, password) values("pikacho", "123456");

文件表t_files

CREATE TABLE `t_files` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `file_name` varchar(200) DEFAULT NULL,
  `ext` varchar(20) DEFAULT NULL,
  `path` varchar(300) DEFAULT NULL,
  `size` bigint(64) DEFAULT NULL,
  `type` varchar(120) DEFAULT NULL,
  `download_counts` int(6) DEFAULT NULL,
  `upload_time` datetime DEFAULT NULL,
  `user_id` int(11) NOT NULL,
  PRIMARY KEY (`id`),
  KEY `user_id` (`user_id`),
  CONSTRAINT `t_files_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `t_user` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8

基于SpringBoot实现文件的上传、下载和在线预览(SpringBootDemo)_第1张图片

2.创建SpringBoot项目

这里不用多说,使用IDEA创建SpringBoot项目,删除多余没用的文件,让项目结构保持简洁。

基于SpringBoot实现文件的上传、下载和在线预览(SpringBootDemo)_第2张图片

 简单地创建一个控制器,然后启动项目,测试该SpringBoot项目是否构建成功。

@Controller
public class HelloController {

    @RequestMapping("hello")
    @ResponseBody
    public String hello(){
        return "hello SpringBoot";
    }
}
 
  

访问项目:http://localhost:8080/hello,出现如下画面说明项目能够成功运行;先保证项目能成功运行,然后再考虑开发功能。

基于SpringBoot实现文件的上传、下载和在线预览(SpringBootDemo)_第3张图片

3.添加该项目需要的依赖以及配置基本环境

pom.xml依赖

       
        
        
            org.springframework.boot
            spring-boot-starter-thymeleaf
        

        
        
            org.springframework.boot
            spring-boot-starter-web
        

        
        
            org.projectlombok
            lombok
            true
        

        
        
            org.mybatis.spring.boot
            mybatis-spring-boot-starter
            2.2.0
        

        
        
            mysql
            mysql-connector-java
            runtime
        

        
        
            org.apache.shiro
            shiro-spring
            1.4.1
        

        
        
            commons-fileupload
            commons-fileupload
            1.4
        

         
        
            com.alibaba
            druid
            1.1.19
        

        
            org.springframework.boot
            spring-boot-starter-test
            test
        
    

application.yml主配置文件

spring:
  application:
    name: fileStorage
  thymeleaf:
    cache: false
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/fileservice?serverTimezone=Asia/Shanghai&userUnicode=true&characterEncoding=utf-8
    username: root
    password: root

  server:
    port: 8080


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

添加完依赖以及将主配置文件配置完成,完成本项目功能需要的基本环境就已经全部搭建完成了,启动项目测试项目是否可以正常运行;出现下面的画面说明项目基本环境搭建成功。

基于SpringBoot实现文件的上传、下载和在线预览(SpringBootDemo)_第4张图片

4.用户登录、注销及注册功能

4.1.完成登录功能

即使一个非常简单的demo项目,也是少不了前端页面。对于一个后端开发者来说,要自己去写前端页面布局和样式是一件挺痛苦也困难的事(是真的不会写啊!)。所以这里使用layui框架来快速构建前端页面。当然,这只是简单的使用一下该框架,想要熟练使用。还需继续深入学习才行。

1.引入layui

将layui需要的文件添加到static文件夹下

基于SpringBoot实现文件的上传、下载和在线预览(SpringBootDemo)_第5张图片

 2.创建登录页面

login.html




    
    
    login
    



请 登 录

UserController.java

    /**
     * 前往登录页面
     * @return
     */
    @RequestMapping("toLogin")
    public String toLogin(){
        return "login";
    }

基于SpringBoot实现文件的上传、下载和在线预览(SpringBootDemo)_第6张图片

这样,之后我们就可以轻松地创建前端页面了,并且不用自己去设计样式和布局等问题。还是要提一下,像这样简单使用一下这种程度就够了,但是要熟练使用的话,就要更加深入学习才行。

3.完成登录功能

  • 创建User实体类--User.java
@Data
@NoArgsConstructor
@AllArgsConstructor
@ToString
public class User {
    
    private Integer id;
    private String username;
    private String password;
}
  • 创建UserDao及其mapper映射文件--UserDao.java & UserDaoMapper.xml

@Mapper
@Repository
public interface UserDao {

    /**
     * 用户登录功能
     * @return
     */
    public User login(User user);


}



    
    

  • 创建UserService类及其实现类--UserService.java & UserServiceImpl.java

public interface UserService {
    public User login(User user);
}
@Service
public class UserServiceImpl implements UserService {

    @Autowired
    public UserDao userDao;

    @Override
    public User login(User user){
        return userDao.login(user);
    }
}
  • 利用Shiro框架实现登录认证功能

Shiro框架可以快速实现登录认证等安全权限功能,这里也是简单使用一下,想要深入理解使用还需查阅其官方文档。

UserRealm.java

public class UserRealm extends AuthorizingRealm {

    @Autowired
    private UserService userService;


    // 授权
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection){
        return null;
    }

    // 认证
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) {
        UsernamePasswordToken usernamePasswordToken = (UsernamePasswordToken)token;

        User user = new User();
        user.setUsername(usernamePasswordToken.getUsername());
        user.setPassword(String.valueOf(usernamePasswordToken.getPassword()));
        // 从数据库查询出来
        User userDB = userService.login(user);
        if(userDB == null){
            return null;
        }

        return new SimpleAuthenticationInfo(userDB, user.getPassword(), "");

    }
}

ShiroConfig.java

@Configuration
public class ShiroConfig {

    // 第一步:创建UserRealm对象
    @Bean
    public UserRealm userRealm(){
        return new UserRealm();
    }

    // 第二步:创建默认安全管理器DefaultWebSecurityManager
    @Bean(name = "securityManager")
    public DefaultWebSecurityManager getDefaultWebSecurityManager(
            @Qualifier("userRealm") UserRealm userRealm ){
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setRealm(userRealm);
        return securityManager;
    }

    // 第三步:创建ShiroFilterFactoryBean
    @Bean
    public ShiroFilterFactoryBean getShiroFilterFactoryBean(
            @Qualifier("securityManager") DefaultWebSecurityManager securityManager){
        ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
        // 设置安全管理器
        bean.setSecurityManager(securityManager);
        /*
        *   anon: 无需认证就能访问
            authc: 必须认证后才能访问
            user: 必须拥有 记住我 功能才能用
            perms: 拥有对某个资源的权限才能访问
            role: 拥有某个角色权限才能访问
         */

        // 拦截/file/* ,该路径只有登录的情况下才能查看
        Map filterMap = new LinkedHashMap<>();
        filterMap.put("/file/*", "authc");
        bean.setFilterChainDefinitionMap(filterMap);

        // 设置没有访问权限时,跳转页面
        bean.setLoginUrl("/login");
        return bean;
    }
}
  • 在UserController类中添加登录方法
    @PostMapping("login")
    public String login(User user, Model model){
        Subject subject = SecurityUtils.getSubject();
        // 封装用户信息
        UsernamePasswordToken token = new UsernamePasswordToken(user.getUsername(), user.getPassword());
        try{
            subject.login(token);
            Session session = subject.getSession();
            session.setAttribute("user", (User)subject.getPrincipal());
            // 前往文件列表页面
            return "list";
        }catch (UnknownAccountException e){
            model.addAttribute("msg", "用户名或密码错误");
            return "login";
        }catch (IncorrectCredentialsException e){
            model.addAttribute("msg", "用户名或密码错误");
            return "login";
        }
    }
  • 最后在SpringbootFileuploadApplication类上添加包扫描注解,不然mybatis无法将接口和xml配置文件绑定起来
@SpringBootApplication
@MapperScan(basePackages = "com.pikacho.dao")
public class SpringbootFileuploadApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpringbootFileuploadApplication.class, args);
    }

}

登录功能代码已经开发完毕了,现在测试一下功能是否正常。 

访问项目:http://localhost:8080/user/toLogin,之前创建用户表时,就已经创建了一个用户。用户名:pikacho  密码:123456。

基于SpringBoot实现文件的上传、下载和在线预览(SpringBootDemo)_第7张图片

 基于SpringBoot实现文件的上传、下载和在线预览(SpringBootDemo)_第8张图片

 4.2.完成注册功能

1.创建注册页面

register.html




    
    
    register
    



请 注 册

2.完成注册功能

  • 在UserDao类中添加register(),编写mapper映射文件
    /**
     *
     * @param user
     */
    public void register(User user);
    
    
        insert into t_user (username, password)
        values(#{username}, #{password});
    
  • 在UserService类及实现类中添加注册方法
    /**
     *
     * @param user
     */
    public void register(User user);
    /**
     *
     * @param user
     */
    @Override
    public void register(User user){
        userDao.register(user);
    }
  • 在UserController类中添加注册逻辑

测试注册功能, 访问项目:http://localhost:8080/user/toRegister ;添加用户pikacho2 :123456

 基于SpringBoot实现文件的上传、下载和在线预览(SpringBootDemo)_第9张图片

 基于SpringBoot实现文件的上传、下载和在线预览(SpringBootDemo)_第10张图片

4.3.创建list页面

前面只是简单的创建一个list页面验证登录功能,现在补全list页面。

list.html




    
    
    FileList
    



基于SpringBoot实现文件的上传、下载和在线预览(SpringBootDemo)_第11张图片

 4.4.完成注销功能

  • 在UserController类中添加注销方法
    /**
     * 注册功能
     * @return
     */
    @GetMapping("logout")
    public String logout(){
        Subject subject = SecurityUtils.getSubject();
        subject.logout();
        // 这里需要注意:redirect:user/toLogin 实际的请求路径http://localhost:8080/user/user/toLogin
        // 所以需要使用redirect:toLogin
        return "redirect:toLogin"; 
    }

5.文件的展示、上传、删除、下载功能

5.1.文件的展示

  • 创建文件实例类--UserFile.java
@Data
@NoArgsConstructor
@AllArgsConstructor
@ToString
@Accessors(chain = true)
public class UserFile {
    private Integer id;
    private String fileName;
    private String ext;
    private String path;
    private long size;
    private String type;
    private Integer downloadCounts;
    private Date uploadTime;
    private Integer userId;
}
  • 创建UserFileDao及其mapper映射文件
@Mapper
@Repository
public interface UserFileDao {

    /**
     * 根据用户id获得用户文件列表
     * @param id
     * @param begin
     * @param offset
     * @return
     */
    public List queryByUserId(Integer id, Integer begin, int offset);


    /**
     * 根据用户id获得该用户文件总数
     * @param id
     * @return
     */
    public int queryFileCount(Integer id);
}



    
        
        
        
        
        
        
    

    
    
     
    
  • 创建UserFileService类及其实现类
public interface UserFileService {

    /**
     * 根据用户id获得文件列表
     * @param id
     * @param page
     * @param limit
     * @return
     */
    public List queryByUserId(Integer id, Integer page, Integer limit);

    /**
     * 根据用户id获得文件数
     * @param id
     * @return
     */
    public int queryFileCounts(Integer id);
}
@Service
public class UserFileServiceImpl implements UserFileService {
    @Autowired
    private UserFileDao userFileDao;

    /**
     * 根据用户id获得文件列表
     * @param id
     * @param page
     * @param limit
     * @return
     */
    @Override
    public List queryByUserId(Integer id, Integer page, Integer limit){
        // page表示第几页,limit表示每页显示多少行数据
        int begin = (page-1)*limit;   // 该计算方法获得开始的位置
        int offset = limit;
        return userFileDao.queryByUserId(id, begin, limit);
    }

    /**
     * 根据用户id获得文件数
     * @param id
     * @return
     */
    @Override
    public int queryFileCounts(Integer id){
        return userFileDao.queryFileCounts(id);
    }
}
  • 创建UserFileController类
@Controller
@RequestMapping("file")
public class UserFileController {
    @Autowired
    private UserFileService userFileService;

    /**
     * 返回文件列表
     * @param session
     * @param request
     * @return
     */
    @PostMapping("all")
    @ResponseBody
    public Map queryAllFile(HttpSession session, HttpServletRequest request){
        int page = Integer.parseInt(request.getParameter("page"));
        int limit = Integer.parseInt(request.getParameter("limit"));
        User user = (User) session.getAttribute("user");
        List files = userFileService.queryByUserId(user.getId(), page, limit);

        Map res = new HashMap<>();
        res.put("code", 0);
        res.put("count", userFileService.queryFileCounts(user.getId()));
        res.put("data", files);
        return res;
    }

}
  • 接着在list.html中添加渲染表格的脚本
        // 渲染表格
        table.render({
            elem: '#fileList',
            height: 600,
            minWidth: 80,
            url: '/file/all',    // 这一定要是/file/all,之前我是file/all不停的报错,无法访问成功
            parseData: function (res) {
                            return {
                                "code": res.code,
                                "msg": "",
                                "count": res.count,
                                "data": res.data
                            };
            },
            method: 'post',
            limit: 10,
            page: true,
            cols: [[
                {field:'id', title:'ID', sort:true, fixed:'left'},
                {field:'fileName', title:'文件名'},
                {field:'ext', title:'文件后缀'},
                {field:'path', title:"存储路径"},
                {field:'size', title:'大小'},
                {field:'type', title:"类型"},
                {field:'downloadCounts', title:'下载次数'},
                {field:'uploadTime', title:'上传时间'},
                {tilte:'操作',align:'center', toolbar:'optBar', width:200, fixed:'right'}
            ]]
        });

基于SpringBoot实现文件的上传、下载和在线预览(SpringBootDemo)_第12张图片

5.2.文件上传

  • 在UserFileDao类中添加上传文件方法及mapper映射文件
    /**
     * 上传文件
     * @param userFile
     */
    public void save(UserFile userFile);
    
    
        insert into t_files(file_name, ext, path, size, type, download_counts, upload_time, user_id)
        values(#{fileName}, #{ext}, #{path}, #{size}, #{type}, #{downloadCounts}, #{uploadTime}, #{userId});
    
  • 在UserFileService类及UserFileServiceImpl类中添加方法
    /**
     * 上传文件
     * @param userFile
     */
    public void save(UserFile userFile);



    /**
     * 上传文件
     * @param userFile
     */
    @Override
    public void save(UserFile userFile){
        userFile.setDownloadCounts(0).setUploadTime(new Date());
        userFileDao.save(userFile);
    }
  • 在UserFileController类中添加上传文件接口
    @GetMapping("index")
    public String fileIndex(){
        return "list";
    }


    /**
     * 上传文件
     * @param file
     * @param session
     * @return
     */
    @PostMapping("upload")
    @ResponseBody
    public Map uploadFile(@RequestParam("file")MultipartFile file, HttpSession session){
        Map res = new HashMap<>();
        try{
            User user = (User) session.getAttribute("user");
            String fileName = file.getOriginalFilename();
            String extension = FilenameUtils.getExtension(fileName);
            long size = file.getSize();
            String type = file.getContentType();
            // 根据日期生成目录
            String localContainer = "/fileContainer";
            String uploadPath = ResourceUtils.getURL("classpath").getPath()+localContainer;
            String dateFormat = new SimpleDateFormat("yyyy-MM-dd").format(new Date());
            File dateDirPath = new File(uploadPath+File.separator+dateFormat);
            if(!dateDirPath.exists()){
                dateDirPath.mkdirs();
            }

            file.transferTo(new File(dateDirPath, fileName));
            // 将文件信息存入数据库
            UserFile userFile = new UserFile();
            userFile.setFileName(fileName)
                    .setExt('.'+extension)
                    .setPath(Paths.get(localContainer, dateFormat, fileName).toString())
                    .setSize(size)
                    .setType(type)
                    .setUserId(user.getId());

            userFileService.save(userFile);

            res.put("code", "0");
            res.put("msg", "上传成功");
            res.put("url", "/file/index");

        }catch(IOException e){
            res.put("code", "-1");
            res.put("msg", "上传失败");
            res.put("url", "/file/index");
        }
        return res;
    }

基于SpringBoot实现文件的上传、下载和在线预览(SpringBootDemo)_第13张图片

 基于SpringBoot实现文件的上传、下载和在线预览(SpringBootDemo)_第14张图片

这里文件存储在我的本地磁盘上,文件表中存储的是文件在我磁盘存储的存储路径。

5.3.文件下载

开发流程与上面基本一致。

 // UserFiledao
   /**
     * 下载文件
     * @param id
     * @return
     */
    public UserFile queryByUserFileId(Integer id);

    /**
     * 更新文件下载次数
     * @param userFile
     */
    public void update(UserFile userFile);


// UserFileService
    /**
     * 下载文件
     * @param id
     * @return
     */
    public UserFile queryByUserFileId(Integer id);

    /**
     * 跟新文件下载次数
     * @param userFile
     */
    public void update(UserFile userFile);

// UserFileServiceImpl
    /**
     * 下载文件
     * @param id
     * @return
     */
    @Override
    public UserFile queryByUserFileId(Integer id) {
        return userFileDao.queryByUserFileId(id);
    }

    /**
     * 跟新文件下载次数
     * @param userFile
     */
    @Override
    public void update(UserFile userFile) {
        userFileDao.update(userFile);
    }
    
    
    
    
        update t_files set download_counts = #{downloadCounts} where id = #{id};
    
    /**
     * 下载文件
     * @param id
     * @param response
     */
    @GetMapping("download/{id}")
    public void download(@PathVariable("id") Integer id, HttpServletResponse response){
        String openStyle = "attachment";
        try{
            getFile(openStyle, id, response);
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    /**
     * 更新文件下载次数
     * @param openStyle
     * @param id
     * @param response
     * @throws Exception
     */
    public void getFile(String openStyle, Integer id, HttpServletResponse response) throws Exception {
        UserFile userFile = userFileService.queryByUserFileId(id);
        String realPath = ResourceUtils.getURL("classpath").getPath()+userFile.getPath();

        FileInputStream is = new FileInputStream(new File(realPath));
        // 附件下载
        response.setHeader("content-disposition", openStyle+";filename=" + URLEncoder.encode(userFile.getFileName(), "UTF-8"));
        // 获取响应response输出流
        ServletOutputStream os = response.getOutputStream();
        // 文件拷贝
        IOUtils.copy(is, os);
        IOUtils.closeQuietly(is);
        IOUtils.closeQuietly(os);

        if(openStyle.equals("attachment")){
            userFile.setDownloadCounts(userFile.getDownloadCounts()+1);
            userFileService.update(userFile);
        }
    }

list.html

        //监听工具条
        table.on('tool(fileTable)', function (obj) {
            var data = obj.data;
            if (obj.event === 'download') {
                window.open("/file/download/" + data.id);
                obj.update({
                    "downloadCounts": data.downloadCounts + 1
                });
            } else if (obj.event === 'delete') {
                layer.confirm('真的删除文件吗?', function (index) {
                    $.ajax({
                        url: "/file/delete/" + data.id,
                        type: "Get",
                        success: function (res) {
                            layer.msg(res.msg);
                            obj.del();
                        },
                        error: function (res) {
                            $.message.alert('msg', res.msg);
                        }
                    });
                    layer.close(index);
                });
            } else if (obj.event === 'preview') {
                
                layer.open({
                    type: 2,
                    skin: 'layui-layer-demo', //样式类名
                    title: '文件预览',
                    closeBtn: 1, //显示关闭按钮
                    anim: 2,
                    area: ['893px', '600px'],
                    shadeClose: true, //开启遮罩关闭
                    content: '/file/preview/' + data.id
                });
            }
        });

基于SpringBoot实现文件的上传、下载和在线预览(SpringBootDemo)_第15张图片

 5.4.文件删除

文件删除开发流程也是同上。

6.完成文件预览功能

  • 添加预览方法

    /**
     * 文件预览
     * @param id
     * @param response
     * @throws IOException
     */
    @GetMapping("preview/{id}")
    public void preview(@PathVariable("id") Integer id, HttpServletResponse response) throws Exception {
        String openStyle = "inline";
        getFile(openStyle,id,response);
    }

基于SpringBoot实现文件的上传、下载和在线预览(SpringBootDemo)_第16张图片

到此,这个小项目就基本完成。

demo源码地址https://github.com/picacho-pkq/SpringBoot-demo

demo下载地址https://download.csdn.net/download/pikcacho_pkq/75146243

你可能感兴趣的:(SpringBoot,java,spring,intellij-idea)