SpringBoot结合Freemaker+ace前端界面轻松实现文件的上传和下载

本文章旨在帮助首次使用SpringBoot的新手开发人员进行文件的上传和下载基本的操作,结合ACE好看的页面风格实现上传和下载,简单快捷。本人使用的是Idea工具。

  1. 创建SpringBoot项目:
    (1)创建项目:打开Idea创建项目 File–>new–>project–>Spring Initializr

    (2)输入一系列的包名项目名相关的,此处就不详细介绍了,继续下一步

    (3)选择Dependencies,这里我用JPA作为持久层的组件以及选用的Freemarker模板引擎。注意要选择Web+JPA+Freemarker.

    (4)创建完成以后,等待初始化,一个SpringBoot的项目就已经创建好了。可以看到比起SpringMVC还是简单了不少。接下来打开项目配置基本的信息(数据库连接),在项目结构的resources下面会有一个Springboot的配置文件填入相关的数据库信息。我把我自己的配置文件包括后期使用的相关配置一并贴上,大家可供参考,可以去相关的博客搜索SpringBoot的配置。
#数据源配置
spring.datasource.url = jdbc:mysql://localhost:3306/job?characterEncoding=utf8&useSSL=true
spring.datasource.username = root
spring.datasource.password =root
spring.datasource.driverClassName = com.mysql.jdbc.Driver

#JPA配置
spring.jpa.properties.hibernate.hbm2ddl.auto=update
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5InnoDBDialect
spring.jpa.show-sql= true

#上传配置
spring.servlet.multipart.max-file-size=100MB
spring.servlet.multipart.max-request-size=100MB
sys.upload.savepath=D:\\uploadfiles\\

(5)引入相关的前端页面的CSS和JS静态资源。放入到项目的resources下面的static目录中。此处我将整个ACE的资源都放进去了,个人可以自定义需要什么再放入什么。另外这里引入一个layer的插件,弹出层,大家可以百度一下挺好用的一个。

springboot项目开发好的前端页面放哪里_JPA

(6)这些基本工作完毕以后即可开始写代码,先加载第一个页面。

  1. 在JAVA目录中创建相关的包和JAVA类
  2. springboot项目开发好的前端页面放哪里_spring_02

  3. 我们的业务是这样的:现在有一个数据列表,每一条数据后面都附有一个自己的附件上传下载和管理,附件同时也要进行管理,因此我们这里有两个数据模型,一个是附件管理,一个是信息管理,都需要保存数据库。效果图是这样的:
  4. springboot项目开发好的前端页面放哪里_SpringBoot_03

  5. 项目结构图
  6. springboot项目开发好的前端页面放哪里_Dropzone.js_04

  7. 废话不多说直接上代码,IndexController:
/**
 * 首页相关业务Controller
 */
@Controller
@RequestMapping("/index")
public class IndexController {

    @Autowired
    private PowerInfoServiceImpl serviceImpl;
    @Autowired
    FileManageServiceImpl fileManageServiceImpl;

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

    /**
     * 查询列表
     * @return
     */
    @RequestMapping(value = "/list")
    public ModelAndView findAllList(){
        ModelAndView mv = new ModelAndView();
        mv.addObject("powerList", serviceImpl.findAll());
        mv.setViewName("/index/list");
        return mv;
    }
     /**
     * 附件上传页面
     * @return
     */
    @RequestMapping(value = "/upload_html")
    public ModelAndView upload_html(){
        ModelAndView mv = new ModelAndView();
        mv.setViewName("/index/upload");
        return mv;
    }


