上周为项目增加了文件上传功能,用于接收http接口上传的文件。现将一些心得与总结记录下来。
项目使用环境:JDK6、Tomcat6
我尝试过两种方式来实现文件上传的功能,分别为common-fileupload与spingMVC的文件上传。
方式一、common-fileupload实现文件上传
相关jar包:commons-fileupload-1.3.1.jar,commons-io-1.4.jar
/** * 1.获取项目所在路径 * 2.构建文件存放路径 * 3.构建文件相对路径 * 4.设置request的字符集 * 5.创建磁盘文件工厂 * 6.设置缓冲大小、上传文件大小限制、临时文件夹 * 7.得到上传文件名 * 8.将文件保存到服务器端 * @throws UnsupportedEncodingException */ public void fileUpload(HttpServletRequest request, String fload) throws Exception { Date currData = new Date(); SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); String classPath=this.getClass().getClassLoader().getResource("").getPath(); String projectPath=classPath.substring(0, classPath.length()-"/WEB-INF/classes/".length()); String projectName=projectPath.substring(projectPath.lastIndexOf("/")+1); String filePath=projectPath+fload+"/"+sdf.format(currData); //文件存放路径 String relativePath="/"+projectName+fload+"/"+sdf.format(currData); //文件存放相对路径 String tempUploadPath=projectPath+"/"+"tempUpload"; //临时文件夹路径为 System.out.println("文件存放的文件夹为----:"+filePath); System.out.println("文件存放的文件夹相对路径为----:"+relativePath); System.out.println("临时文件夹路径为----:"+tempUploadPath); request.setCharacterEncoding("UTF-8"); final long MAX_SIZE=100 * 1024 * 1024; //上传文件大小限制 DiskFileItemFactory dfif = new DiskFileItemFactory(); // 实例化一个硬盘文件工厂,用来配置上传组件ServletFileUpload dfif.setSizeThreshold(40960000); // 设置缓冲(字节),这个值决定了是fileinputstream还是bytearrayinputstream File tempUploadFload = new File(tempUploadPath); if(!tempUploadFload.isDirectory()){ tempUploadFload.mkdirs(); } dfif.setRepository(tempUploadFload); //设置临时文件夹路径 ServletFileUpload sfu = new ServletFileUpload(dfif); sfu.setFileSizeMax(MAX_SIZE); //设置上传文件大小 sfu.setHeaderEncoding("UTF-8"); List<FileItem> fils=null; try { fils = sfu.parseRequest(request); } catch (Exception e) { e.printStackTrace(); } Iterator<FileItem> fileite = fils.iterator(); String fileName=""; String fileType=""; String fileAllPath=""; String relativeAllPath=""; long fileSize=0L; while(fileite.hasNext()){ FileItem fileItem = fileite.next(); if(fileItem==null || fileItem.isFormField() ){ //忽略简单form字段 continue; } String path = fileItem.getName() ; fileSize = fileItem.getSize(); fileName=(path.lastIndexOf("/")!=-1)?path.substring(path.lastIndexOf("/")+1):path; //得到上传文件名 fileType=path.substring(path.lastIndexOf(".")+1); //文件类型 fileAllPath=filePath+"/"+fileName; //文件全路径 relativeAllPath=filePath+"/"+fileName; //文件相对路径 System.out.println("文件全路径为-----:"+fileAllPath); if(fileName!=null&&!"".equals(fileName.trim())){ File uploadFileFload = new File(filePath); File uploadFile = new File(fileAllPath); if(!uploadFileFload.isDirectory()){ boolean falg = uploadFileFload.mkdirs(); System.out.println("文件夹创建状态-----:"+falg); }else{ System.out.println("文件保存在------:"+filePath); } fileItem.write(uploadFile); //写入文件 System.out.println("文件上传成功,文件名为: "+fileName+" 大小为: "+fileSize+"字节"); }else{ System.out.println("文件上传失败"); } } }
方式一的心得:
1.服务器端与客户端的编码要一致,否则上传包含有中文的文件的文件名会乱码。
2.设置了缓存大小后,如果上传的文件大于缓存设置。则会先上传到临时文件夹,当调用write方法时才写入到指定路径中,以节省内存占用。
3.上传文件夹要先创建好,然后再写入文件。这个是容易忽略的。
4.在springMVC等框架中,parseRequest返回的结果如果为空,则是框架已经对对包含了文件输入流的requet进行了处理。我们得到的已经不是原始的request,所以变会为空了。笔者在使用第一种方法的时候就遇到这个问题,遂使用方式二。
方式二、使用springMVC的文件上传
实际上spingMVC对request的解析也是用common-fileupload中的parseRequest方法,这也就是方式一中parseRequest为空的原因。spingMVC已经帮我们处理了。
首先我们需要现在application-content.xml中添加该类,使用该类处理后,与上传相关的设置是在xml中配置的。
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"/>
之后的处理就比较简单了,以下是实现。
public List<Map<String,String>> mvcFileUpload(HttpServletRequest request, String fload) throws IllegalStateException, IOException{ Date currData = new Date(); SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); String classPath=this.getClass().getClassLoader().getResource("").getPath(); String projectPath=classPath.substring(0, classPath.length()-"/WEB-INF/classes/".length()); String projectName=projectPath.substring(projectPath.lastIndexOf("/")+1); String filePath=projectPath+fload+"/"+sdf.format(currData); //文件夹存放路径 String relativePath="/"+projectName+fload+"/"+sdf.format(currData); //文件夹存放相对路径 String fileAllPath=null; //文件存放路径 String relativeAllPath=null; //文件存放相对路径 System.out.println("文件存放的文件夹为----:"+filePath); System.out.println("文件存放的文件夹相对路径为----:"+relativePath); File saveFolder = new File(filePath); if(!saveFolder.isDirectory()){ boolean falg = saveFolder.mkdirs(); System.out.println("文件夹创建状态-----:"+falg); } String realFileName=null; //真实上传文件名 String fileType=null; //文件类型 List<Map<String,String>> result=new ArrayList<Map<String,String>>(); MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) request; Iterator<String> fileNameIte = multipartRequest.getFileNames(); while(fileNameIte.hasNext()){ String fileName = fileNameIte.next(); MultipartFile mr = multipartRequest.getFile(fileName); realFileName = mr.getOriginalFilename(); if(StringUtils.isNotBlank(realFileName)){ System.out.println("上传的文件名为-----:"+realFileName); fileType=realFileName.substring(realFileName.lastIndexOf(".")+1); realFileName=realFileName.substring(0, realFileName.lastIndexOf("."))+"-"+System.nanoTime()+"."+fileType; //对上传文件进行重命名 fileAllPath=filePath+"/"+realFileName; relativeAllPath=relativePath+"/"+realFileName; File localFile = new File(fileAllPath); mr.transferTo(localFile); Map<String, String> map = new HashMap<String, String>(); map.put("fileName", realFileName); map.put("fileType", fileType); map.put("fileAllPath", fileAllPath); map.put("relativeAllPath", relativeAllPath); result.add(map); } } return result; }
但笔者发现spingMVC处理的并不完善,当向相同路径重新上传相同文件名的文件时。会出现阻塞,可能是在处理的过程中没有关闭流,解决的办法就是对上传的每个文件进行重命名。