springboot项目实现图片上传和下载,通过自定义Servlet实现图片回显以及文件下载

5 篇文章 0 订阅
5 篇文章 0 订阅

介绍:本次案例使用springboot项目实现了文件的上传及下载,并且在windows环境和Linux环境下都适用。

​ 本次案例是在https://blog.csdn.net/weixin_45614461/article/details/109856069的基础上进行改进,通过自定义servlet实现对上传的图片回显到页面和文件的下载。

一、功能预览:

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

二、功能实现:

2.1、先创建一个表,用来存储上传文件的一些基本信息。

在这里插入图片描述

2.2、创建一个springboot项目,并且导入下列依赖:
<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-thymeleaf</artifactId>
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</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-starter-test</artifactId>
        <scope>test</scope>
        <exclusions>
            <exclusion>
                <groupId>org.junit.vintage</groupId>
                <artifactId>junit-vintage-engine</artifactId>
            </exclusion>
        </exclusions>
    </dependency>
</dependencies>
2.3、修改yml文件:

创建三个yml文件:

application-dev.yml:该配置文件是在windows环境下使用的

spring:
  application:
    name: file_upload_download
  servlet:
    multipart:
      max-file-size: 100MB
      max-request-size: 100MB
  mvc:
    static-path-pattern: /**
  datasource:
    url: jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf8&allowMultiQueries=true&serverTimezone=GMT%2B8
    username: root
    password: 123456
    driver-class-name: com.mysql.cj.jdbc.Driver
server:
  port: 8099
#上传文件的存储地址
file:
  doc-dir: D:/myFile

application-prod.yml:该配置文件是在Linux环境下使用的

spring:
  application:
    name: file_upload_download
  servlet:
    multipart:
      max-file-size: 100MB
      max-request-size: 100MB
  mvc:
    static-path-pattern: /**
  datasource:
    url: jdbc:mysql://192.168.56.2:3306/test?useUnicode=true&characterEncoding=utf8&allowMultiQueries=true&serverTimezone=GMT%2B8
    username: root
    password: 123456
    driver-class-name: com.mysql.cj.jdbc.Driver
server:
  port: 8099

file:
  doc-dir: /usr/local/myFile  #上传文件地址

使用哪个配置文件直接在application.yml中进行切换即可:

spring:
  profiles:
    active: prod #dev
2.4、实体类:
package com.xdemo.file_upload_uownload.entity;

import lombok.Data;

/**
 * @author xdemo
 * @projectName file_upload_uownload
 * @description: TODO
 * @date 2020-11-19 11:39
 */
@Data
public class FileInformation {

    private Integer id;

    private String title;

    private String uploadDate;

    private String imageName;

    private String fileName;
}

2.5、dao层:mapper

创建一个FileMapper,提供将文件信息添加到数据库,以及查询数据库中所有文件信息方法。

package com.xdemo.file_upload_uownload.dao;

import com.xdemo.file_upload_uownload.entity.FileInformation;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;

import java.util.List;

/**
 * @author xdemo
 * @projectName file_upload_uownload
 * @description: TODO
 * @date 2020-11-19 11:42
 */
@Mapper
public interface FileMapper {

    /**
     * 添加
     * @author xdemo
     * @date 2020-11-20 16:17
     * @param title
     * @param uploadDate
     * @param imageName
     * @param fileName
     * @return int
     */
    @Insert("INSERT INTO file_information (title,upload_date,image_name,file_name) VALUES(#{title},#{upload_date},#{image_name},#{file_name})")
    public int insert(@Param("title")String title,@Param("upload_date")String uploadDate,@Param("image_name")String imageName,@Param("file_name")String fileName);

    //查询
    @Select("SELECT id,title,upload_date uploadDate,image_name imageName,file_name fileName from file_information")
    public List<FileInformation> findAllFile();
}

2.6、自定义servlet

创建一个类,用于获取配置文件中的上传文件路径

package com.xdemo.entity;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

@Component
public class Global {

    //文件上传根路径
    private static String dir;

    public static String getDir() {
        return dir;
    }

    @Value("${file.doc-dir}")
    public static void setDir(String dir) {
        Global.dir = dir;
    }
}

自定义一个servlet,用于上传图片的回显以及文件下载,使用注解方式注册servlet

package com.xdemo.servlet;

import com.xdemo.entity.Global;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.FileCopyUtils;
import org.springframework.web.util.UriUtils;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;

/**
 * @description: 自定义servlet,当用户访问指定路径时会进入这个servlet获取对应数据,然后返回一个文件流
 * @date 2021-05-31 10:52
 */
@WebServlet(name = "fileDownloadServlet",urlPatterns = FileDownloadServlet.FILEBASEURL + "*")
public class FileDownloadServlet extends HttpServlet {

    //上传文件和图片存放的文件夹,也就是该servlet的请求路径
    public final static String FILEBASEURL = "/files/";

    private Logger logger = LoggerFactory.getLogger(getClass());

    //将指定文件转为流响应到客户端
    public void fileOutputStream(HttpServletRequest req, HttpServletResponse resp) throws IOException, ServletException {
        String filepath = req.getRequestURI();
        int index = filepath.indexOf(FILEBASEURL);
        if (index >= 0) {
            filepath = filepath.substring(index + FILEBASEURL.length());
        }
        try {
            filepath = UriUtils.decode(filepath, "UTF-8");
        } catch (Exception e1) {
            logger.error(String.format("解释文件路径失败,URL地址为%s", filepath), e1);
        }
        File file = new File(getFilesBaseDir() + FILEBASEURL + filepath);
        try {
            FileCopyUtils.copy(new FileInputStream(file), resp.getOutputStream());  //输出流
            resp.setHeader("Content-Type", "application/octet-stream");
            return;
        } catch (FileNotFoundException e) {
            req.setAttribute("exception", new FileNotFoundException("请求的文件不存在"));
            req.getRequestDispatcher("/error/404.jsp").forward(req, resp);
        }
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        fileOutputStream(req, resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        fileOutputStream(req, resp);
    }

    /**
     * 获取上传文件的根目录
     *
     * @return
     */
    public static String getFilesBaseDir() {
        String DIR = Global.getDir();
        if (!DIR.endsWith("/")) {
            DIR += "/";
        }
        return DIR;
    }

}

扫描servlet,在启动类上加上@ServletComponentScan注解

package com.xdemo;

import com.xdemo.servlet.FileDownloadServlet;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.ServletComponentScan;

@SpringBootApplication
@ServletComponentScan(basePackageClasses = {FileDownloadServlet.class})
public class FileDownloadApplication {

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

}

2.7、服务层:上传实现

创建一个FileService接口

package com.xdemo.service;

import com.xdemo.entity.FileInformation;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import java.util.List;

/**
 * @author xdemo
 * @projectName file_upload_download_2
 * @description: TODO
 * @date 2021-04-28 12:17
 */
public interface FileService {

    public Boolean uploadFile(@RequestParam("file") MultipartFile file, @RequestParam("fileImage")MultipartFile fileImage, @RequestParam("title")String title);

    public List<FileInformation> getAllFile();

}

,FileServiceImpl实现FileService接口,提供文件上传实现和查询所有文件方法。

package com.xdemo.service.impl;

import com.xdemo.dao.FileMapper;
import com.xdemo.entity.FileInformation;
import com.xdemo.entity.Global;
import com.xdemo.service.FileService;
import com.xdemo.servlet.UserfilesDownloadServlet;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;

/**
 * @author xdemo
 * @projectName file_upload_download_2
 * @description: TODO
 * @date 2021-04-28 12:18
 */
@Service
public class FileServiceImpl implements FileService {
    private static final Logger LOGGER= LoggerFactory.getLogger(FileService.class);

    @Autowired
    FileMapper fileMapper;

