您的位置:首页 > 其它

SSH实现带进度条的Ajax文件上传

2016-03-06 12:37 465 查看
相信大家还没有做过这个功能的时候,在其他的博客上面也找到了很多类似的资料,但是对于一些新手来说,很难理解其中的意思。下面这段话是我从其他博客摘取下来的,说的是在Struts2中实现这个功能的原理:

利用Ajax在客户端一直查询服务器端的上传进度,取得进度的状态文本信息(xml,json格式的文本等),然后利用JS解析,显示在前台。
在Struts2. 0中,框架事先已经定义一种监听器:ProgressListener(进度监听器),里面有一个update(long readedBytes, long totalBytes, int currentItem)方法,其中,readedBytes是已经上传到服务器的位数,而totalBytes是上传文件总位数.当文件已二进制的方式上传时,每上传一部分数据,就会调用这个方法一次。故要实现监听进度,必须实现这个接口,并实现update方法,在update方法中保存这个进度到session。当客服端需要进度的信息时,只需要访问某个action,在这个action中读取session中保存的进度状态就可以了。

相信大家看了这段话之后很难理解其中的意思,我当时也是这样的。但是这个文件上传的功能必须实现,那么这个显示进度的功能也必须实现,所以无论如何我们也得实现这个功能,这是一件很痛苦的事情。但是,经过小编的理解之后,觉得有更好的方式解读这两句话。首先看一个图吧:



看了这个图之后是不是顿时思路清晰多了?当然这里只是知道了大概的思路,但是具体怎么做还没有体现出来。
现在我们开始动手实现具体的细节吧。
首先我这里是使用ssh框架进行开发的,还不会使用ssh框架的同学请自学,主要是Struts2框架。
然后是ajaxfileupload.js这个文件,这个是JQuery的一个插件,用于实现无刷新文件上传。下载链接在这里点击打开链接
然后是,前端我用了Bootstrap框架,进度条的显示的样式使用了Bootstrap的默认样式。
然后是准备自定义的MultiPartRequest类:
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package util.upload;

import com.opensymphony.xwork2.LocaleProvider;
import com.opensymphony.xwork2.inject.Inject;
import com.opensymphony.xwork2.util.LocalizedTextUtil;
import com.opensymphony.xwork2.util.logging.Logger;
import com.opensymphony.xwork2.util.logging.LoggerFactory;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.RequestContext;
import org.apache.commons.fileupload.FileUploadBase.SizeLimitExceededException;
import org.apache.commons.fileupload.disk.DiskFileItem;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.apache.struts2.dispatcher.multipart.JakartaMultiPartRequest;
import org.apache.struts2.dispatcher.multipart.MultiPartRequest;

public class MyMultiPartRequest implements MultiPartRequest {
static final Logger LOG = LoggerFactory.getLogger(JakartaMultiPartRequest.class);
protected Map<String, List<FileItem>> files = new HashMap();
protected Map<String, List<String>> params = new HashMap();
protected List<String> errors = new ArrayList();
protected long maxSize;
private Locale defaultLocale;

public MyMultiPartRequest() {
this.defaultLocale = Locale.ENGLISH;
}

@Inject("struts.multipart.maxSize")
public void setMaxSize(String maxSize) {
this.maxSize = Long.parseLong(maxSize);
}

@Inject
public void setLocaleProvider(LocaleProvider provider) {
this.defaultLocale = provider.getLocale();
}

public void parse(HttpServletRequest request, String saveDir) throws IOException {
String errorMessage;
try {
this.setLocale(request);
this.processUpload(request, saveDir);
} catch (SizeLimitExceededException var5) {
if(LOG.isWarnEnabled()) {
LOG.warn("Request exceeded size limit!", var5, new String[0]);
}

errorMessage = this.buildErrorMessage(var5, new Object[]{Long.valueOf(var5.getPermittedSize()), Long.valueOf(var5.getActualSize())});
if(!this.errors.contains(errorMessage)) {
this.errors.add(errorMessage);
}
} catch (Exception var6) {
if(LOG.isWarnEnabled()) {
LOG.warn("Unable to parse request", var6, new String[0]);
}

errorMessage = this.buildErrorMessage(var6, new Object[0]);
if(!this.errors.contains(errorMessage)) {
this.errors.add(errorMessage);
}
}

}

protected void setLocale(HttpServletRequest request) {
if(this.defaultLocale == null) {
this.defaultLocale = request.getLocale();
}

}

protected String buildErrorMessage(Throwable e, Object[] args) {
String errorKey = "struts.messages.upload.error." + e.getClass().getSimpleName();
if(LOG.isDebugEnabled()) {
LOG.debug("Preparing error message for key: [#0]", new String[]{errorKey});
}

return LocalizedTextUtil.findText(this.getClass(), errorKey, this.defaultLocale, e.getMessage(), args);
}

protected void processUpload(HttpServletRequest request, String saveDir) throws FileUploadException, UnsupportedEncodingException {
Iterator i$ = this.parseRequest(request, saveDir).iterator();

while(i$.hasNext()) {
FileItem item = (FileItem)i$.next();
if(LOG.isDebugEnabled()) {
LOG.debug("Found item " + item.getFieldName(), new String[0]);
}

if(item.isFormField()) {
this.processNormalFormField(item, request.getCharacterEncoding());
} else {
this.processFileField(item);
}
}

}

protected void processFileField(FileItem item) {
if(LOG.isDebugEnabled()) {
LOG.debug("Item is a file upload", new String[0]);
}

if(item.getName() != null && item.getName().trim().length() >= 1) {
Object values;
if(this.files.get(item.getFieldName()) != null) {
values = (List)this.files.get(item.getFieldName());
} else {
values = new ArrayList();
}

((List)values).add(item);
this.files.put(item.getFieldName(), (List<FileItem>) values);
} else {
LOG.debug("No file has been uploaded for the field: " + item.getFieldName(), new String[0]);
}
}

protected void processNormalFormField(FileItem item, String charset) throws UnsupportedEncodingException {
if(LOG.isDebugEnabled()) {
LOG.debug("Item is a normal form field", new String[0]);
}

Object values;
if(this.params.get(item.getFieldName()) != null) {
values = (List)this.params.get(item.getFieldName());
} else {
values = new ArrayList();
}

if(charset != null) {
((List)values).add(item.getString(charset));
} else {
((List)values).add(item.getString());
}

this.params.put(item.getFieldName(), (List<String>) values);
item.delete();
}

protected List<FileItem> parseRequest(HttpServletRequest servletRequest, String saveDir) throws FileUploadException {
UploadStatus status = new UploadStatus(); // 上传状态
UploadListener listner = new UploadListener(status); // 监听器
servletRequest.getSession().setAttribute("uploadStatus", status); // 把状态放到session里去

DiskFileItemFactory fac = createDiskFileItemFactory(saveDir);
ServletFileUpload upload = new ServletFileUpload(fac);
upload.setSizeMax(maxSize);
upload.setProgressListener(listner);// 添加监听器

return upload.parseRequest(createRequestContext(servletRequest));
}

protected ServletFileUpload createServletFileUpload(DiskFileItemFactory fac) {
ServletFileUpload upload = new ServletFileUpload(fac);
upload.setSizeMax(this.maxSize);
return upload;
}

protected DiskFileItemFactory createDiskFileItemFactory(String saveDir) {
DiskFileItemFactory fac = new DiskFileItemFactory();
fac.setSizeThreshold(0);
if(saveDir != null) {
fac.setRepository(new File(saveDir));
}

return fac;
}

public Enumeration<String> getFileParameterNames() {
return Collections.enumeration(this.files.keySet());
}

public String[] getContentType(String fieldName) {
List items = (List)this.files.get(fieldName);
if(items == null) {
return null;
} else {
ArrayList contentTypes = new ArrayList(items.size());
Iterator i$ = items.iterator();

while(i$.hasNext()) {
FileItem fileItem = (FileItem)i$.next();
contentTypes.add(fileItem.getContentType());
}

return (String[])contentTypes.toArray(new String[contentTypes.size()]);
}
}

public File[] getFile(String fieldName) {
List items = (List)this.files.get(fieldName);
if(items == null) {
return null;
} else {
ArrayList fileList = new ArrayList(items.size());

File storeLocation;
for(Iterator i$ = items.iterator(); i$.hasNext(); fileList.add(storeLocation)) {
FileItem fileItem = (FileItem)i$.next();
storeLocation = ((DiskFileItem)fileItem).getStoreLocation();
if(fileItem.isInMemory() && storeLocation != null && !storeLocation.exists()) {
try {
storeLocation.createNewFile();
} catch (IOException var8) {
if(LOG.isErrorEnabled()) {
LOG.error("Cannot write uploaded empty file to disk: " + storeLocation.getAbsolutePath(), var8, new String[0]);
}
}
}
}

return (File[])fileList.toArray(new File[fileList.size()]);
}
}

public String[] getFileNames(String fieldName) {
List items = (List)this.files.get(fieldName);
if(items == null) {
return null;
} else {
ArrayList fileNames = new ArrayList(items.size());
Iterator i$ = items.iterator();

while(i$.hasNext()) {
FileItem fileItem = (FileItem)i$.next();
fileNames.add(this.getCanonicalName(fileItem.getName()));
}

return (String[])fileNames.toArray(new String[fileNames.size()]);
}
}

public String[] getFilesystemName(String fieldName) {
List items = (List)this.files.get(fieldName);
if(items == null) {
return null;
} else {
ArrayList fileNames = new ArrayList(items.size());
Iterator i$ = items.iterator();

while(i$.hasNext()) {
FileItem fileItem = (FileItem)i$.next();
fileNames.add(((DiskFileItem)fileItem).getStoreLocation().getName());
}

return (String[])fileNames.toArray(new String[fileNames.size()]);
}
}

public String getParameter(String name) {
List v = (List)this.params.get(name);
return v != null && v.size() > 0?(String)v.get(0):null;
}

public Enumeration<String> getParameterNames() {
return Collections.enumeration(this.params.keySet());
}

public String[] getParameterValues(String name) {
List v = (List)this.params.get(name);
return v != null && v.size() > 0?(String[])v.toArray(new String[v.size()]):null;
}

public List<String> getErrors() {
return this.errors;
}

private String getCanonicalName(String filename) {
int forwardSlash = filename.lastIndexOf("/");
int backwardSlash = filename.lastIndexOf("\\");
if(forwardSlash != -1 && forwardSlash > backwardSlash) {
filename = filename.substring(forwardSlash + 1, filename.length());
} else if(backwardSlash != -1 && backwardSlash >= forwardSlash) {
filename = filename.substring(backwardSlash + 1, filename.length());
}

return filename;
}

protected RequestContext createRequestContext(final HttpServletRequest req) {
return new RequestContext() {
public String getCharacterEncoding() {
return req.getCharacterEncoding();
}

public String getContentType() {
return req.getContentType();
}

public int getContentLength() {
return req.getContentLength();
}

public InputStream getInputStream() throws IOException {
ServletInputStream in = req.getInputStream();
if(in == null) {
throw new IOException("Missing content in the request");
} else {
return req.getInputStream();
}
}
};
}

public void cleanUp() {
Set names = this.files.keySet();
Iterator i$ = names.iterator();

while(i$.hasNext()) {
String name = (String)i$.next();
List items = (List)this.files.get(name);
Iterator i$1 = items.iterator();

while(i$1.hasNext()) {
FileItem item = (FileItem)i$1.next();
if(LOG.isDebugEnabled()) {
String msg = LocalizedTextUtil.findText(this.getClass(), "struts.messages.removing.file", Locale.ENGLISH, "no.message.found", new Object[]{name, item});
LOG.debug(msg, new String[0]);
}

if(!item.isInMemory()) {
item.delete();
}
}
}

}
}
实现自定义的ProgressListener

package util.upload;

import org.apache.commons.fileupload.ProgressListener;

public class UploadListener implements ProgressListener {

private UploadStatus status;

public UploadListener(UploadStatus status) {
this.status = status;

}

public void update(long bytesRead, long contentLength, int items) {
// 上传组件会调用该方法
status.setBytesRead(bytesRead); // 已读取的数据长度
status.setContentLength(contentLength); // 文件总长度
status.setItems(items); // 正在保存第几个文件

}
}
用来存储下载进度状态的Java bean
package util.upload;

/**
* Created by martsforever on 2016/2/24.
*/
public class UploadStatus {
private long bytesRead;//已经上传的字节数,单位:字节
private long contentLength;//所有文件总长度,单位:字节
private int items;//正在上传的第几个文件
private long startTime = System.currentTimeMillis();//开始上传的时间,用于计算上传速度

public long getBytesRead() {
return bytesRead;
}

public void setBytesRead(long bytesRead) {
this.bytesRead = bytesRead;
}

public long getContentLength() {
return contentLength;
}

public void setContentLength(long contentLength) {
this.contentLength = contentLength;
}

public int getItems() {
return items;
}

public void setItems(int items) {
this.items = items;
}

public long getStartTime() {
return startTime;
}

public void setStartTime(long startTime) {
this.startTime = startTime;
}
}
这桑三个文件准备好了之后不要忘了在struts的配置文件中添加一下代码:
<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.3//EN"
"http://struts.apache.org/dtds/struts-2.3.dtd">

<struts>
<constant name="struts.multipart.parser" value="util.upload.MyMultiPartRequest" />
<constant name="struts.multipart.handler" value="util.upload.MyMultiPartRequest" />
<constant name="struts.multipart.maxSize" value="2000000000"/>
</struts>


好了,到现在为止,准备工作已经做完了,现在开始实现刚才图中的两个ajax和两个action:

jsp中的代码:

选择文件的表单:
<div class="col-xs-12 col-sm-12 col-md-12 clean text-center" style="padding-top: 10vh">
<center>
<form id="newResourceForm" action="uploadResourceFile" method="post">
<input type="text" class="form-control" id="name" placeholder="资源名称"
style="width: 30vw; margin-bottom: 5vh">
<input type="text" class="form-control" id="introduce" placeholder="简介"
style="width: 30vw; margin-bottom: 5vh">

<input id="resource" type="file" name="resource" style="display:none">

<div class="input-group" style="width: 30vw; margin-bottom: 5vh">
<input id="photoCover" type="text" class="form-control" placeholder="文件临时路径"
aria-describedby="basic-addon2">
<span class="input-group-addon btn btn-default" id="basic-addon2"
onclick="$('input[id=resource]').click();">选择文件</span>
</div>
</form>
<button type="submit" class="btn btn-success" style="width: 8vw" id="newResourceConfirmBtn">提交</button>
</center>
</div>
显示文件进度
<%--上传文件进度模态对话框--%>
<div class="modal fade" id="uploadFileProgressModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span
aria-hidden="true">×</span></button>
<h4 class="modal-title">正在上传文件</h4>
</div>
<div class="modal-body">
<div class="progress">
<div id="uploadProgress" class="progress-bar progress-bar-success progress-bar-striped active"
role="progressbar"
aria-valuenow="40" aria-valuemin="0" aria-valuemax="100" style="width: 0%">
<span class="sr-only">40% Complete (success)</span>
</div>
</div>
</div>
</div>
</div>
</div>
消息提示:
<%--消息提示模态对话框--%>
<div class="modal fade" id="msg" tabindex="-1" role="dialog" aria-labelledby="myModalLabel">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span
aria-hidden="true">×</span></button>
<h4 class="modal-title">消息提示</h4>
</div>
<div class="modal-body">
<span id="msgContent"></span>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal" id="msgBtn">确定</button>
</div>
</div>
</div>
</div>
不要忘了导入刚才的js文件:
<script src="js/ajaxfileupload.js"></script>


两个ajax
<script type="text/javascript">

var options = {
backdrop: false,
keyboard: false
};
var uploadInterval;

$("#newResourceConfirmBtn").click(function () {

//        alert($("#name").val().trim()+$("#introduce").val().trim());

$.ajaxFileUpload({
url: 'uploadResourceFile?name='+$("#name").val().trim()+"&"+"introduce="+$("#introduce").val().trim(),
type: 'post',
secureuri: false, //一般设置为false
fileElementId: 'resource', // 上传文件的id、name属性名
dataType: 'json', //返回值类型,一般设置为json、application/json
data: {//传递参数到服务器
"name": $("#name").val().trim(),
"introduce": $("#introduce").val().trim()
},
success: function (data, status) {
if (data.saveStatus == "success") {
$("#msgContent").html("添加成功");
$("#msg").modal(options);
$("#msgBtn").unbind('click');
$("#msgBtn").click(function(){
window.location.reload();
});
}
else("保存失败!");
},
error: function (data, status, e) {
alert(e);
}
});

$("#uploadConfirmBtn").attr({"disabled": "disabled"});
$("#uploadFileProgressModal").modal(options);

uploadInterval = setInterval("showuploadProgress()", 1000);

});

//    var precent = 0;

function showuploadProgress() {

$.ajax({
type: "POST",  //提交方式
url: "getFileUploadProgress",//路径
dataType: "json",//返回的json格式的数据
success: function (result) {//返回数据根据结果进行相应的处理
$("#uploadProgress").width(result.percent + '%');
if(result.percent == 100){
clearInterval(uploadInterval);
}
},
error: function (XMLHttpRequest, textStatus, errorThrown) {
alert(errorThrown);
}
});

//        precent+=10;
//        $("#uploadProgress").width(precent+'%');
//        if(precent == 100){
//            $("#uploadConfirmBtn").removeAttr("disabled");
//            clearInterval(uploadInterval);
//        }
}

$('input[id=resource]').change(function () {
$('#photoCover').val($(this).val());
});
</script>


