SpringMVC+Ajax文件上传+进度条

Ajax文件上传进度条

参考博客地址

1 准备SSM的Maven环境

任意的ssm环境的项目,非maven的也可,但是注意在SpringMVC的Controller中使用@ResponseBody注解的时候必须引入jackson的依赖包,否则该注解无法使用.

<!-- jackson 版本 -->
<jackson.version>2.5.4</jackson.version>

<!-- springMVC依赖的jackson -->
<dependency>
   <groupId>com.fasterxml.jackson.core</groupId>
   <artifactId>jackson-core</artifactId>
   <version>${jackson.version}</version>
 </dependency>
 <dependency>
   <groupId>com.fasterxml.jackson.core</groupId>
   <artifactId>jackson-databind</artifactId>
   <version>${jackson.version}</version>
 </dependency>
<dependency>
   <groupId>com.fasterxml.jackson.core</groupId>
   <artifactId>jackson-annotations</artifactId>
   <version>${jackson.version}</version>
 </dependency>

2 前端页面

前端页面的重点是ajax文件上传,因为一直无法获得文件的对象,所以在这里费的时间比较长,需要注意js获得文件对象的方式。

引入BootStrap的样式

此处非必须,不是BootStrap也不影响

<!-- 导入css样式 -->
<link href="${pageContext.request.contextPath}/static/bootstrap3/css/bootstrap.min.css" rel="stylesheet" />
<!-- 导入jQuery库 -->
<script type="text/javascript" src="${pageContext.request.contextPath}/static/bootstrap3/jquery/jquery-1.11.3.min.js" ></script>
<!-- 导入js库 -->
<script type="text/javascript" src="${pageContext.request.contextPath}/static/bootstrap3/js/bootstrap.min.js" ></script>

<style type="text/css">
fieldset{padding:.35em .625em .75em;margin:0 2px;border:1px solid silver}
legend{padding:.5em;border:0;width:auto}
</style>

上传表单

上传表单

<fieldset>
<h1>Ajax文件上传进度条</h1>
<!-- 
ajax提交后form表单中如果存在button按钮的话ajax完成之后form表单
还是会自动提交
-->
<form action="${pageContext.request.contextPath}/pd/upload2.action" 
    method="post" id="form1" name="form1" 
    enctype="multipart/form-data" 
    onsubmit="return false;" role="form" class="form-inline">
<input type="file" name="fileObj"  id="fileObj" class="form-group" />
</form>

<!-- 动画进度条 -->
<div id="pgID" style="display:block;width:300px;" >
    <div class="progress progress-striped active" >
        <div id="pdBar" class="progress-bar progress-bar-success" role="progressbar"
             aria-valuenow="60" aria-valuemin="0" aria-valuemax="100"
             style="width:0%;">
            <span id="pdNum" class="sr-only">40% 完成</span>
        </div>
    </div>
</div>
<div>
<button class="btn btn-primary" id="upload" >上传</button>
<button class="btn btn-danger" id="cancel" >取消</button>
</div>
<p>开始时间:<font id="startTime" color="red"></font></p>
<p>现在时间:<font id="currentTime" color="red"></font></p>
<p>已经传输了的时间(s):<font id="time" color="red"></font></p>
<p>传输速度(byte/s):<font id="velocity" color="red"></font></p>
<p>估计总时间:<font id="totalTime" color="red"></font></p>
<p>估计剩余时间:<font id="timeLeft" color="red"></font></p>
<p>上传百分比:<font id="percent" color="red"></font></p>
<p>已完成数:<font id="length" color="red"></font></p>
<p>总长度(M):<font id="totalLength" color="red"></font></p>
</fieldset>

JS代码部分

需要注意的是JS获取文件的时候var fileObj = $("#fileObj")[0].files[0];
必须这样写,否则取不到文件


<script type="text/javascript">
var timeInfo = 0;

$(function(){
    $("#upload").click(function(){
        uploadFile();
        timeInfo = setInterval("getUploadInfo()",1000);
    });

    $("#cancel").click(function(){
        $("#pdBar").css({"width":"0%"});
        clearInterval(timeInfo);
    });

});

//文件上传
function uploadFile(){
    var fileObj = $("#fileObj")[0].files[0];//必须这样写,否则取不到文件
    console.log(fileObj);
    //$('#form1').serialize() 无法序列化二进制文件,这里采用formData上传
    //需要浏览器支持:Chrome 7+、Firefox 4+、IE 10+、Opera 12+、Safari 5+。
     var formData = new FormData(); // FormData 对象
//      formData.append("author", "hooyes"); // 可以增加表单数据
     formData.append("fileObj", fileObj); // 文件对象
     console.log("开始上传..."+formData);
    $.ajax({
        type:"POST",
        url:"${pageContext.request.contextPath}/pd/upload.action",
        data:formData,
        async: true,  
        cache: false, 
        dataType:"json",
        contentType: false,/*必须false才会自动加上正确的Content-Type */
        processData: false,/*必须false才会避开jQuery对 formdata 的默认处理XMLHttpRequest会对 formdata 进行正确的处理*/
        success:function(res){
            console.log(res);
            if(res.tag === true){
                clearInterval(timeInfo);
                getUploadInfo();//修正得到文件上传信息
                setPdWidth(100);//修正文件上传为100%
                alert("上传成功!");
            }else{
                clearInterval(timeInfo);
                alert("上传失败!");
            }
        },
        error:function(){
            clearInterval(timeInfo);
            alert("上传错误!");
        }
    });
}

//打开进度条
function openProgess(){
    /* var modalHeight=$(window).height() / 2 - $('#pgID').height() / 2; 
    $("#pgID").css({"display":"block","margin":modalHeight}); */
    $("#pgID").css({"display":"block","width":"300px"}); 
}

//关闭进度条
function closeProgess(){
    $("#pgID").css({"display":"none","width":"300px"});
    $("#pdBar").css({"width":"0%"});
}

//得到上传文件进度信息
function getUploadInfo(){
    $.ajax({
        url:"${pageContext.request.contextPath}/pd/getInfo.action",
        data:{time:new Date()},
        type:"post",
        dataType:"json",
        cache:false,
        success:function(res){
            if(res.percent == 100){
                clearInterval(timeInfo);
                return;
            }
            console.log(res);
            setPdWidth(res.percent);
            setUploadInfo(res);
        },
        error:function(){
            clearInterval(timeInfo);
            console.log("得到上传文件信息出错!");
        }
    });
}

//设置上传文件进度信息
function setUploadInfo(res){
    $("#startTime").text(res.startTime);
    $("#currentTime").text(res.currentTime);
    $("#time").text(res.time);
    $("#velocity").text(res.velocity);
    $("#totalTime").text(res.totalTime);
    $("#timeLeft").text(res.timeLeft);
    $("#percent").text(res.percent);
    $("#length").text(res.length);
    $("#totalLength").text(res.totalLength);
}

function setPdWidth(percent){
    $("#pdBar").css({"width":percent+"%"});
}

</script>

3 后端代码

Progress实体类

创建一个进度条的实体类,此处省略getter和setter方法

/**
 * 进度条的entity
 * @author BartG
 *
 */
public class Progress {

    private long bytesRead;
    private long contentLength;
    private long items;
    private long startTime = System.currentTimeMillis(); // 开始上传时间,用于计算上传速率

    public Progress() { }

上传文件监听器

我们要获得上传文件的实时详细信息,必须继承org.apache.commons.fileupload.ProgressListener类,获得信息的时候将进度条对象Progress放在该监听器的session对象中。

package com.bart.module.pb.listener;

import javax.servlet.http.HttpSession;
import org.apache.commons.fileupload.ProgressListener;
import org.springframework.stereotype.Component;
import com.bart.module.pb.entity.Progress;

@Component
public class FileUploadProgressListener implements ProgressListener {
    private HttpSession session;
    public void setSession(HttpSession session){
        this.session=session;
        Progress status = new Progress();//保存上传状态
        session.setAttribute("status", status);
    }

    @Override
    public void update(long bytesRead, long contentLength, int items) {
        Progress status = (Progress) session.getAttribute("status");
        status.setBytesRead(bytesRead);//已读取数据长度  
        status.setContentLength(contentLength);//文件总长度  
        status.setItems(items);//正在保存第几个文件  

    }


}
文件解析器

SpringMVC默认有一个文件解析器CommonsMultipartResolver用来解析上传的文件,我们需要重写该类,自己重写的监听器放到org.apache.commons.fileupload.FileUpload中,还需要将session放到自定义的监听器中。

package com.bart.module.pb.resolver;

import java.util.List;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileUpload;
import org.apache.commons.fileupload.FileUploadBase;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.multipart.MaxUploadSizeExceededException;
import org.springframework.web.multipart.MultipartException;
import org.springframework.web.multipart.commons.CommonsMultipartResolver;

import com.bart.module.pb.listener.FileUploadProgressListener;

public class CustomMultipartResolver extends CommonsMultipartResolver {

    // 注入第二步写的FileUploadProgressListener 
    @Autowired
    private FileUploadProgressListener progressListener;

    public void setFileUploadProgressListener(FileUploadProgressListener progressListener) {
        this.progressListener = progressListener;
    }

    @Override
    public MultipartParsingResult parseRequest(HttpServletRequest request) throws MultipartException {
        String encoding = determineEncoding(request);
        FileUpload fileUpload = prepareFileUpload(encoding);
        progressListener.setSession(request.getSession());
        fileUpload.setProgressListener(progressListener);
        try {
            List<FileItem> fileItems = ((ServletFileUpload) fileUpload).parseRequest(request);
            return parseFileItems(fileItems, encoding);
        } catch (FileUploadBase.SizeLimitExceededException ex) {
            throw new MaxUploadSizeExceededException(fileUpload.getSizeMax(), ex);
        } catch (FileUploadException ex) {
            throw new MultipartException("Could not parse multipart servlet request", ex);
        }
    }

}
配置SpringMVC的配置文件解析器

需要配置文件解析器为自定义的

<!-- ====================== 配置自定义的MutilpartResover解析器处理文件上传进度条 ====================== -->
    <bean id="multipartResolver" 
        class="com.bart.module.pb.resolver.CustomMultipartResolver">
        <property name="defaultEncoding" value="UTF-8"></property>
        <property name="maxUploadSize" value="1024000000"></property>
    </bean> 

Controller代码

上传文件
@ResponseBody
    @RequestMapping(value="/upload", method = RequestMethod.POST)
    public Map<String, Object> index(HttpServletRequest request, HttpServletResponse response,
            @RequestParam("fileObj") CommonsMultipartFile file) throws IOException {
        Map<String, Object> result = new HashMap<>();
        if (file.getSize() > 0) {
request.getSession().getServletContext().getRealPath(path);
            String uploadPath = "D:/upload/";
            try {
                FileUtils.copyInputStreamToFile(file.getInputStream(),new File(uploadPath, file.getOriginalFilename()));
                result.put("tag",true);
            } catch (Exception e) {
                result.put("tag",false);
            }
        }
        return result;
    }
得到上传文件详细信息
@ResponseBody
    @RequestMapping("/getInfo")
    public Map<String, Object> getUploadInfo(HttpServletRequest request, HttpServletResponse response){
        Map<String, Object> result = new HashMap<>();
        response.setHeader("Cache-Control", "no-store"); //禁止浏览器缓存  
        response.setHeader("Pragrma", "no-cache");  //禁止浏览器缓存  
        response.setDateHeader("Expires", 0);   //禁止浏览器缓存  

        Progress status = (Progress) request.getSession(true).getAttribute("status");//从session中读取上传信息  
        if(status == null){  
            result.put("error", "没发现上传文件!"); 
            return result;
        }  

        long startTime = status.getStartTime();  //上传开始时间  
        long currentTime = System.currentTimeMillis(); //现在时间  
        long time = (currentTime - startTime) /1000 +1;//已经传顺的时间 单位:s  
        double velocity = status.getBytesRead()/time; //传输速度:byte/s  
        double totalTime = status.getContentLength()/velocity; //估计总时间  
        double timeLeft = totalTime -time;    //估计剩余时间  
        int percent = (int)(100*(double)status.getBytesRead()/(double)status.getContentLength()); //百分比  
        double length = status.getBytesRead()/1024/1024; //已完成数  
        double totalLength = status.getContentLength()/1024/1024; //总长度  M 
        result.put("startTime",startTime);
        result.put("currentTime",currentTime);
        result.put("time",time);
        result.put("velocity",velocity);
        result.put("totalTime",totalTime);
        result.put("timeLeft",timeLeft);
        result.put("percent",percent);
        result.put("length",length);
        result.put("totalLength",totalLength);
        return result;
    }

这里写图片描述

总结

  • 获得文件对象js写法
    var fileObj = $("#fileObj")[0].files[0];
  • 后台需要自定义文件解析器并使用CommonsMultipartFile来接收
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值