文件夹上传:从前端到后端
文件上传是 Web 开发肯定会碰到的问题,而文件夹上传则更加难缠。网上关于文件夹上传的资料多集中在前端,缺少对于后端的关注,然后讲某个后端框架文件上传的文章又不会涉及文件夹。今天研究了一下这个问题,在此记录。
先说两个问题:
是否所有后端框架都支持文件夹上传?
是否所有浏览器都支持文件夹上传?
第一个问题:YES,第二个问题:NO
只要后端框架对于表单的支持是完整的,那么必然支持文件夹上传。至于浏览器,截至目前,只有 Chrome 支持。 Chrome 大法好!
不要期望文件上传这个功能的浏览器兼容性,这是做不到的。
好,假定我们的所有用户都用上了 Chrome,要怎么做才能成功上传一个文件夹呢?这里不用drop这种高大上的东西,就用最传统的<input>。用表单 submit 和 ajax 都可以做,先看 submit 方式。
<form method="POST" enctype=multipart/form-data>
<input type='file' name="file" webkitdirectory >
<button>upload</button>
</form>
我们只要添加上 webkitdirectory 这个属性,在选择的时候就可以选择一个文件夹了,如果不加,文件夹被选中的时候就是灰色的。不过貌似加上这个属性就没法选中文件了... enctype=multipart/form-data 也是必要的,解释参见这里
如果用 ajax 方式,我们可以省去<form>,只留下<input>就 OK。
<input type='file' webkitdirectory >
<button id="upload-btn" type="button">upload</button>
但是这样是不够的,关键在于 Js 的使用。
var files = [];
$(document).ready(function(){
$("input").change(function(){
files = this.files;
});
});
$("#upload-btn").click(function(){
var fd = new FormData();
for (var i = 0; i < files.length; i++) {
fd.append("file", files[i]);
}
$.ajax({
url: "/upload/",
method: "POST",
data: fd,
contentType: false,
processData: false,
cache: false,
success: function(data){
console.log(data);
}
});
});
用 ajax 方式,我们必须手动构造一个 FormData Object, 然后放在 data 里面提交到后端。 FormData 好像就只有一个 append 方法,第一个参数是 key,第二个参数是 value,用来构造表单数据。ajax请求中,通过 input 元素的 files 属性获取上传的文件。files属性不论加不加 webkitdirectory 都是存在的,用法也基本一样。不过当我们上传文件夹时,files 中会包含文件相对路径的信息,之后会看到。
用 ajax 上传的好处有两点,首先是异步,这样不会导致页面卡住,其次是能比较方便地实现上传进度条。关于上传进度条的实现可以参考这里。需要注意的是contentType和processData必须设置成false,参考了这里。
————————————————
后台Java部分
//上传文件 @RequestMapping("/uploadDate") @ResponseBody public Map<String, Object> uploadDate(HttpServletRequest request, HttpServletResponse response) throws IllegalStateException, IOException { //创建一个通用的多部分解析器 CommonsMultipartResolver multipartResolver = new CommonsMultipartResolver(request.getSession().getServletContext()); Map<String, Object> maps =new HashMap<String, Object>(); Map<String,Object>docxMap=null; try{ //判断 request 是否有文件上传,即多部分请求 if(multipartResolver.isMultipart(request)){ //转换成多部分request MultipartHttpServletRequest multiRequest = (MultipartHttpServletRequest)request; //取得request中的所有文件名 Iterator<String> iter = multiRequest.getFileNames(); String importDate = new SimpleDateFormat("yyyyMMdd").format(new Date()); Map<String,Object>xlsxMap=null; while(iter.hasNext()){ //取得上传文件 MultipartFile file = multiRequest.getFile(iter.next()); if(file != null){ //取得当前上传文件的文件名称 String myFileName = file.getOriginalFilename(); System.out.println("文件名称:"+myFileName); String strName = myFileName.substring(myFileName.lastIndexOf(".") + 1); System.out.println("文件后缀名称:"+strName); if(strName.equals("xlsx") || strName.equals("xls")){ //导入Excel 根据自己的业务逻辑 }else if(strName.equals("docx") || strName.equals("doc")){ //保存docx文档 根据自己的业务逻辑 } } } } maps.put("success", "success"); maps.put("count",count); return maps; } catch (Exception e){ maps.put("erro", "导入失败!"); logger.error("错误信息:" + e.getMessage(), e); e.printStackTrace(); return maps; } }