公司项目中使用的框架是springmvc+hibernate+spring,目前需要做一个类似qq邮箱附件上传的功能,暂时只是上传小类型的附件
处理过程和解决方案都需要添加附件,处理过程和解决方案都可以添加多个附件,也可一个都不添加
以其中一个为例:(文件保存到了数据库中),有关plupload的内容可参考:http://www.360doc.com/content/14/0714/03/552866_394228686.shtml
首先是po
package cn.com.plupload.po; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.Table; import org.hibernate.annotations.GenericGenerator; /** * 附件 和处理过程多对一 * @author GoodLuck * */ @Entity @Table(name="annex",catalog="itac") public class Annex { @Id @Column(name="id") @GenericGenerator(name="generator",strategy="increment") private Long id; /** * 上传文件名称 */ @Column(name="realName") private String realName; /** * 上传文件内容 */ @Column(name="fileContent") private byte[] fileContent; /** * 处理人id,本例中可以忽略 */ @Column(name="handId") private Long handId; /** * 客户id */ @Column(name="customerId") private Long customerId; /** * 外键列,此外键可能对应处理过程表,也可能对应的事解决方案表 */ @Column(name="foreign_id") private Long foreignId; //getter and setter }
dao层
package cn.com.plupload.dao; import java.util.List; import javax.annotation.Resource; import org.hibernate.SessionFactory; import org.springframework.orm.hibernate4.support.HibernateDaoSupport; import org.springframework.stereotype.Repository; import cn.com.plupload.po.Annex; /** * 方法很简单,注释就忽略掉了 * @author GoodLuck * */ @Repository public class AnnexDao extends HibernateDaoSupport{ @Resource public void set(SessionFactory sessionFactory){ this.setSessionFactory(sessionFactory); } public Long insertAnnex(Annex annex){ return (Long) this.getHibernateTemplate().save(annex); } public Long getMaxId(){ List l = this.getHibernateTemplate().find("select max(id) from Annex"); ); } public void deleteAnnex(Annex annex){ this.getHibernateTemplate().delete(annex); } }
service层
package cn.com.plupload.service; import javax.annotation.Resource; import javax.transaction.Transactional; import org.springframework.stereotype.Service; import cn.com.plupload.dao.AnnexDao; import cn.com.plupload.po.Annex; /** * 注释忽略 * @author GoodLuck * */ @Service public class AnnexService { @Resource private AnnexDao annexDao; @Transactional public Long saveAnnex(Annex annex){ return this.annexDao.insertAnnex(annex); } @Transactional public Long getMaxId(){ return this.annexDao.getMaxId(); } @Transactional public void deleteAnnex(Annex annex){ this.annexDao.deleteAnnex(annex); } }
controller层
package cn.com.plupload.controller; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import javax.annotation.Resource; import javax.servlet.http.HttpServletResponse; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.multipart.MultipartFile; import com.google.gson.Gson; import cn.com.plupload.po.Annex; import cn.com.plupload.service.AnnexService; @Controller public class AnnexController { @Resource private AnnexService annexService; /** * 跳转到上传文件页面 * @return */ @RequestMapping("toUploadPage") public String toUploadPage(){ return "upload/upload"; } @RequestMapping("doTest") public String doTest(){ System.out.println("in"); return null; } @RequestMapping("uploadAnnex") public void upload(@RequestParam("file") MultipartFile file, String annexStr, HttpServletResponse response) { /** * 如果annexStr为null,则设置为"" */ annexStr=annexStr==null?"":annexStr; StringBuffer sb = new StringBuffer(); InputStream is; try { Long maxId = this.annexService.getMaxId(); maxId=maxId==:maxId; is = file.getInputStream(); byte[] buffer = this.inputStrean2ByteArr(is); Annex annex = new Annex(); annex.setId(maxId+); annex.setCustomerId(1L); annex.setFileContent(buffer); annex.setHandId(1L); annex.setRealName(file.getOriginalFilename()); Long annexId = this.annexService.saveAnnex(annex); /** * 将当前的附件的id和name以id*name的形式发送到前台,如果有多个附件,则最后的形式就是|id*name}id*name格式 * 第一个|可以在前台做一下处理,则,后台接收的时候,就不用再去截取去掉第一个| */ sb.append(annexStr + "|" + annexId + "*" + file.getOriginalFilename()); response.getWriter().write(sb.toString()); } catch (IOException e) { e.printStackTrace(); } } private byte[] inputStrean2ByteArr(InputStream inStream) throws IOException { ByteArrayOutputStream swapStream = new ByteArrayOutputStream(); ]; ; , )) > ) { swapStream.write(buff, , rc); } byte[] in2b = swapStream.toByteArray(); return in2b; } @ResponseBody @RequestMapping("deleteAnnex") public String deleteAnnex(Long annexId){ Gson gson = new Gson(); try{ Annex annex = new Annex(); annex.setId(annexId); this.annexService.deleteAnnex(annex); return gson.toJson("success"); }catch(Exception e){ return gson.toJson("fail"); } } }
页面
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%> <!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="text/html; charset=UTF-8"> <title>Insert title here</title> <script type="text/javascript" src="${pageContext.request.contextPath }/resources/js/jquery-1.11.1.js"></script> <script type="text/javascript" src="${pageContext.request.contextPath }/resources/js/plupload.full.min.js"></script> </head> <body style="font: 13px Verdana; background: #eee; color: #333"> <a id="pickfiles" href="javascript:;">[选择文件]</a> <div id="container"> </div> <br /> <div id="filelist">Your browser doesn't have Flash, Silverlight or HTML5 support.</div> <br /> <pre id="console"></pre> <form> <div id="hiddenStr"></div> </form> <div id="zifuchuan"></div> <script type="text/javascript"> var idName = ""; var uploader = new plupload.Uploader( { runtimes : 'html5,flash,silverlight,html4', browse_button : 'pickfiles', // you can pass in id... container : document.getElementById('container'), // ... or DOM Element itself url : '<c:url value="/uploadAnnex"/>', flash_swf_url : '<c:url value="/resources/other/Moxie.swf"/>', silverlight_xap_url : '<c:url value="/resources/other/Moxie.xap"/>', filters : { max_file_size : '10mb', mime_types : [ { title : "Image files", extensions : "jpg,gif,png" }, { title : "Zip files", extensions : "zip,rar" } ] }, init : { PostInit : function() { document.getElementById('filelist').innerHTML = ''; }, FilesAdded : function(up, files) {//参数files为列队中的所有文件 //本例中,当添加了文件,就会自动上传,不需要点击额外的按钮,文件完成之后会生成一个input,旧的input就是去了作用 //留着也是不合适的,所以需要在这里删除,当文件上传结束之后,最后生成一个总的字符串,放入到input中,用于传递到后台, //保存到处理过程或者解决方案中,格式为id*name|id*name.... $("#annexStr").remove(); plupload.each(files,function(file) { //file为列队中的单个文件 document.getElementById('filelist').innerHTML += '<div id="' + file.id + '">' + file.name + ' (' + plupload.formatSize(file.size) + ') <b></b><a href="javascript:void(0)">删除</a></div>'; }); //调用start开始上传 uploader.start(); return true; }, //当一个文件上传成功之后会调用这个方法, FileUploaded :function(up,file,ret){ /** 参数up为当前上传组件实例,参数file为当前上传到数据库的文件,ret中的response为返回的数据 file内属性有 file{ id:文件编号,包含当前文件名称的一个div的id,很长的字符串,如o_19pemdjmq1ovsoqc1cb11m7ua2ts loaded:已经上传多少字节 name:文件名 percent:上传进度 size:文件大小, status:四中,QUEUED,UPLOADING,FAILED,DONE } */ //将所有在数据库中保存过得文件id和name存储在idName中 idName = idName+ret.response; var currentIdName = ""; //获取当前这个上传文件的idName,并且截取成id*Name格式,如果第一个字符不是"|",则就不需要在进行解决,如果是|则截取 )=="|"){ //去掉第一个|进行保存 currentIdName=ret.response.substring(); }//获取当前文件保存后的id var sqareIndex = currentIdName.indexOf("*"); ,sqareIndex); //动态给每个删除连接添加onclick事件,并且把当前的文件保存后id传过去 $("#"+file.id+" a:first").attr("onclick","deleteAnnex("+currentId+")"); //给每个删除超链接添加上id,该id值为当前文件保存到数据库后的id,即主键 $("#"+file.id+" a:first").attr("id","id"+currentId); }, UploadProgress : function(up, file) { document.getElementById(file.id) .getElementsByTagName(].innerHTML = '<span>' + file.percent + "%</span>"; }, Error : function(up, err) { document.getElementById('console').innerHTML += "\nError #" + err.code + ": " + err.message; }, UploadComplete :function(up,files){ //这个也是为了测试,可以不用 $("#zifuchuan").html(idName); //判断第一个字符是否是| )=="|"){ //去掉第一个|进行保存 idName=idName.substring(); } //此时保存的字符串在后台就不需要在进行截取了 $("#hiddenStr").append("<input id='annexStr' type='text' value="+idName+"></input>"); } } }); //初始化上传组件 uploader.init(); //需要更改隐藏域的值 function deleteAnnex(annexId){ //获取当前超链接的父对象 var $currentHype = $("#id"+annexId).parent(); $.ajax({ type:'post', url:'<c:url value="/deleteAnnex"/>', data:{ annexId:annexId }, success:function(ret){ if(ret=="success"){ $currentHype.remove(); //确保隐藏狂即id为annexStr的隐藏狂内的值是正确的 var idNameStr = $("#annexStr").val(); //用于保存删除后的字符串 var after_delete_idNameStr = ""; )=="|"){ idNameStr=idNameStr.substring(); } //存在| ){ var idNameArr = idNameStr.split("|"); ;i<idNameArr.length;i++){ ){ //包含* ]; if(id!=annexId){ after_delete_idNameStr = after_delete_idNameStr+idNameArr[i]+"|"; } } } } //如果已经没有|,说明只有一个id*name存在了,而此时删除的也正是这个id*name,此时 //after_delete_idNameStr的值为"",删除掉最后一个id*name之后,id为annexStr的隐藏狂的值也为空了 //所以此时只需要下面这行代码就ok //如果从最后一个向前删除,可能最后一个字符是|,需要去掉 )=="|"){ after_delete_idNameStr = after_delete_idNameStr.substring(,after_delete_idNameStr.length-); } $("#annexStr").attr("value",after_delete_idNameStr); //此时idName的值也应该是删除后的值 idName = after_delete_idNameStr; } }, dataType:'json' }) } </script> </body> </html>
web.xml
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" id="WebApp_ID" version="3.1"> <display-name>plupload</display-name> <filter> <filter-name>characterEncoding</filter-name> <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class> <init-param> <param-name>encoding</param-name> <param-value>utf-</param-value> </init-param> </filter> <filter-mapping> <filter-name>characterEncoding</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <servlet> <servlet-name>springServlet</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:action-servlet.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> <async-supported>true</async-supported> </servlet> <servlet-mapping> <servlet-name>springServlet</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> <welcome-file-list> <welcome-file>index.html</welcome-file> <welcome-file>index.htm</welcome-file> <welcome-file>index.jsp</welcome-file> <welcome-file>default.html</welcome-file> <welcome-file>default.htm</welcome-file> <welcome-file>default.jsp</welcome-file> </welcome-file-list> </web-app>
spring+springmvc配置文件
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd "> <!-- DispatcherServlet Context: defines this servlet's request-processing infrastructure --> <!-- Enables the Spring MVC @Controller programming model --> <!-- springmvc注解,必须配置 --> <context:component-scan base-package="com.h3c.zgc" /> <mvc:annotation-driven /> <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close"> <property name="driverClass"> <value>com.mysql.jdbc.Driver</value> </property> <property name="jdbcUrl"> <value>jdbc:mysql://localhost:3306/itac</value> </property> <property name="user"> <value>root</value> </property> <property name="password"> <value>root</value> </property> <!--连接池中保留的最小连接数。 --> <property name=" /> <!--连接池中保留的最大连接数。Default: --> <property name=" /> <!--最大空闲时间,1800秒内未使用则连接被丢弃。若为0则永不丢弃。Default: --> <property name=" /> <!--当连接池中的连接耗尽的时候c3p0一次同时获取的连接数。Default: --> <property name=" /> <property name=" /> <property name=" /> <!--每60秒检查所有连接池中的空闲连接。Default: --> <property name=" /> <!--定义在从数据库获取新连接失败后重复尝试的次数。Default: --> <property name=" /> <property name="breakAfterAcquireFailure" value="true" /> <property name="testConnectionOnCheckout" value="false" /> </bean> <bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean"> <property name="dataSource" ref="dataSource" /> <property name="packagesToScan" value="com.h3c.zgc" /> <property name="hibernateProperties"> <value> hibernate.dialect=org.hibernate.dialect.MySQL5Dialect hibernate.hbm2ddl.auto=update hibernate.connection.autocommit=true hibernate.show_sql=true hibernate.format_sql=true hibernate.cache.use_second_level_cache=true hibernate.cache.use_query_cache=false hibernate.current_session_context_class=org.springframework.orm.hibernate4.SpringSessionContext </value> </property> </bean> <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"> <property name="></property> <!-- byte --> <property name="defaultEncoding" value="utf-8" /> </bean> <bean id="txManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager"> <property name="sessionFactory" ref="sessionFactory"></property> <property name="nestedTransactionAllowed" value="true" /> </bean> <tx:annotation-driven transaction-manager="txManager" proxy-target-class="true" mode="proxy" /> <!-- 静态资源处理配置 --> <mvc:resources mapping="/resources/**" location="/resources/" /> <!-- Resolves views selected for rendering by @Controllers to .jsp resources in the /WEB-INF/views directory --> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix" value="/views/" /> <property name="suffix" value=".jsp" /> </bean> </beans>
效果: