SpringBoot结合Freemaker+ace前端界面轻松实现文件的上传和下载
本文章旨在帮助首次使用SpringBoot的新手开发人员进行文件的上传和下载基本的操作,结合ACE好看的页面风格实现上传和下载,简单快捷。本人使用的是Idea工具。
- 创建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的插件,弹出层,大家可以百度一下挺好用的一个。
(6)这些基本工作完毕以后即可开始写代码,先加载第一个页面。
- 在JAVA目录中创建相关的包和JAVA类
- 我们的业务是这样的:现在有一个数据列表,每一条数据后面都附有一个自己的附件上传下载和管理,附件同时也要进行管理,因此我们这里有两个数据模型,一个是附件管理,一个是信息管理,都需要保存数据库。效果图是这样的:
- 项目结构图
- 废话不多说直接上代码,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;
}
}
- 前端页面在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>
- 代码基本就是这些,实体类和相关的service非常简单是实体和基本的增删查改代码。
- 整个流程到这里就完毕了,轻松实现上传和下载,感兴趣的伙伴可以在下方评论或者私信我。