后端接收文件的action:
@Action(value = "uploadResourceFile")
public void uploadResourceFile()throws Exception {

System.out.println("uploadResourceFile");
String name = request.getParameter("name");
String introduce = request.getParameter("introduce");
System.out.println("name="+name);
System.out.println("introduce="+introduce);
System.out.println("file is null"+(resource == null));

System.out.println("fileName"+resourceFileName);
System.out.println("fileContentType"+resourceContentType);

JSONObject jsonObject = new JSONObject();
response.setContentType("text/html;charset=utf-8");
response.setCharacterEncoding("utf-8");

if (resource != null) {

Resource newNesource = new Resource();
newNesource.setName(name);
newNesource.setIntroduce(introduce);
newNesource.setTime(dateFormat.format(new Date()));
newNesource.setDownloadTimes(0);
newNesource.setSize(resource.length());
//            resource.setDownloadUrl("resource/"+resourceFileName+resource.getTime());
resourceContentType = resourceFileName.substring(resourceFileName.lastIndexOf(".") + 1);//获得正真的文件类型
System.out.println("fileContentType:" + resourceContentType);
resourceFileName = newNesource.getName() + "." + resourceContentType;//存储的文件名称为用户账号名

String realpath = ServletActionContext.getServletContext().getRealPath("/resource");
/*System.out.println("realpath:" + realpath);*/

File saveFile = new File(new File(realpath), resourceFileName);
if (!saveFile.getParentFile().exists()) {
System.out.println("目录不存在,重新创建目录!");
saveFile.getParentFile().mkdirs();
}

FileUtils.copyFile(resource, saveFile);
String savePath = saveFile.getAbsolutePath();

newNesource.setDownloadUrl("resource/" + resourceFileName);
resourceService.add(newNesource);

System.out.println(resource.toString());

jsonObject.put("saveStatus", "success");
} else {
System.out.println("file is null");
jsonObject.put("saveStatus", "fail");
}
response.getWriter().write(jsonObject.toString());
}
/**
* 获取文件上传的进度
*/
@Action(value = "getFileUploadProgress")
public void getFileUploadProgress() throws Exception{
UploadStatus status = (UploadStatus) session.get("uploadStatus");

if (status == null) {
System.out.println("uploadStatus is null");
return;
}

long startTime = status.getStartTime(); //上传开始时间
long currentTime = System.currentTimeMillis(); //现在时间
long time = (currentTime - startTime) / 1000 + 1; //已传输的时间 单位:s

//传输速度单位:byte/s
double velocity = ((double) status.getBytesRead()) / (double) time;
//估计总时间
double totalTime = status.getContentLength();
//估计剩余时间
double timeLeft = totalTime - time;
//已经完成的百分比
int percent = (int) (100 * (double) status.getBytesRead() / (double) status.getContentLength());
//已经完成数单位:m
double length = ((double) status.getBytesRead()) / 1024 / 1024;
//总长度 单位:m
double totalLength = ((double) status.getContentLength()) / 1024 / 1024;

System.out.println("bytesRead:"+status.getBytesRead());
System.out.println("ContentLength:"+status.getContentLength());

//        System.out.println("percent:"+percent);
//        System.out.println("length:"+length);
//        System.out.println("totalLength:"+totalLength);
//        System.out.println("velocity:"+velocity);
//        System.out.println("time:"+time);
//        System.out.println("totalTime:"+totalTime);
//        System.out.println("timeLeft:"+timeLeft);
//        System.out.println("fileNumber:"+status.getItems());

JSONObject jsonObject = new JSONObject();

jsonObject.put("percent", String.valueOf(percent));
jsonObject.put("length", String.valueOf(length));
jsonObject.put("totalLength", String.valueOf(totalLength));
jsonObject.put("velocity", String.valueOf(velocity));
jsonObject.put("time", String.valueOf(time));
jsonObject.put("totalTime", String.valueOf(totalTime));
jsonObject.put("timeLeft", String.valueOf(timeLeft));
jsonObject.put("fileNumber", String.valueOf(status.getItems()));

response.setContentType("text/html;charset=utf-8");
response.setCharacterEncoding("utf-8");
response.getWriter().write(jsonObject.toString());
}
不要忘了该Action中添加三个成员变量:
private File resource;
private String resourceFileName;
private String resourceContentType;
还要提供get和set方法,这个成员变量的名称必须和ajax中的fileElementId一致,而且html中input的id和name都是这个。(这里看不清楚的可以提问,我也觉得这里表达不清楚)。
这里面使用了JsonObject,其中可以通过maven添加依赖:
<!--导入org.json的jar包--><!--org的json解析包-->
<dependency>
<groupId>org.json</groupId>
<artifactId>json</artifactId>
<version>20151123</version>
</dependency>
因为这个功能已经完成到项目中去了,所以没有办法将源码公布,请大家见谅,此文章仅供参考。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: