Java web实现图片上传服务器、同步保存数据库以及如何在前端页面展示教程
- 一、上传
- 二、后端代码处理
- 三、数据库读取图片在前端页面展示(重点)
大概流程:
2.上传请求发起后,java代码的处理:你是要将上传的图片只保存在服务器还是只保存在数据库还是说两者都采取。上传到服务器很简单,保存到数据库也很简单,但是此处需要考虑业务,图片保存在数据库时采用哪种保存方式(本博文业务来自于项目,因为图片数量巨多,故在数据库是通过保存图片的路径实现的,并非二进制流);
3.图片保存在数据库后,在前段页面的回显功能。
一、上传
目前上传图片的插件很多,我在做这个需求的时候也用到了好几款图片上传插件,没有一个完美的插件,多多少少都有问题,必要的时候需要改作者的源码。我选择的是zyupload插件。下面说说使用教程。
1.点击此处下载zyupload,网上也有一大堆,大家可以随便去找都可以。 2.使用方法:
下载插件压缩包后,解压。把css文件zyupload-1.0.0.css和js文件zyupload-1.0.0.js以及图标文件夹images引入到你的项目,images文件夹跟zyupload.css文件在同一个目录下。如图:
3.创建一个上传组件对象:
<input type="button"onclick="javascrtpt:window.location.href='<%=path%>/uploadPhotoIndex'" value="上传照片">
这行代码就是创建了一个按钮,点击后打开上传图片的页面。因为我用的是SSM加shiro框架,所有页面都是在Controller中统一处理的,(我的处理代码如下)这里显得有点啰嗦,大家可以直接转xxx.jsp就可以了。
/**
* 上传图片主页
*/
@RequestMapping(value = "/uploadPhotoIndex")
public String uploadPhotoIndex() {
return "bs/uploadPhoto";
}
4.上传页面代码:
<%@ page language="java" contentType="text/html; charset=utf-8"
pageEncoding="utf-8"%>
<%
String path = request.getContextPath();
%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="multipart/form-data; charset=utf-8">
<title>Insert title here</title>
<link rel="stylesheet" type="text/css"href="<%=path%>/css/bootstrap.min.css">
<link rel="stylesheet" type="text/css" href="<%=path%>/css/zyupload-1.0.0.css" />
<script type="text/javascript" src="<%=path%>/js/jquery-3.1.1.min.js"></script>
<script type="text/javascript" src="<%=path%>/js/zyupload-1.0.0.js"></script>
</head>
<body>
<div id="demo" class="demo"></div>
</body>
<script type="text/javascript">
var path='<%=path%>';
$(function(){
// 初始化插件
$("#demo").zyUpload({
width : "100%", // 宽度
height : "400px", // 宽度
itemWidth : "150px", // 文件项的宽度
itemHeight : "210px", // 文件项的高度
url : path+"/test/uploadPhoto", // 上传文件的路径
fileType : ["jpg","png","PNG","JPG"],// 上传文件的类型
multiple : true, // 是否可以多个文件上传
dragDrop : true, // 是否可以拖动上传文件
del : true, // 是否可以删除文件
finishDel : false, // 是否在上传文件完成后删除预览
/* 外部获得的回调接口 */
onSelect: function(files, allFiles){ // 选择文件的回调方法
console.info("当前选择了以下文件:");
console.info(files);
console.info("之前没上传的文件:");
console.info(allFiles);
},
onDelete: function(file, surplusFiles){ // 删除一个文件的回调方法
console.info("当前删除了此文件:");
console.info(file);
console.info("当前剩余的文件:");
console.info(surplusFiles);
},
onSuccess: function(file,response){ // 文件上传成功的回调方法
console.info("此文件上传成功:");
console.info(file);
},
onFailure: function(file){ // 文件上传失败的回调方法
console.info("此文件上传失败:");
console.info(file);
},
onComplete: function(responseInfo){ // 上传完成的回调方法
console.info("文件上传完成");
console.info(responseInfo);
myConfirm('提示', '所有照片上传成功!是否将已上传的照片更新到数据库?', insertPhoto, function(){});
}
});
});
function insertPhoto(){
$.ajax({
type : 'post',
contentType : 'application/json',
url : path + '/test/insertPhoto',
processData : false,
dataType : 'json',
data : JSON.stringify({}),
success : function(data) {
if (data.requestResult.success) {
myAlert("提示", data.requestResult.successMsg);
} else {
myAlert("提示", data.requestResult.errorMsg);
}
},
error : function() {
myAlert("警告", "请求失败");
}
});
}
</script>
</html>
body里面就一句,创建一个实例对象就可以,然后通过$("#demo").zyUpload({})去初始化它。下面的五个方法特别重要,可以借用来扩展你的业务。我是在图片全部上传到服务器后调用了onComplete: function(responseInfo){}这个方法,去询问是否将上传的图片更新到数据库,为什么这么做,因为保存图片到数据库的时候如果throw异常不好处理,可能后端保存失败了,但是前端提示成功了,或者压根就不提示。为了方便处理异常,我还是选择了通过ajax去保存图片路径到数据库。
5.前端上传效果:
二、后端代码处理
1.先贴上传到服务器代码:
@RequestMapping(value = "test/uploadPhoto")
public String uploadPhoto(@RequestParam("file") MultipartFile[] file, HttpServletRequest request) throws Exception {
String firstPath = "E:\\tempPhotos";// 存入数据库的路径前缀
for (int i = 0; i < file.length; i++) {// 循环存入文件并组合路径
MultipartFile thisfile = file[i];
String fileName = thisfile.getOriginalFilename();// 得到文件名称
File tempFile = new File(firstPath, fileName);
if (fileName != "" && !thisfile.isEmpty()) {
if (!tempFile.getParentFile().exists()) {// 检测是否存在目录
tempFile.getParentFile().mkdirs();
}
thisfile.transferTo(tempFile);// 写入文件
}
}
return "bs/uploadPhoto";//上传成功返回上传图片页面。
}
2.保存图片到数据库代码:
前端通过ajax请求实现插入的,图片上传完成后会调用此请求,上述前段代码已提及,上传和保存分开是为了便于处理异常,ajax实现的异步刷新感觉体验好
@RequestMapping(value = "test/insertPhoto")
public @ResponseBody Response insertPhoto1()throws Exception {
Response response = new Response();//这个是我用来消息处理的工具类。你们可以用自己的替换
String temppath = "E:\\tempPhotos";// 临时上传的图片目录
String targetpath = "E:\\photos";// 最终备份的图片目录
File tempfile = new File(temppath);
File targetfile = new File(targetpath);
if (!targetfile.getCanonicalFile().exists()) {// 检测是否存在目录
targetfile.getCanonicalFile().mkdirs();
}
String[] fileName = tempfile.list();
Photos = null;声明一个图片实体
List<Photos> photos = new ArrayList<>();
try {
for (int i = 0; i < tempfile.list().length; i++) {
photos = new Photos();
//因为图片命名是编号_姓名的方式,所以这里做了一个处理,
int index = fileName[i].indexOf("_");
photos .setBh(fileName[i].substring(0, index));
//这里是关键,存入图片的路径。比如图片命名是:123_张三.jpg
存进去就是 /photos/123_张三 这样的,采用相对路径的办法,实际地址是E:\photos\tempPhotos\123_张三.jpg,关键就在于图片在前端展示的时候有个大坑,稍后会讲到。
photos.setPhoto("/photos/" + fileName[i]);
photolist.add(photos );
}
//通过mybatis框架通过list插入图片数据。效率比使用for循环好多了
int m = photosService.updatePhotoList(photolist);
if (m > 0) {
response.getRequestResult().setSuccess(true);
response.getRequestResult().setSuccessMsg("照片更新到数据库成功!");
}else {
response.getRequestResult().setSuccess(false);
response.getRequestResult().setErrorMsg("<font color=\"red\">照片更新到数据库失败!<br/>请检查照片名称是否正确或者上传时照片类型是否选对!</font>");
}
} catch (java.lang.StringIndexOutOfBoundsException e) {
response.getRequestResult().setSuccess(false);
response.getRequestResult().setErrorMsg("照片更新到数据库失败!<br/>失败原因:照片名称分割时出现字符串下标越界异常!<br/>解决方案:检查上传图片的命名是否符合规范要求!<br/>");
}catch (Exception e) {
response.getRequestResult().setSuccess(false);
response.getRequestResult().setErrorMsg(e.getMessage().substring(e.getMessage().lastIndexOf(":")));
}
// 插入图片到数据库以后先将临时上传的图片备份(复制整个临时图片文件夹到photos目录下)
org.apache.commons.io.FileUtils.copyDirectoryToDirectory(tempfile, targetfile);
// 删除临时图片文件夹
org.apache.commons.io.FileUtils.deleteDirectory(tempfile);
return response;
三、数据库读取图片在前端页面展示(重点)
1.采用MySQL数据库进行存储,存储图片的方式是通过保存图片的相对路径(非blob),如图:
2.前端绑定:
既然数据库存储的是路径,那么就直接把图片路经查询出来放到图片标签img里面不就美滋滋了!(这里会打脸,尤其新手)。
前端我用的knockout js,绑定数据就是通过data-bind方式,大家用自己熟悉的就可以。上面一行代码就等同于:
大家看看src="",是不是跟数据库存的值是一样的。但是显示效果是什么样子呢?看下图:
图片找不到?找不到?哪里的问题?原因在于Tomcat服务器只能扫描到项目下的资源,很明显咱们图片上传后存放的真实路径在这里:
这个不是项目的路径,并且咱们用的是相对路径存放的照片在数据库。解决办法就是通过Tomcat添加虚拟路径来解决。看下面:
1.双击你的Tomcat服务器打开。
2.选择Modules ,再在右上角选择Add External Web Module,出现如图小弹出框。在第一个输入框填入存放图片的物理路径,第二个输入框填映射地址(比如数据库你存放的格式是/photos/abc.png,那么这里就是/photos),为了更清楚解释,请下图看关系:
3.再上一步的操作基础上,还需要一一步操作就可以完成了,打开发布该项目的Tomcat,找到conf下的server.xml打开,在标签内加上一句
到此,一切结束了。
4.测试结果:一切正常了
5,由于是项目里面的一个功能,所以源码没办法分享,大家要是哪里遇到困难,请留言,一起讨论,或者觉得本文有不足之处,望各路大神指出。感激不尽!