您的位置:首页 > 其它

基于AJAX的文件上传显示进度条实现

2017-06-14 21:35 871 查看
基于Ajax的文件上传要实现的功能要求,要在用户提交了上传按钮请求后,客户端其页面要显示文件上传进度条。

其整个功能时序图如图所示。



简单的说,要实现在客户端显示进度条,需要做的是:当客户端提交上传文件请求后,服务器在上传文件的过程中,将上传进度情况保存到Session中,客户端周期性的发送请求来获取保存在Session中值,以获取上传文件的进度信息。

1. 新建web工程AjaxUpload。

2. 将commons-fileupload-1.2.1-bin.zip包中的commons-fileupload-1.2.1.jar文件和commons-io-1.4-bin.zip包中的commons-io-1.4.jar文件拷贝到web工程下的WEB-INF\lib目录下。

3. 由于本实例涉及到多个类,处理此类问题最好是给相应的类打包进行管理。在web工程src目录下新建一个包com.ncu.upload。

4. 服务器端实现。

首先要创建一个用来保存文件上传状态的类 FileUploadStatus。其源码如下:

[java] view
plain copy

print?

package com.ncu.upload;

import java.util.*;

public class FileUploadStatus {

//上传总量

private long uploadTotalSize=0;

//读取上传总量

private long readTotalSize=0;

//当前上传文件号

private int currentUploadFileNum=0;

//成功读取上传文件数

private int successUploadFileCount=0;

//状态

private String status="";

//处理起始时间

private long processStartTime=0l;

//处理终止时间

private long processEndTime=0l;

//处理执行时间

private long processRunningTime=0l;

//上传文件URL列表

private List uploadFileUrlList=new ArrayList();

//取消上传

private boolean cancel=false;

//上传base目录

private String baseDir="";

public String getBaseDir() {

return baseDir;

}

public void setBaseDir(String baseDir) {

this.baseDir = baseDir;

}

public boolean getCancel() {

return cancel;

}

public void setCancel(boolean cancel) {

this.cancel = cancel;

}

public List getUploadFileUrlList() {

return uploadFileUrlList;

}

public void setUploadFileUrlList(List uploadFileUrlList) {

this.uploadFileUrlList = uploadFileUrlList;

}

public long getProcessRunningTime() {

return processRunningTime;

}

public void setProcessRunningTime(long processRunningTime) {

this.processRunningTime = processRunningTime;

}

public long getProcessEndTime() {

return processEndTime;

}

public void setProcessEndTime(long processEndTime) {

this.processEndTime = processEndTime;

}

public long getProcessStartTime() {

return processStartTime;

}

public void setProcessStartTime(long processStartTime) {

this.processStartTime = processStartTime;

}

public long getReadTotalSize() {

return readTotalSize;

}

public void setReadTotalSize(long readTotalSize) {

this.readTotalSize = readTotalSize;

}

public int getSuccessUploadFileCount() {

return successUploadFileCount;

}

public void setSuccessUploadFileCount(int successUploadFileCount) {

this.successUploadFileCount = successUploadFileCount;

}

public int getCurrentUploadFileNum() {

return currentUploadFileNum;

}

public void setCurrentUploadFileNum(int currentUploadFileNum) {

this.currentUploadFileNum = currentUploadFileNum;

}

public String getStatus() {

return status;

}

public void setStatus(String status) {

this.status = status;

}

public long getUploadTotalSize() {

return uploadTotalSize;

}

public void setUploadTotalSize(long uploadTotalSize) {

this.uploadTotalSize = uploadTotalSize;

}

}

由于要在客户端要显示进度条,所以在上传过程中服务器端需要监视和维护上传状态的信息,此过程需要处理的数据信息是:不断更新Session中保存的FileUploadStatus实例的信息,如:已经上传的字节数,上传文件的总大小等。FileUpload现在的1.2版本为监视上传进度提供了内建的支持,可以直接继承类ProgressListener,然后重载update()方法,在该方法中添加自己要处理的代码,最后在文件上传处理代码(后面会讲到)中通过为ServletFileUpload对象注册创建的监听类。监听类UploadListener的源代码如下:

[java] view
plain copy

print?

package com.ncu.upload;

import javax.servlet.http.HttpSession;

import org.apache.commons.fileupload.ProgressListener;

