上周为项目增加了文件上传功能,用于接收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处理的并不完善,当向相同路径重新上传相同文件名的文件时。会出现阻塞,可能是在处理的过程中没有关闭流,解决的办法就是对上传的每个文件进行重命名。