使用plupload做一个类似qq邮箱附件上传的效果

时间:2021-12-25 06:39:06

公司项目中使用的框架是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>  

效果:

使用plupload做一个类似qq邮箱附件上传的效果