public class UploadListener implements ProgressListener {

private HttpSession session=null;

public UploadListener (HttpSession session){

this.session=session;

}

/**

* 更新状态

* @param pBytesRead 读取字节总数

* @param pContentLength 数据总长度

* @param pItems 当前正在被读取的field号

*/

public void update(long pBytesRead, long pContentLength, int pItems) {

FileUploadStatus fuploadStatus = UploadServlet.takeOutFileUploadStatusBean(this.session);

fuploadStatus.setUploadTotalSize(pContentLength);

//读取完成

if (pContentLength == -1) {

fuploadStatus.setStatus("完成对" + pItems + "个文件的读取:读取了 " + pBytesRead + "/" + pContentLength+ " bytes.");

fuploadStatus.setReadTotalSize(pBytesRead);

fuploadStatus.setCurrentUploadFileNum(pItems);

fuploadStatus.setProcessEndTime(System.currentTimeMillis());

fuploadStatus.setProcessRunningTime(fuploadStatus.getProcessEndTime());

}else{//读取过程中

fuploadStatus.setStatus("当前正在处理第" + pItems+"个文件:已经读取了 " + pBytesRead + " / " + pContentLength+ " bytes.");

fuploadStatus.setReadTotalSize(pBytesRead);

fuploadStatus.setCurrentUploadFileNum(pItems);

fuploadStatus.setProcessRunningTime(System.currentTimeMillis());

}

//System.out.println("已经读取:" + pBytesRead);

UploadServlet.storeFileUploadStatusBean(this.session, fuploadStatus);

}

}

有了前面两个类的基础,下来我们可以动手去实现真正处理整个操作Servlet类。源代码如下。

[java] view
plain copy

print?

package com.ncu.upload;

import java.io.*;

import java.util.List;

import javax.servlet.ServletException;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;

import javax.servlet.http.HttpSession;

import org.apache.commons.fileupload.FileItem;

import org.apache.commons.fileupload.FileUploadException;

import org.apache.commons.fileupload.disk.DiskFileItemFactory;

import org.apache.commons.fileupload.servlet.*;

/**

* Servlet implementation class for Servlet: UploadServlet

*

*/

public class UploadServlet extends javax.servlet.http.HttpServlet implements javax.servlet.Servlet {

static final long serialVersionUID = 1L;

public static final String UPLOAD_STATUS="UPLOAD_STATUS";

public static final String UPLOAD_DIR="/upload";

public UploadServlet() {

super();

}

/**

* 从文件路径中取出文件名

* @param filePath

* @return

*/

private String takeOutFileName(String filePath){

int pos=filePath.lastIndexOf(File.separator);

if (pos>0){

return filePath.substring(pos+1);

}

else{

return filePath;

}

}

/**

* 从request中取出FileUploadStatus Bean

* @param request

* @return

*/

public static FileUploadStatus takeOutFileUploadStatusBean(HttpSession session){

Object obj=session.getAttribute(UPLOAD_STATUS);

if (obj!=null){

return (FileUploadStatus)obj;

}

else{

return null;

}

}

/**

* 把FileUploadStatus Bean保存到session

* @param request

* @param uploadStatusBean

*/

public static void storeFileUploadStatusBean(

HttpSession session,

FileUploadStatus uploadStatusBean){

session.setAttribute(UPLOAD_STATUS,uploadStatusBean);

}

/**

* 删除已经上传的文件

* @param request

*/

private void deleteUploadedFile(HttpServletRequest request){

FileUploadStatus fUploadStatus=takeOutFileUploadStatusBean(request.getSession());

for(int i=0;i<fUploadStatus.getUploadFileUrlList().size();i++){

File uploadedFile = new File(request.getRealPath(UPLOAD_DIR)+

File.separator+fUploadStatus.getUploadFileUrlList().get(i));

uploadedFile.delete();

}

fUploadStatus.getUploadFileUrlList().clear();

fUploadStatus.setStatus("删除已上传的文件");

storeFileUploadStatusBean(request.getSession(),fUploadStatus);

}

/**

* 上传过程中出错处理

* @param request

* @param errMsg

* @throws IOException

* @throws ServletException

*/

private void uploadExceptionHandle(

HttpServletRequest request,

String errMsg) throws ServletException, IOException{

//首先删除已经上传的文件

deleteUploadedFile(request);

FileUploadStatus fUploadStatus=takeOutFileUploadStatusBean(request.getSession());

fUploadStatus.setStatus(errMsg);

storeFileUploadStatusBean(request.getSession(),fUploadStatus);

}

/**

* 初始化文件上传状态Bean

* @param request

* @return

*/

private FileUploadStatus initFileUploadStatusBean(HttpServletRequest request){

FileUploadStatus fUploadStatus=new FileUploadStatus();

fUploadStatus.setStatus("正在准备处理");

fUploadStatus.setUploadTotalSize(request.getContentLength());

fUploadStatus.setProcessStartTime(System.currentTimeMillis());

fUploadStatus.setBaseDir(request.getContextPath()+UPLOAD_DIR);

return fUploadStatus;

}

/**

* 处理文件上传

* @param request

* @param response

* @throws IOException

* @throws ServletException

*/

private void processFileUpload(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException{

DiskFileItemFactory factory = new DiskFileItemFactory();

//设置内存阀值,超过后写入临时文件

//factory.setSizeThreshold(10240000*5);

//设置临时文件存储位置

//factory.setRepository(new File(request.getRealPath("/upload/temp")));

ServletFileUpload upload = new ServletFileUpload(factory);

//设置单个文件的最大上传size

//upload.setFileSizeMax(10240000*5);

//设置整个request的最大size

//upload.setSizeMax(10240000*5);

//注册监听类

upload.setProgressListener(new UploadListener(request.getSession()));

//保存初始化后的FileUploadStatus Bean

storeFileUploadStatusBean(request.getSession(),initFileUploadStatusBean(request));

try {

List items = upload.parseRequest(request);

//处理文件上传

for(int i=0;i<items.size();i++){

FileItem item=(FileItem)items.get(i);

//取消上传

if (takeOutFileUploadStatusBean(request.getSession()).getCancel()){

deleteUploadedFile(request);

break;

}

//保存文件

else if (!item.isFormField() && item.getName().length()>0){

String fileName=takeOutFileName(item.getName());

File uploadedFile = new File(request.getRealPath(UPLOAD_DIR)+File.separator+fileName);

item.write(uploadedFile);

//更新上传文件列表

FileUploadStatus fUploadStatus=takeOutFileUploadStatusBean(request.getSession());

fUploadStatus.getUploadFileUrlList().add(fileName);

storeFileUploadStatusBean(request.getSession(),fUploadStatus);

Thread.sleep(500);

}

}

} catch (FileUploadException e) {

e.printStackTrace();

//uploadExceptionHandle(request,"上传文件时发生错误:"+e.getMessage());

} catch (Exception e) {

// TODO Auto-generated catch block

e.printStackTrace();

//uploadExceptionHandle(request,"保存上传文件时发生错误:"+e.getMessage());

}

}

/**

* 回应上传状态查询

* @param request

* @param response

* @throws IOException

*/

private void responseFileUploadStatusPoll(HttpServletRequest request,HttpServletResponse response) throws IOException{

FileUploadStatus fUploadStatus=(FileUploadStatus)request.getSession().getAttribute(UPLOAD_STATUS);

//计算上传完成的百分比

long percentComplete = (long)Math.floor(((double) fUploadStatus.getReadTotalSize()/(double) fUploadStatus.getUploadTotalSize())*100.0);

System.out.println("com:"+percentComplete);

response.setContentType("text/xml");

response.setCharacterEncoding("UTF-8");

response.setHeader("Cache-Control", "no-cache");

if ( ((long)fUploadStatus.getReadTotalSize() == (long)fUploadStatus.getUploadTotalSize()) || (fUploadStatus.getCancel() == true)){

response.getWriter().write(fUploadStatus.getStatus().toString()+"success");

}else{

response.getWriter().write(fUploadStatus.getStatus().toString()+"<div class=\"prog-border\"><div class=\"prog-bar\" style=\"width: "

+ percentComplete + "%;\"></div></div>");

}

}

/**

* 处理取消文件上传

* @param request

* @param response

* @throws IOException

*/

private void processCancelFileUpload(HttpServletRequest request,HttpServletResponse response) throws IOException{

FileUploadStatus fUploadStatus=(FileUploadStatus)request.getSession().getAttribute(UPLOAD_STATUS);

fUploadStatus.setCancel(true);

request.getSession().setAttribute(UPLOAD_STATUS, fUploadStatus);

responseFileUploadStatusPoll(request,response);

}

/**

* 在上传文件列表中查找与文件名相关的id

* @param request

* @param fileName 文件名

* @return 找到返回id,否则返回-1

*/

private int findFileIdInFileUploadedList(HttpServletRequest request,String fileName){

FileUploadStatus fileUploadStatus=takeOutFileUploadStatusBean(request.getSession());

for(int i=0;i<fileUploadStatus.getUploadFileUrlList().size();i++){

if (fileName.equals((String)fileUploadStatus.getUploadFileUrlList().get(i))){

return i;

}

}

return -1;

}

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

doPost(request,response);

}

protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

boolean isMultipart = ServletFileUpload.isMultipartContent(request);

if (isMultipart) {

processFileUpload(request,response);

}else{

request.setCharacterEncoding("UTF-8");

if (request.getParameter("uploadStatus")!=null){

responseFileUploadStatusPoll(request,response);

}

if (request.getParameter("cancelUpload")!=null){

processCancelFileUpload(request,response);

}

}

}

}

至此,服务器端的代码已经基本完成。

5. 客户端实现

由于在上传文件时需要在同一页面显示对应的进度条控件,因此,在提交表单时当前页面不能被刷新。我们可以通过将表单提交至一个隐藏的 iframe 中来实现。关于Ajax的技术前面讲过,这里就不再细说,直接给出源代码如下:

[html] view
plain copy

print?

<!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>基于Ajax的上传文件显示进度条</title>

<style>

.prog-border {

height: 15px;

width: 205px;

background: #fff;

border: 1px solid #000;

margin: 0;

padding: 0;

}

.prog-bar {

height: 11px;

margin: 2px;

padding: 0px;

background: #178399;

font-size: 10pt;

}

body{

font-family: Arial, Helvetica, sans-serif;

font-size: 10pt;

}

</style>

<script language="javascript" type="text/javascript">

<!--

//var userName=document.getElementById("userName").value;

//创建跨浏览器的XMLHttpRequest对象

var timer;

function startListener(){

var xmlhttp;

try{

//IE 5.0

xmlhttp = new ActiveXObject('Msxm12.XMLHTTP');

}catch(e){

try{

//IE 5.5 及更高版本

xmlhttp = new ActiveXObject('Microsoft.XMLHTTP');

}catch(e){

try{

//其他浏览器

xmlhttp = new XMLHttpRequest();

}catch(e){}

}

}

var progressStatusText = document.getElementById("progressBar");

xmlhttp.open("get","UploadServlet?uploadStatus=true",true);

/**此处Header设置非常重要,必须设置Content-type类型,负责会报错误

*/

xmlhttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded");

xmlhttp.onreadystatechange = function(){

if(xmlhttp.readyState == 4){

if(xmlhttp.status == 200){

progressStatusText.innerHTML = "";

progressStatusText.innerHTML = xmlhttp.responseText;

var temp = xmlhttp.responseText.indexOf("success");

if ( temp > 0 ){

window.clearTimeout(timer);

}else{

timer = window.setTimeout(startListener,1000);

}

}

}

}

xmlhttp.send(null);

}

function startUpload(){

timer = window.setTimeout(startListener,1000);

return true;

}

function cancelUpload(){

var xmlhttp;

try{

//IE 5.0

xmlhttp = new ActiveXObject('Msxm12.XMLHTTP');

}catch(e){

try{

//IE 5.5 及更高版本

xmlhttp = new ActiveXObject('Microsoft.XMLHTTP');

}catch(e){

try{

//其他浏览器

xmlhttp = new XMLHttpRequest();

}catch(e){}

}

}

var progressStatusText = document.getElementById("progressBar");

xmlhttp.open("get","UploadServlet?cancelUpload=true",true);

xmlhttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded");

//xmlhttp.setRequestHeader("Content-type", "multipart/form-data");

xmlhttp.onreadystatechange = function(){

if(xmlhttp.readyState == 4){

if(xmlhttp.status == 200){

progressStatusText.innerHTML = "";

progressStatusText.innerHTML = xmlhttp.responseText;

}

}

}

xmlhttp.send(null);

return false;

}

//-->

</script>

</head>

<body>

<div id="controlPanel">

<!-- 这个是隐藏的<iframe>作为表单提交后处理的后台目标

通过表单form的target属性指定该<iframe>将返回信息显示在<iframe>框架中

-->

<iframe id='target_upload' name='target_upload' src='' style='display: none'></iframe>

<form id="fileUploadForm" name="fileUploadForm" action="UploadServlet"

enctype="multipart/form-data" method="post" onsubmit="return startUpload();" target="target_upload">

<input type="file" name="file" id="file" size="40"/><br>

<input type="submit" name="uploadButton" id="uploadButton" value="开始上传"/>

<input type="button" name="cancelUploadButton" id="cancelUploadButton" value="取消上传" onclick="return cancelUpload();"/><br>

</form>

<div id="progressBar">

</div>

</div>

</body>

</html>
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: