外包项目已经部分上线,突然甲方测试出上传文件有问题,IE10及以下版本、火狐低版本、其它浏览器的一些版本等上传都失败。
当项目主管跟我提出这个问题时,我们看了下,整个项目有约30多处涉及到了文件上传(包括图片和非图片),每一处上传涉及到界面元素、后端处理、js回调处理,其中js部分是一个共同的方法(也有特殊要求而重写的其它类似方法),界面上,在单击图片空白框后,弹出浏览文件的界面供用户选择要上传的文件,确定后,图片及时显示出来,待真正提交表单后,将图片的路径信息保存在库中,被上传的文件保存在项目外的文件夹下,显示时复制到项目内。之所以上传失败,是因为js的上传方法使用了FormData对象,这个对象只在支持html5的高版本浏览器中才可以使用。现在问题是,如果要改,涉及的面比较大,最关键的是,如何改?因为甲方很强势,界面必需满足原型设计。当初之所以这样上传,完全是为了项目进度,直接采用了上一个项目的上传方案。现在出现兼容问题,弄不好要拖累项目的进度。
不管是上传图片还是pdf等文档,在上传后,要能马上看图片或pdf的缩略图,这里涉及到一个异步上传问题。
如果不使用FormData对象,可能就不能及时显示图片,虽然jquery的post使用$('#form').serialize()可以异步提交表单,但对于file的input域,根本不起作用。网上搜索异步上传文件的方法,倒是有不少方案,有jquery.form.js的、有用其它方式,但试了其中的方式后,发现很多方案最后用的还是FormData。
如果将上传input域单独使用一个form,可以上传,但页面跳走,其它信息不能提交。能不能提交后不跳走,并能返回 后台信息呢?即做到异步提交又能回调呢?
在反复调找后,总算找到一种方式,可以模拟异步上传,又能返回信息。如果按此法改造各个上传的地方,应该是容易的,不是那种推到式的重写。最大特点是,它采用了普通方式上传文件,就不存在浏览器兼容问题了。
现摘取其中部分代码,以作记载。
前端部分, 两个jsp页面,一个主页,用于放整个表单信息,一个专门放上传域。
<script type="text/javascript">
function callBack(data){//后台上传完成后回调,参数为文件的路径信息
var json=eval('('+data+')');
$('#aImage').attr('src','/echarts'+json['fileURL']);//将图片显示在界面上
$('#path').val(json['fileURL']);//保存文件的路径信息用于表单提交
}
</script>
</head>
<body>
<form action="/echarts/up/submitForm" method="post">
<input type="text" name="title" id="title" value="输入标题"/>
<input type="hidden" name="path" id="path" value="隐藏的待提交的已经上传的文件地址"/>
<img id="aImage" src="" οnclick="javascript:iframe.window.$('#afile').click()" width="100" height="100">
<input type="submit" value="提交">
</form>
<iframe src="/echarts/upload/do.jsp" id="iframe" name="iframe" style="display: none;"></iframe>
</body>
上传域jsp(名称do.jsp,放入上面隐藏的iframe中):
<script type="text/javascript">
function subform(){
$('#fileform').submit();//提交表单
}
</script>
<form action="/echarts/up/uploadAttachment" id="fileform" enctype="multipart/form-data" method="post" style="display: none;">
<input type="file" name="afile" id="afile" οnchange="subform()"/>
</form>
后台部分:
public String uploadAttachment(){
try{
HttpServletResponse response = ServletActionContext.getResponse();
response.setContentType("text/html;charset=utf-8");
PrintWriter out= response.getWriter();
String path=ServletActionContext.getRequest().getRealPath("/uploadfile");
String fileURL=path+"/"+afileFileName;
FileUtils.copyFile(afile,new File(fileURL));
String res="{success:'true',fileURL:'/uploadfile/"+afileFileName+"'}";
String rt="<script>window.parent.callBack(\""+res+"\");</script>";//就靠这句回调前端
out.print(rt);
}catch(Exception e){
e.printStackTrace();
}
return null;
}
因为不是真正的异步,所以如果上传的文件较大,可能会有一个不应期,这个应该正好,因为只有传完后才能到下一步。以前的方式倒是有问题了,还没有等上传完成就可提交表单,路径信息可能丢失。