springMVC上传文件 带有进度条
一、需求
一个文件上传的功能(带进度条)
二、实现方式
文件上传应该大部分人都接触过,一般都是基于commons-fileupload组件来实现,SpringMVC的文件上传功能也是在commons-fileupload组件提供的功能上面做了一些包装功能,使文件上传开发更容易方便。
三、遇到的问题
采用以上方式,会有比较大的问题。
在监听器那边采用的存到session中,那么
如果是同一个浏览器同一个标签页有同时触发了两次上传的操作,那么进度条的数值会乱串!
如果是同一个浏览器不同标签页同时上传,同样进度条的数值也会乱串!!!
四、改进方式
总的来说,在提交的时候,对上传任务进行编号,js端以时间戳加随机数编号就可以,把编号值传入服务端。在服务端session中对上传进度和编号进行一对一放入。获取的时候对比编号找出自己的那个上传任务,然后展示进度条就ok了。
详细点简单点的说就是将一次请求上传文件看做是一个线程,那么一次上传文件的线程id是唯一的,通过维护一个静态的ConcurrentHashMap值,就可以在监听器那边获取到客户端这边传入的文件编号。
五、附录代码
1.进度条代码(比较简单的,采用两个div,其中一个div作为背景,另一个div作为进度展示的宽度)
<html>
<head>
<title>进度条</title>
<style type="text/css">
.container{
width:450px;
border:1px solid #6C9C2C;
height:25px;
}
#bar{
background:#95CA0D;
float:left;
height:100%;
text-align:center;
line-height:150%;
}
</style>
<script type="text/javascript">
function run(){
var bar = document.getElementById("bar");
var total = document.getElementById("total");
bar.style.width=parseInt(bar.style.width) + 1 + "%";
//total.innerHTML = bar.style.width;
document.getElementById("bar1").innerHTML = bar.style.width;
if(bar.style.width == "100%"){
window.clearTimeout(timeout);
return;
}
var timeout=window.setTimeout("run()",100);
}
window.onload = function(){
run();
}
</script>
</head>
<body>
<div class="container">
<div id="bar1" style=" position: fixed;width:450px;text-align:center"></div>
<div id="bar" style="width:0%;"></div>
</div>
<span id="total"></span>
</body>
</html>
2.js 计算上传文件的大小
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN""http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta name="DEscription" contect="my code demo" />
<meta name="Author" contect="Michael@www.micmiu.com" />
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>js验证文件大小</title>
</head>
<body>
<img id="tempimg" dynsrc="" src="" style="display:none" />
<input type="file" name="file" id="fileuploade" size="40" />
<input type="button" name ="check" value="checkfilesize" οnclick="checkfile()"/>
</body>
<script type="text/javascript">
var maxsize = 2*1024*1024;//2M
var errMsg = "上传的附件文件不能超过2M!!!";
var tipMsg = "您的浏览器暂不支持计算上传文件的大小,确保上传文件不要超过2M,建议使用IE、FireFox、Chrome浏览器。";
function checkfile(){
try{
var obj_file = document.getElementById("fileuploade");
if(obj_file.value==""){
alert("请先选择上传文件");
return;
}
var filesize = 0;
//1.非IE
if(obj_file.files != null && typeof(obj_file.files)!="undefined"){
filesize = obj_file.files[0].size;
alert("非IE内核:"+filesize);
}else{
//2.IE
var obj_img = document.getElementById('tempimg');
obj_img.dynsrc=obj_file.value;
filesize = obj_img.fileSize;
alert("IE内核:"+filesize);
}
if(typeof(filesize) != 'number'){
alert(tipMsg);
return;
}
alert("类型typeof:"+typeof(filesize));
if(filesize==-1){
alert(tipMsg);
return;
}else if(filesize>maxsize){
alert(errMsg);
return;
}else{
alert("文件大小符合要求");
return;
}
}catch(e){
alert(e);
}
}
</script>
</html>
3.服务端改进的监听器代码:
01.新建GlobalThreadMap类,该类主要维护文件上传 的文件标识 (主要通过线程id)注意,在上传文件完成之后需要在上传文件的action中需要执行两个remove方法。
public class GlobalThreadMap {
//维护文件标识id
public final static ConcurrentHashMap<Long, String> uploadKeys = new ConcurrentHashMap<Long, String>();
//维护session 中进度条数值
public static ConcurrentHashMap<String, Progress> resMap = new ConcurrentHashMap<String, Progress>();
public static void putKey(String uploadKey) {
long id = Thread.currentThread().getId();
uploadKeys.put(id, uploadKey);
System.out.println("GlobalThreadMap putKey:" + id + " " + uploadKey);
System.out.println("当前GlobalThreadMap uploadKeys的值是: "+uploadKeys);
}
public static String getKey() {
return uploadKeys.get(Thread.currentThread().getId());
}
public static void removeKey() {
uploadKeys.remove(Thread.currentThread().getId());
}
public static void putResValue(Progress progress) {
resMap.put(getKey(), progress);
}
public static Progress getResValueByKey() {
return resMap.get(getKey());
}
public static void removeResKey() {
resMap.remove(getKey());
}
}
02.在扩展的监听器的类CustomMultipartResolver中方法resolveMultipart中:
public MultipartHttpServletRequest resolveMultipart(HttpServletRequest request) throws MultipartException {
// 获取到request,要用到session
this.request = request;
//设置值
String random = request.getParameter("random");
GlobalThreadMap.putKey(random);
return super.resolveMultipart(request);
}
03.在具体监听器实现这边FileUploadProgressListener
public class FileUploadProgressListener implements ProgressListener {
private HttpSession session;
public FileUploadProgressListener() {
}
public FileUploadProgressListener(HttpSession session) {
this.session = session;
Progress status = new Progress();
// 设置值
GlobalThreadMap.putResValue(status);
session.setAttribute("upload_ps", GlobalThreadMap.resMap);// 这是现在的
// 但是key去取出来是一个空的值,我是在上传文件开始的地方设置的值
// session.setAttribute("upload_ps", status); //这是之前的将进度条的数值设置到session里面
}
/**
* pBytesRead 到目前为止读取文件的比特数 pContentLength 文件总大小 pItems 目前正在读取第几个文件
*/
public void update(long pBytesRead, long pContentLength, int pItems) {
String key = GlobalThreadMap.getKey();
Map<String, Progress> map = (Map<String, Progress>) session
.getAttribute("upload_ps");
Progress status = map.get(key);
status.setBytesRead(pBytesRead);
status.setContentLength(pContentLength);
status.setItems(pItems);
status.setPercent(status.getPercent());
status.setSpeed(status.getSpeed());
status.setMbRead(status.getMbRead());
// 设置值
GlobalThreadMap.putResValue(status);
session.setAttribute("upload_ps", GlobalThreadMap.resMap);
}
}
04.同样的,在实时获取进度条数值的这边的action中,也需要稍稍修改一点:
@RequestMapping(value = "/upfile/progress.do", method = RequestMethod.POST )
@ResponseBody
public String initCreateInfo(HttpServletRequest request) {
String random = request.getParameter("random");
Map<String, Progress> map = (Map<String, Progress>) request.getSession().getAttribute("upload_ps");
if(map == null ){
return "{}";
}
Progress status = map.get(random);
JSONObject jsonStu = JSONObject.fromObject(status);
return jsonStu.toString();
}
05.对了,xml配置那边,需要将上传文件的的监听器配置成多例的
<bean id="multipartResolver" scope="prototype" class="com.sunsharing.ihome.air.web.common.fileupload.CustomMultipartResolver">
<property name="defaultEncoding" value="UTF-8" />
<property name="maxUploadSize" value="1000000000000" />
</bean>
ok。改造完成。
====================================
====================================
====================================