J2EE使用hibernate存储和显示BLOB图片(从页面到数据库)

来源:互联网 发布:颜值测试软件 编辑:程序博客网 时间:2024/06/06 11:39

由于项目需求要对项目中的图片进行数据库存储,这就意味着我们以前常用的通过servlet单独进行图片上传的方法就不太方便了(虽然不是说不可以,我们可以先提交到服务器生成文件,再返回文件名,然后提交数据到服务器的时候通过文件名获取文件再进行存储,但是这tm也太麻烦了吧)。于是就想有没有什么办法能直接从form表单里就对图片进行上传呢?心动不如行动,马上就干起来。

先交代一下背景,项目框架使用的是velocity+spring+hibernate,数据库使用的MySQL,这次进行的实验是在之前有同学完成的后台管理模块的基础上进行的。

首先,我们从页面端开始。我们一般情况下提交数据给服务器,都是在页面上的form表单里添加input元素,并设置name属性,在提交之后再服务器端通过request.getParameter()方法就可以获取数据。然而当我们在我们的form表单中加了一个<input type="file" name="imgurl" />的时候,却突然发现在服务器端String imgUrl = map.get("imgurl");获得的值为null,这就奇怪了为啥他就是要搞特殊呢?仔细一看发现,原来是因为form表单不同的数据格式的原因,简单引用网上的资料可知:

在Form元素的语法中,通过EncType表明提交数据的格式 
用 Enctype 属性指定将数据回发到服务器时浏览器使用的编码类型。 
下边是说明: 
application/x-www-form-urlencoded: 窗体数据被编码为名称/值对。这是标准的编码格式。 
multipart/form-data: 窗体数据被编码为一条消息,页上的每个控件对应消息中的一个部分。 
text/plain: 窗体数据以纯文本形式进行编码,其中不含任何控件或格式字符。
 
补充
form的enctype属性为编码方式,常用有两种:application/x-www-form-urlencoded和multipart/form-data,默认为application/x-www-form-urlencoded。 
当action为get时候,浏览器用x-www-form-urlencoded的编码方式把form数据转换成一个字串(name1=value1&name2=value2...),然后把这个字串append到url后面,用?分割,加载这个新的url。 
当action为post时候,浏览器把form数据封装到http body中,然后发送到server。 
如果没有type=file的控件,用默认的application/x-www-form-urlencoded就可以了。 
但是如果有type=file的话,就要用到multipart/form-data了。浏览器会把整个表单以控件为单位分割,并为每个部分加上Content-Disposition(form-data或者file),Content-Type(默认为text/plain),name(控件name)等信息,并加上分割符

至于具体multipart/form-data格式封装的http请求是什么样子的,这个就自己通过浏览器去看看吧,网上资料也很多。如这个讲的还行

于是,马上把form加上属性enctype="multipart/form-data",再次提交一看,顿时蒙逼了,不止是文件属性为null了,连之前的其他几个属性值现在也变成null了,这特么什么情况,仔细一想,对了,刚才资料里看过的,修改了数据的提交格式以后,数据不再是键值对,肯定不能再以同样的方式进行获取了,马上继续找相关内容,网上也有很多的解决办法。大家可以参考,我的解决方式如下:
try {request.setCharacterEncoding("utf-8");} catch (UnsupportedEncodingException e1) {e1.printStackTrace();}        response.setCharacterEncoding("utf-8");        response.setContentType("text/html;charset=utf-8");        Map<String, String> map=new HashMap<>();// 用于存储表单非二进制流部分        Map<String, Object> jsonMap = new HashMap<String, Object>();                boolean isMultipart = ServletFileUpload.isMultipartContent(request);// 检查输入请求是否为multipart表单数据。        if (isMultipart == true) {   // 如果是二进制流形式的表单        Blob bb = null;            FileItemFactory factory = new DiskFileItemFactory();// 通过它来解析请求。执行解析后,所有的表单项目都保存在一个List中。            ServletFileUpload upload = new ServletFileUpload(factory);            List<FileItem> items = null;            try {                items = upload.parseRequest(request);            } catch (FileUploadException e) {                // TODO Auto-generated catch block                e.printStackTrace();            }            Iterator<FileItem> itr = items.iterator();            while (itr.hasNext()) {                FileItem item = (FileItem) itr.next();                if (item.isFormField()) {//检测是否为普通表单 如果是普通表单项目,将其名字与对应的value值放入map中。                    String fieldName = item.getFieldName();                    try {map.put(fieldName, item.getString("UTF-8"));} catch (UnsupportedEncodingException e) {e.printStackTrace();}//获取表单value时确定数据格式,如果不写,有中文的话会乱码                                    } else {   //如果是提交的图片                     bb = Hibernate.createBlob(item.get());                }                            }            String privilegeName = map.get("privilegename");String userNum = map.get("usernum");String funUrl = map.get("funurl");//接下来这部分就是数据的存储啦,这个自己搞定}
按理说,到了这步,我们应该是已经能成功的把图片以BLOB的格式存储到数据库中了(其中hibernate映射那部分就不说了,和其他的类型没什么区别的)!当然,大家的可能也真的是可以成功的了。然而。。。我发现服务器上仍然获取不到数据,这就奇了个怪了,头发抓掉一大把,再看看页面源码,突然发现原来提交部分同学为了解决重复刷新的问题,竟然使用了ajax提交而不是form的submit。。。当时就醉了。那前面这些东西不是白搞了(当然,其实没白搞,知识还是学到了)!然后又去找,通过ajax如何提交multipart/form-data格式的数据,结果还真找到了一大堆,其中,比较感兴趣的就是html5的一个新对象,叫FormData,具体例子在这里。所以,又对提交的页面进行修改:
                                                var formElement = document.getElementById("form_submit");var formData = new FormData(formElement);formData.append("serialnumber", "hello formdata");alert(formData);jQuery.ajax({type : "post",url : "$!request.getContextPath()/MemberPrivilegeAction.do?method=addMemberPrivilege",data : formData,// 你的formidasync : false,contentType : false,processData : false,success : function(data) {if (data.success) {alert("添加成功!");window.location.href = "$!request.getContextPath()/MemberPrivilegeAction.do?method=memberPrivilegeManage";} else {alert(data.msg);}},dataType : 'json'});

再次断点调试,偶也,数据终于成功存到数据库了,工程终于完成了一半。


那么接下来,就是数据的展示了。首先,通过hibernate把数据库中的BOLB类型数据取出来,没什么问题,只要映射文件配置好,和其他的字段是没区别的。但是,找把图片展示到页面上那就有问题了。找了很多种方法,正方偏方试了一遍,最终发现,还是通过servlet来获取比较靠谱,比如这里。那么问题就来了,我的项目里有很多地方都用得到图片,我不可能为每个图片都写个servlet来进行显示,这样显然是很不科学的。于是就想到了通过java 的反射机制来解决这个问题,通过servlet参数传过来要显示的实体和实体的某个属性以及数据的主键id,再到数据库中获取对应的数据进行展示。于是,就有了下面的这个servlet:
/** *  */package com.yykj.web.servlet;import java.io.IOException;import java.sql.Blob;import java.sql.SQLException;import javax.servlet.ServletException;import javax.servlet.ServletOutputStream;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import com.yykj.web.export.Reflection;import com.yykj.web.util.ServiceFactory;/** * @author lixueweikm@gmail.com * @date 2016年7月30日 */public class BlobImgServlet extends HttpServlet {private static final String CONTENT_TYPE = "image/gif ";private static final String SERVICE_PACKAGE = "com.yykj.service.";private static final String MODEL_PACKAGE = "com.yykj.dao.model.";/* * (non-Javadoc) *  * @see * javax.servlet.http.HttpServlet#doGet(javax.servlet.http.HttpServletRequest * , javax.servlet.http.HttpServletResponse) */@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp)throws ServletException, IOException {String entrty = req.getParameter("entrtyName");String attribute = req.getParameter("attributeName");String entrtyid = req.getParameter("entrtyid");entrty = captureName(entrty);Class<?> serviceBean = null;Blob blob = null;try {serviceBean = Class.forName(SERVICE_PACKAGE + entrty + "Service");//这里是通过spring进行service层的依赖注入,获得实体对应的service类Object oservice = ServiceFactory.getService(serviceBean);//这是一个反射的工具类Reflection re = new Reflection();Object omodel = re.invokeMethod(oservice, "get" + entrty + "ById",new String[] { entrtyid });blob = (Blob) re.getProperty(omodel, attribute);} catch (ClassNotFoundException e1) {e1.printStackTrace();} catch (Exception e) {e.printStackTrace();}if (blob != null) {try {int fileSize = (int) blob.length();byte[] ab = blob.getBytes(1, fileSize);resp.setContentType(CONTENT_TYPE);resp.setContentLength(fileSize);ServletOutputStream op = resp.getOutputStream();op.write(ab);op.flush();op.close();} catch (SQLException e) {// TODO Auto-generated catch blocke.printStackTrace();}}}/* * (non-Javadoc) *  * @see * javax.servlet.http.HttpServlet#doPost(javax.servlet.http.HttpServletRequest * , javax.servlet.http.HttpServletResponse) */@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp)throws ServletException, IOException {// TODO Auto-generated method stubthis.doGet(req, resp);}// 首字母大写public static String captureName(String name) {char[] cs = name.toCharArray();if(Character.isLowerCase(cs[0])){cs[0] -= 32;}return String.valueOf(cs);}}

在页面端要显示图片的地方加上<img src="$request.getContextPath()/bimg?entrtyName=MemberPrivilege&attributeName=imgUrl&entrtyid=$!p.privilegeId"/>一看,哎哟喂,果然可以。至此,打完收工。大笑
对了,那反射工具类也gx一下看看:

package com.yykj.web.export;import java.lang.reflect.Field;import java.lang.reflect.Method;public class Reflection {public Object invokeMethod(Object owner, String methodName, Object[] args)throws Exception {Class ownerClass = owner.getClass();Class[] argsClass = new Class[args.length];for (int i = 0, j = args.length; i < j; i++) {if (args[i] != null) {argsClass[i] = args[i].getClass();} else {argsClass[i] = String.class;}}Method method = ownerClass.getMethod(methodName, argsClass);return method.invoke(owner, args);}public Object getProperty(Object owner, String fieldName) throws Exception {Class ownerClass = owner.getClass();Field field = ownerClass.getDeclaredField(fieldName);field.setAccessible(true);Object property = field.get(owner);return property;}public Object getClass(String className) throws InstantiationException,IllegalAccessException, ClassNotFoundException {Object obj = Class.forName(className).newInstance();return obj;}}


0 0
原创粉丝点击