    /**
     * 上传附件至服务器本地
     * @param request
     * @return
     * @throws IOException
     * @throws ServletException
     */
    @RequestMapping(value = "/upload")
    @ResponseBody
    public String uploadFiles(HttpServletRequest request) throws IOException, ServletException {
        String[] file_ids=(String[])(request.getParameterValues("file_id"));
        String file_id=file_ids[0];
        List<Part> listPart = new ArrayList<>();
        try {
            Iterator its = request.getParts().iterator();
            while (its.hasNext()) {
                Part part = (Part) its.next();
                listPart.add(part);
            }

            for (Part p :listPart) {
                FileUploadTool.createDirectory(savepath+file_id);
                FileUploadTool.writeToLocal(savepath+file_id+"\\"+FileUploadTool.getFileName(p),p);

                FileManage f=new FileManage();
                f.setCreateTime(new Date());
                f.setFileName(FileUploadTool.getFileName(p));
                f.setFilePath(savepath+file_id+"\\"+FileUploadTool.getFileName(p));
                f.setInfoId(Integer.parseInt(file_id));
                fileManageServiceImpl.save(f);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ServletException e) {
            e.printStackTrace();
        }
       return "success";
    }


    /**
     * 根据点击的信息获取当前信息的附件列表
     * @param request
     * @return
     */
    @RequestMapping(value = "/fileList")
    @ResponseBody
    public ModelAndView fileList(HttpServletRequest request){
        String info_id= request.getParameter("info_id");
        ModelAndView mv = new ModelAndView();
        mv.addObject("fileList", fileManageServiceImpl.findByInfoId(Integer.parseInt(info_id)));
        mv.setViewName("/index/fileList");
        return mv;
    }

    /**
     * 下载附件
     * @param request
     * @param response
     * @throws IOException
     */
    @RequestMapping(value = "/downloadFile")
    @ResponseBody
    public void downloadFile(HttpServletRequest request,HttpServletResponse response) throws IOException {
        String info_id= request.getParameter("file_id");

        Optional<FileManage> f=fileManageServiceImpl.findById(Integer.parseInt(info_id));

        FileManage fm=f.orElse(null);
        FileUploadTool.downloadFile(request,response,fm.getFilePath(),fm.getFileName());


    }

    /**
     * 删除对应的附件
     * @param request
     * @param response
     * @throws IOException
     */
    @RequestMapping(value = "/deleteFile")
    @ResponseBody
    public void deleteFile(HttpServletRequest request,HttpServletResponse response) throws IOException {
        String info_id= request.getParameter("file_id");

        Optional<FileManage> f=fileManageServiceImpl.findById(Integer.parseInt(info_id));

        FileManage fm=f.orElse(null);
        FileUploadTool.deleteFile(new File(fm.getFilePath()));
        fileManageServiceImpl.delFile(fm.getId());


    }
    }

6.上传和下载工具类FileUploadTool:

/**
 * 文件上传下载相关工具类
 */
public class FileUploadTool {



    /**
     * 将上传文件写到本地
     * @param PathName 路径
     * @param part 文件流
     */
    public static   void writeToLocal(String PathName, Part part)  {
        InputStream in = null;
        try {
            in = part.getInputStream();

        OutputStream out = new FileOutputStream(PathName);
        byte[] buffer = new byte[1024];
        int length = -1;
        while ((length = in.read(buffer)) != -1) {
            out.write(buffer, 0, length);
        }
            in.close();
            out.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 获取上传文件的文件名
     * @param part 文件流
     * @return
     */
    public static String getFileName(Part part) {
                String head = part.getHeader("Content-Disposition");
                String fileName = head.substring(head.indexOf("filename=\"")+10, head.lastIndexOf("\""));
                return fileName;
           }


    /**
     * 创建file所指定的文件夹目录 成功返回true(已经存在了也返回true) 失败返回false
     * @return
     */
    public static boolean createDirectory(String filePath) {
        File file=new  File(filePath);
        if (file == null) {
            return false;
        }
        if (file.exists()) {
            if (file.isDirectory()) {
                return true;
            } else {
                return false;
            }
        }
        if (file.mkdirs()) {
            return true;
        }
        return false;
    }

    /**
     * 下载文件
     * @param request
     * @param response
     * @param filePath 文件的路径
     * @param filename 文件的名称
     * @throws IOException
     */
    public static void downloadFile(HttpServletRequest request, HttpServletResponse response,String filePath,String filename) throws IOException {
        File file = new File(filePath);
        if (file.exists()) {
            // /文件存在,完成下载

            // 下载注意事项1--设置下载文件的mimeType
            String mimeType = request.getServletContext().getMimeType(filename);
            response.setContentType(mimeType);

            String agent = request.getHeader("user-agent").toLowerCase();

            if (agent.contains("msie")) {
                // IE浏览器
                filename = URLEncoder.encode(filename, "utf-8");

            } else if (agent.contains("Firefox")) {
                // 火狐浏览器
                BASE64Encoder base64Encoder = new BASE64Encoder();
                filename = "=?utf-8?B?"
                        + base64Encoder.encode(filename.getBytes("utf-8"))
                        + "?=";
            } else {
                // 其它浏览器
                filename = URLEncoder.encode(filename, "utf-8");
            }

            // 下载注意事项2--永远是下载
            response.setHeader("content-disposition", "attachment;filename="
                    + filename);

            FileInputStream fis = new FileInputStream(file); // 读取要下载文件的内容
            OutputStream os = response.getOutputStream(); // 将要下载的文件内容通过输出流写回到浏览器端.
            int len = -1;
            byte[] b = new byte[1024 * 100];

            while ((len = fis.read(b)) != -1) {
                os.write(b, 0, len);
                os.flush();
            }
            os.close();
            fis.close();

        } else {
            throw new RuntimeException("下载资源不存在.");
        }

    }

    /**
     * 删除文件
     * @param file
     * @return
     */
    public static boolean deleteFile(File file) {
        if (file == null) {
            return false;
        }
        if (!file.exists()) {
            return true;
        }
        if (file.isDirectory()) {
            File[] files = file.listFiles();
            for (File subFile : files) {
                deleteFile(subFile);// 递归删除
            }
        }
        file.delete();
        return true;
    }

}
  1. 前端页面在templates下面新建目录index,创建页面list.ftl,upload.ftl,fileList.ftl.
    list.ftl:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <head>
        <meta charset="utf-8">
        <title>信息列表</title>

        <meta name="viewport" content="width=device-width, initial-scale=1.0">

        <!-- basic styles -->

        <link href="/assets/css/bootstrap.min.css" rel="stylesheet">
        <link rel="stylesheet" href="/assets/css/font-awesome.min.css">

        <!--[if IE 7]>
        <link rel="stylesheet" href="/assets/css/font-awesome-ie7.min.css"/>
        <![endif]-->

        <!-- page specific plugin styles -->
        <link rel="stylesheet" href="/assets/css/dropzone.css">
        <!-- fonts -->

        <link rel="stylesheet" href="http://fonts.googleapis.com/css?family=Open+Sans:400,300">

        <!-- ace styles -->

        <link rel="stylesheet" href="/assets/css/ace.min.css">
        <link rel="stylesheet" href="/assets/css/ace-rtl.min.css">
        <link rel="stylesheet" href="/assets/css/ace-skins.min.css">


        <!--[if lte IE 8]>
        <link rel="stylesheet" href="/assets/css/ace-ie.min.css"/>
        <![endif]-->

        <!-- inline styles related to this page -->

        <!-- ace settings handler -->

        <script src="/assets/js/jquery-2.0.3.min.js"></script>
        <script src="/assets/js/ace-extra.min.js"></script>

        <script src="/assets/layer/layer.js"></script>

        <!--[if lt IE 9]>
        <script src="/assets/js/html5shiv.js"></script>
        <script src="/assets/js/respond.min.js"></script>

        <![endif]-->


    </head>

</head>
<body>

<div class="page-header">
    <h1>
        信息列表

    </h1>

</div>
<div class="row">
    <div class="col-xs-2">

    </div>
    <div class="col-xs-4">
               <button class="btn btn-block btn-danger" onclick="importFile()">导入Excel数据</button>
        </div>
</div>


<div class="row">
    <div class="col-xs-12">
        <div class="table-responsive" style="background-color: white">
            <table id="sample-table-1" class="table table-striped table-bordered table-hover">
                <thead>
                <tr>

                    <th>序号</th>
                    <th>厂站名</th>
                    <th class="hidden-480">分支线路名称</th>

                    <th>
                        <i class="icon-time bigger-110 hidden-480"></i>
                        项目名称
                    </th>
                    <th>
                        <i class="icon-time bigger-110 hidden-480"></i>
                        操作
                    </th>

                </tr>
                </thead>

                <tbody>
            <#list powerList as power>

            <tr>
                <td>
                    ${power.序号}
                </td>

                <td>
                    ${power.厂站名}
                </td>
                <td>${power.分支线路名称}</td>
                <td>${power.项目名称}</td>


                <td>
                    <div class="visible-md visible-lg hidden-sm  btn-group">
                        <button class="btn btn-info btn-sm up_btn" onclick="openUpload('${power.序号}')">上传附件</button>
                    </div>
                    <div class="visible-md visible-lg hidden-sm btn-group">
                        <button class="btn btn-sm green" onclick="openFileList('${power.序号}')">查看附件</button>
                    </div>

                </td>
            </tr>
            </#list>

                </tbody>
            </table>
        </div><!-- /.table-responsive -->
    </div>
</div>
<div id="dropzonediv" style="display: none">


    <div >
        <div  id="dropzone"  class="dropzone dz-clickable">

            <div class="dz-default dz-message">
                <span>
                    <span class="bigger-150 bolder">
                        <i class="icon-caret-right red"></i>
                       拖拽文件到此处
                    </span>

                    <span class="smaller-80 grey">(或点击此处上传文件)</span>
                    <br>
                    <i class="upload-icon icon-cloud-upload blue icon-3x"></i>
                </span>
            </div>
        </div>
    </div>
</div>
</body>

<script src="/assets/js/typeahead-bs2.min.js"></script>

<!-- page specific plugin scripts -->

<script src="/assets/js/dropzone.min.js"></script>

<!-- ace scripts -->

<script src="/assets/js/ace-elements.min.js"></script>

<script type="text/javascript">
    function openUpload(a) {
        layer.open({
            type: 2,
            title: '文件上传',
            shadeClose: true,
            shade: 0.8,
            area: ['800px', '420px'],
            content: '/index/upload_html?row_id='+a //iframe的url
        });

    }


    
    function openFileList(info_id) {


            layer.open({
                type: 2,
                title: '文件上传',
                shadeClose: true,
                shade: 0.8,
                area: ['800px', '420px'],
                content: '/index/fileList?info_id='+info_id //iframe的url
            });




    }



  


</script>
</html>

upload.ftl:

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <head>
        <meta charset="utf-8">
        <title>信息列表</title>

        <meta name="viewport" content="width=device-width, initial-scale=1.0">

        <!-- basic styles -->

        <link href="/assets/css/bootstrap.min.css" rel="stylesheet">
        <link rel="stylesheet" href="/assets/css/font-awesome.min.css">

        <!--[if IE 7]>
        <link rel="stylesheet" href="/assets/css/font-awesome-ie7.min.css"/>
        <![endif]-->

        <!-- page specific plugin styles -->
        <link rel="stylesheet" href="/assets/css/dropzone.css">
        <!-- fonts -->

        <link rel="stylesheet" href="http://fonts.googleapis.com/css?family=Open+Sans:400,300">

        <!-- ace styles -->

        <link rel="stylesheet" href="/assets/css/ace.min.css">
        <link rel="stylesheet" href="/assets/css/ace-rtl.min.css">
        <link rel="stylesheet" href="/assets/css/ace-skins.min.css">


        <!--[if lte IE 8]>
        <link rel="stylesheet" href="/assets/css/ace-ie.min.css"/>
        <![endif]-->

        <!-- inline styles related to this page -->

        <!-- ace settings handler -->

        <script src="/assets/js/jquery-2.0.3.min.js"></script>
        <script src="/assets/js/ace-extra.min.js"></script>

        <script src="/assets/layer/layer.js"></script>

        <!--[if lt IE 9]>
        <script src="/assets/js/html5shiv.js"></script>
        <script src="/assets/js/respond.min.js"></script>

        <![endif]-->


    </head>

</head>
<body>


<div id="dropzonediv">


    <div >
        <div  id="dropzone"  class="dropzone dz-clickable">

            <div class="dz-default dz-message">
                <span>
                    <span class="bigger-150 bolder">
                        <i class="icon-caret-right red"></i>
                       拖拽文件到此处
                    </span>

                    <span class="smaller-80 grey">(或点击此处上传文件)</span>
                    <br>
                    <i class="upload-icon icon-cloud-upload blue icon-3x"></i>
                </span>
            </div>
        </div>
    </div>


</div>

</body>

<script src="/assets/js/typeahead-bs2.min.js"></script>

<!-- page specific plugin scripts -->

<script src="/assets/js/dropzone.min.js"></script>

<!-- ace scripts -->

<script src="/assets/js/ace-elements.min.js"></script>

<script type="text/javascript">
    //获取url路径参数
    function GetQueryString(id) {
        var reg = new RegExp("(^|&)"+ id +"=([^&]*)(&|$)");
        var r = window.location.search.substr(1).match(reg);
        if(r!=null)return  unescape(r[2]);return null;
    }


    var file_id=GetQueryString("row_id");





    Dropzone.autoDiscover = false;
        $("#dropzone").dropzone({
            url: "/index/upload?file_id="+file_id,
            dictDefaultMessage: '<span class="bigger-150 bolder"><i class="icon-caret-right red"></i> 拖动文件至该处</span><span class="smaller-80 grey">(或点击此处)</span> <br /><i class="upload-icon icon-cloud-upload blue icon-3x"></i>',
            dictResponseError: '网络错误请稍后重试!',
            parallelUploads: 5,//有多少文件将上载到并行处理
            maxFileSize: 3072,//以MB为单位[上传文件的大小限制]
            autoProcessQueue: true,//关闭自动上传功能,默认会true会自动上传,也就是添加一张图片向服务器发送一次请求
            addRemoveLinks:true,//在每个预览文件下面添加一个remove[删除]或者cancel[取消](如果文件已经上传)上传文件的链  接
            uploadMultiple: true,//允许上传多文件
            dictCancelUpload: '取消',
            autoDiscover:false,
            dictRemoveFile: '删除',
            dictRemoveLinks: "删除",
            acceptedFiles: ".jpg,JPG,.jpeg,.doc,.docx,.ppt,.pptx,.txt,.avi,.pdf,.mp3,.zip,.mp4",
            dictFallbackMessage: '不好意思,您的浏览器不支持!',//如果浏览器不支持,默认消息将被替换为这个文本。默认为 “Your browser does not support drag'n'drop file uploads.”。
            dictInvalidFileType: '该文件不允许上传',
            previewTemplate: "<div class=\"dz-preview dz-file-preview\">\n  <div class=\"dz-details\">\n    <div class=\"dz-filename\"><span data-dz-name></span></div>\n    <div class=\"dz-size\" data-dz-size></div>\n    <img data-dz-thumbnail />\n  </div>\n  <div class=\"progress progress-small progress-striped active\"><div class=\"progress-bar progress-bar-success\" data-dz-uploadprogress></div></div>\n  <div class=\"dz-success-mark\"><span></span></div>\n  <div class=\"dz-error-mark\"><span></span></div>\n  <div class=\"dz-error-message\"><span data-dz-errormessage></span></div>\n</div>",


            init: function() {

                var _this=this;
                $(".up_btn").click(function () {
                    _this.removeAllFiles();
                });



                this.on("success", function (file) {


                });
                this.on("removedfile", function (file) {
                    //删除文件

                    console.log("File " + file.name + "removed");
                });

            }


        });








</script>
</html>

fileList.ftl:

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <head>
        <meta charset="utf-8">
        <title>信息列表</title>

        <meta name="viewport" content="width=device-width, initial-scale=1.0">

        <!-- basic styles -->

        <link href="/assets/css/bootstrap.min.css" rel="stylesheet">
        <link rel="stylesheet" href="/assets/css/font-awesome.min.css">

        <!--[if IE 7]>
        <link rel="stylesheet" href="/assets/css/font-awesome-ie7.min.css"/>
        <![endif]-->

        <!-- page specific plugin styles -->
        <link rel="stylesheet" href="/assets/css/dropzone.css">
        <!-- fonts -->

        <link rel="stylesheet" href="http://fonts.googleapis.com/css?family=Open+Sans:400,300">

        <!-- ace styles -->

        <link rel="stylesheet" href="/assets/css/ace.min.css">
        <link rel="stylesheet" href="/assets/css/ace-rtl.min.css">
        <link rel="stylesheet" href="/assets/css/ace-skins.min.css">


        <!--[if lte IE 8]>
        <link rel="stylesheet" href="/assets/css/ace-ie.min.css"/>
        <![endif]-->

        <!-- inline styles related to this page -->

        <!-- ace settings handler -->

        <script src="/assets/js/jquery-2.0.3.min.js"></script>
        <script src="/assets/js/ace-extra.min.js"></script>

        <script src="/assets/layer/layer.js"></script>

        <!--[if lt IE 9]>
        <script src="/assets/js/html5shiv.js"></script>
        <script src="/assets/js/respond.min.js"></script>

        <![endif]-->


    </head>

</head>
<body>
<div class="page-header">
    <h1>
        附件列表

    </h1>
</div>
<div class="row">
    <div class="col-xs-12">
        <div class="table-responsive" style="background-color: white">
            <table id="fileList" class="table table-striped table-bordered table-hover" >
                <thead>
                <tr>

                    <th>序号</th>
                    <th>文件名称</th>
                    <th class="hidden-480">上传时间</th>


                    <th>
                        <i class="icon-time bigger-110 hidden-480"></i>
                        操作
                    </th>

                </tr>
                </thead>

                <tbody>
            <#list fileList as file>

            <tr>
                <td>
                    ${file.id}
                </td>

                <td>
                    ${file.fileName}
                </td>
                <td>
                    ${file.createTime}
                </td>

                <td>
                        <a class="btn btn-xs btn-success" href="/index/downloadFile?file_id=${file.id}">
                            <i class="icon-download bigger-120"></i>
                        </a>
                        <button class="btn btn-xs btn-danger" onclick="delFile('${file.id}')">
                            <i class="icon-trash bigger-120"></i>
                        </button>

                </td>
            </tr>
            </#list>

                </tbody>
            </table>
        </div><!-- /.table-responsive -->
    </div>
</div>
</body>

<script src="/assets/js/typeahead-bs2.min.js"></script>

<script src="/assets/js/dropzone.min.js"></script>
<script src="/assets/js/ace-elements.min.js"></script>
<script type="text/javascript">
    function delFile(file_id) {
        layer.confirm('确认删除?', {
            btn: ['确认','取消'] //按钮
        }, function(){
            $.ajax({
                url :  "deleteFile",
                type : 'POST',
                data : {
                    "file_id" :file_id
                },
                success : function(data) {
                    window.location.reload();
                },
                error : function() {
                    alert("网络错误请稍后重试!");
                }
            });
        });
    }

</script>
</html>
  1. 代码基本就是这些,实体类和相关的service非常简单是实体和基本的增删查改代码。
  2. 整个流程到这里就完毕了,轻松实现上传和下载,感兴趣的伙伴可以在下方评论或者私信我。