    @Override
    public Boolean uploadFile(MultipartFile file, MultipartFile fileImage, String title) {
        //获取上传文件存储文件夹
        String saveDir = UserfilesDownloadServlet.USERFILES_BASE_URL;
        //根据日期拼接文件路径
        saveDir += new SimpleDateFormat("yyyy/MM/dd").format(new Date()) + "/";
        //文件存储的真实路径
        String realPath = "";
        realPath += UserfilesDownloadServlet.getUserfilesBaseDir() + UserfilesDownloadServlet.USERFILES_BASE_URL;
        realPath += new SimpleDateFormat("yyyy/MM/dd").format(new Date()) + "/";
        String fileName = file.getOriginalFilename(); //上传的文件名
        String imageName = fileImage.getOriginalFilename(); //上传的图片名
        File fileDir = new File(realPath); //上传后的文件存储地址文件夹
        //判断文件夹是否存在
        if(!fileDir.exists()){
            //不存在,创建
            fileDir.mkdirs();
        }
        //文件存放地址
        File resultFile = new File(fileDir + "/" + fileName);
        //图片存放地址
        File resultImg = new File(fileDir + "/" + imageName);
        if(resultFile.exists()){
            LOGGER.warn("文件已经存在!");
            return false;
        }
        if(resultImg.exists()){
            LOGGER.warn("图片已经存在!");
            return false;
        }
        try {
            file.transferTo(resultFile);
            fileImage.transferTo(resultImg);
        } catch (IOException e) {
            e.printStackTrace();
        }
        //结果存入数据库
        fileMapper.insert(title, new SimpleDateFormat("yyyy-MM-dd").format(new Date()), saveDir+imageName, saveDir+fileName);
        return true;
    }

    /**
     * 查询数据库中所有文件信息的方法
     * @date 2020-11-20 16:42
     * @param
     */
    @Override
    public List<FileInformation> getAllFile() {
        return fileMapper.findAllFile();
    }

}


2.8、控制器:

创建一个FileController,提供文件上传的控制器和一个查询所有文件后跳转前端页面的控制器。

package com.xdemo.controller;

import com.xdemo.entity.FileInformation;
import com.xdemo.service.FileService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import java.util.List;

/**
 * @author xdemo
 * @projectName file_download
 * @description: TODO
 * @date 2021-05-31 11:11
 */
@Controller
public class FileController {
    @Autowired
    FileService fileService;

    private static final Logger LOGGER= LoggerFactory.getLogger(FileController.class);

    @RequestMapping("/")
    public String index(HttpServletRequest request){
        List<FileInformation> allFile = fileService.getAll();
        request.setAttribute("fileList", allFile);
        return "fileDownload";
    }

    /**
     * 文件上传
     * @author xdemo
     * @date 2020-11-20 16:50
     * @param file    文件
     * @param fileImage  用于做封面的图片
     * @param title   标题
     * @return java.lang.String
     */
    @PostMapping("/uploadFile")
    public String uploadFile(@RequestParam("file") MultipartFile file, @RequestParam("fileImage")MultipartFile fileImage, @RequestParam("title")String title){
        if(file.isEmpty()){
            LOGGER.error("上传失败,请选择文件!");
            return "redirect:/";
        }
        try {
            boolean result = fileService.uploadFile(file,fileImage,title);
//            LOGGER.info(result);
            return "redirect:/getAllFile";
        } catch (Exception e) {
            e.printStackTrace();
            LOGGER.error("文件上传失败!");
            return "redirect:/";
        }
    }
}

3.0、前端页面:

创建一个前端页面,使用表单将用户输入的文件信息传输到controller。

<!DOCTYPE html>
<html lang="en" xmlns:th="https://www.thymeleaf.org">
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <!-- 上述3个meta标签*必须*放在最前面,任何其他内容都*必须*跟随其后! -->
    <title>Bootstrap 101 Template</title>

    <!-- Bootstrap -->
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css" rel="stylesheet">

    <style>
        html,body{
            height: 100%;
            width: 100%;
            margin: 0 auto;
        }
        .titleClass{
            margin-top:8px;
            color: white;
            font-weight: bolder;
        }
        .timeClass{
            margin-top: 25px;
            margin-bottom: 10px;
            color: grey;
            font-size: 14px;
        }
        .contentTd{
            padding-left: 10px;
            padding-right: 10px;
            width: 150px!important;
            height: 150px;
        }
        tr{
            margin-top: 10px;
            margin-bottom: 60px;
            display: block;
        }
        .buttonP{
            padding-top: 20px;
        }
        .imageTd{
            width: 267px!important;
            height: 150px;
        }
        .imageTd img{
            width: 100%;
            height: 100%;
        }
    </style>
</head>
<body>
<!-- jQuery (Bootstrap 的所有 JavaScript 插件都依赖 jQuery,所以必须放在前边) -->
<script src="https://cdn.jsdelivr.net/npm/jquery@1.12.4/dist/jquery.min.js"></script>
<!-- 加载 Bootstrap 的所有 JavaScript 插件。你也可以根据需要只加载单个插件。 -->
<script src="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/js/bootstrap.min.js"></script>
<div style="width: 100%;height: 100%;background-color: #0B656D">
    <table align="center" style="width: 85%;">
        <th:block th:each="usr,status:${fileList}">
            <p th:remove="tag" th:utext="${(status.index+1)%3==1 ? '&lt;tr&gt;':''}"/>
            <td class="imageTd">
                <img th:src="@{/images/{imageName}(imageName=${usr.imageName})}">
            </td>
            <td class="contentTd">
                <p class="titleClass"><span th:text="${usr.title}"></span></p>
                <p class="timeClass"><span th:text="${usr.uploadDate}"></span></p>
                <p class="buttonP">
                    <!--<a href="/download/2018年度中国城市活力研究报告.pdf" download>-->
                    <a th:href="@{/download/{fileName}(fileName=${usr.fileName})}" download>
                        <button type="button" class="btn btn-primary">下载</button>
                    </a>
                </p>
            </td>
            <p th:remove="tag" th:utext="${(status.index+1)%5==0 ? '&lt;/tr&gt;':''}"/>
        </th:block>
        <tr>
            <td>
                <button type="button" class="btn btn-primary" data-toggle="modal" data-target="#myModal">
                上传
                </button>
            </td>
        </tr>
    </table>
    <div class="modal fade" id="myModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel">
        <div class="modal-dialog" role="document">
            <div class="modal-content">
                <div class="modal-header">
                    <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
                    <h4 class="modal-title" id="myModalLabel">文件上传</h4>
                </div>
                <div class="modal-body">
                    <form enctype="multipart/form-data" method="post" action="/uploadFile">
                        <div class="form-group">
                            <label for="exampleInputEmail1">文件标题</label>
                            <input type="text" class="form-control" id="exampleInputEmail1" placeholder="文件标题" name="title">
                        </div>
                        <div class="form-group">
                            <label for="exampleInputFile">文件</label>
                            <input type="file" id="exampleInputFile" name="file">
                            <p class="help-block">上传文件</p>
                        </div>
                        <div class="form-group">
                            <label for="exampleInputFile">文件封面</label>
                            <input type="file" id="fileImage" name="fileImage">
                            <p class="help-block">上传文件封面</p>
                        </div>
                        <div class="modal-footer">
                            <button type="button" class="btn btn-default" data-dismiss="modal">关闭</button>
                            <button type="submit" class="btn btn-primary">提交</button>
                        </div>
                    </form>
                </div>

            </div>
        </div>
    </div>
</div>
</body>
</html>

我这里使用了Bootstrp的模态框作为表单弹出,以及做了一点样式。

在这里插入图片描述

这段代码是当一行()里面有三个单元格之后就会添加一个,也就是下图的效果:

在这里插入图片描述

参考博客:https://blog.csdn.net/iteye_19045/article/details/97809707

3.1、测试:上传成功之后,yml里的文件存放路径可以看到刚才上传的文件

在这里插入图片描述

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值