文件上传知识点整理
一、servlet api 实现
参考文献:http://www.cnblogs.com/xdp-gacl/p/4224960.html
1、前台处理
文件上传方式使用表单形式 enctype="multipart/form-data"2、后台处理
说明:- 使用注解@MultipartConfig将一个Servlet标识为支持文件上传。
- Servlet3.0将multipart/form-data的POST请求封装成Part,通过Part对上传的文件进行操作。
package me.gacl.web.controller; import java.io.File; import java.io.IOException; import java.io.PrintWriter; import java.util.Collection; import javax.servlet.ServletException; import javax.servlet.annotation.MultipartConfig; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.Part; //使用@WebServlet配置UploadServlet的访问路径 @WebServlet(name="UploadServlet",urlPatterns="/UploadServlet") //使用注解@MultipartConfig将一个Servlet标识为支持文件上传 @MultipartConfig//标识Servlet支持文件上传 public class UploadServlet extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.setCharacterEncoding("utf-8"); response.setCharacterEncoding("utf-8"); response.setContentType("text/html;charset=utf-8"); //存储路径 String savePath = request.getServletContext().getRealPath("/WEB-INF/uploadFile"); //获取上传的文件集合 Collection<Part> parts = request.getParts(); //上传单个文件 if (parts.size()==1) { //Servlet3.0将multipart/form-data的POST请求封装成Part,通过Part对上传的文件进行操作。 //Part part = parts[0];//从上传的文件集合中获取Part对象 Part part = request.getPart("file");//通过表单file控件(<input type="file" name="file">)的名字直接获取Part对象 //Servlet3没有提供直接获取文件名的方法,需要从请求头中解析出来 //获取请求头,请求头的格式:form-data; name="file"; filename="snmp4j--api.zip" String header = part.getHeader("content-disposition"); //获取文件名 String fileName = getFileName(header); //把文件写到指定路径 part.write(savePath+File.separator+fileName); }else { //一次性上传多个文件 for (Part part : parts) {//循环处理上传的文件 //获取请求头,请求头的格式:form-data; name="file"; filename="snmp4j--api.zip" String header = part.getHeader("content-disposition"); //获取文件名 String fileName = getFileName(header); //把文件写到指定路径 part.write(savePath+File.separator+fileName); } } PrintWriter out = response.getWriter(); out.println("上传成功"); out.flush(); out.close(); } /** * 根据请求头解析出文件名 * 请求头的格式:火狐和google浏览器下:form-data; name="file"; filename="snmp4j--api.zip" * IE浏览器下:form-data; name="file"; filename="E:\snmp4j--api.zip" * @param header 请求头 * @return 文件名 */ public String getFileName(String header) { /** * String[] tempArr1 = header.split(";");代码执行完之后,在不同的浏览器下,tempArr1数组里面的内容稍有区别 * 火狐或者google浏览器下:tempArr1={form-data,name="file",filename="snmp4j--api.zip"} * IE浏览器下:tempArr1={form-data,name="file",filename="E:\snmp4j--api.zip"} */ String[] tempArr1 = header.split(";"); /** *火狐或者google浏览器下:tempArr2={filename,"snmp4j--api.zip"} *IE浏览器下:tempArr2={filename,"E:\snmp4j--api.zip"} */ String[] tempArr2 = tempArr1[2].split("="); //获取文件名,兼容各种浏览器的写法 String fileName = tempArr2[1].substring(tempArr2[1].lastIndexOf("\\")+1).replaceAll("\"", ""); return fileName; } public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.doGet(request, response); } }
二、common-fileupload 组件实现
参考文献:http://www.cnblogs.com/xdp-gacl/p/4200090.html
所需jar包:commons-fileupload-1.3.1.jar commons-io-2.5.jar
1、前台处理
文件上传方式使用表单形式 enctype="multipart/form-data"2、后台处理
注意事项:
- 为防止文件覆盖的现象发生,要为上传文件产生一个唯一的文件名
- 为防止一个目录下面出现太多文件,要使用hash算法打散存储
使用Apache文件上传组件处理文件上传步骤:
1)、创建一个DiskFileItemFactory工厂
设置工厂的缓冲区的大小,当上传的文件大小超过缓冲区的大小时,就会生成一个临时文件存放到指定的临时目录当中,如果不指定,那么缓冲区的大小默认是10KB,设置上传时生成的临时文件的保存目录。
2)、创建一个文件上传解析器
3)、判断提交上来的数据是否是上传表单的数据
4)、使用ServletFileUpload解析器解析上传数据,解析结果返回的是一个List<FileItem>集合,每一个FileItem对应一个Form表单的输入项
package me.gacl.web.controller; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.util.List; import java.util.UUID; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.commons.fileupload.FileItem; import org.apache.commons.fileupload.FileUploadBase; import org.apache.commons.fileupload.ProgressListener; import org.apache.commons.fileupload.disk.DiskFileItemFactory; import org.apache.commons.fileupload.servlet.ServletFileUpload; /** * @ClassName: UploadHandleServlet * @Description: TODO(这里用一句话描述这个类的作用) * @author: 孤傲苍狼 * @date: 2015-1-3 下午11:35:50 * */ public class UploadHandleServlet extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //得到上传文件的保存目录,将上传的文件存放于WEB-INF目录下,不允许外界直接访问,保证上传文件的安全 String savePath = this.getServletContext().getRealPath("/WEB-INF/upload"); //上传时生成的临时文件保存目录 String tempPath = this.getServletContext().getRealPath("/WEB-INF/temp"); File tmpFile = new File(tempPath); if (!tmpFile.exists()) { //创建临时目录 tmpFile.mkdir(); } //消息提示 String message = ""; try{ //使用Apache文件上传组件处理文件上传步骤: //1、创建一个DiskFileItemFactory工厂 DiskFileItemFactory factory = new DiskFileItemFactory(); //设置工厂的缓冲区的大小,当上传的文件大小超过缓冲区的大小时,就会生成一个临时文件存放到指定的临时目录当中。 factory.setSizeThreshold(1024*100);//设置缓冲区的大小为100KB,如果不指定,那么缓冲区的大小默认是10KB //设置上传时生成的临时文件的保存目录 factory.setRepository(tmpFile); //2、创建一个文件上传解析器 ServletFileUpload upload = new ServletFileUpload(factory); //监听文件上传进度 upload.setProgressListener(new ProgressListener(){ public void update(long pBytesRead, long pContentLength, int arg2) { System.out.println("文件大小为:" + pContentLength + ",当前已处理:" + pBytesRead); /** * 文件大小为:14608,当前已处理:4096 文件大小为:14608,当前已处理:7367 文件大小为:14608,当前已处理:11419 文件大小为:14608,当前已处理:14608 */ } }); //解决上传文件名的中文乱码 upload.setHeaderEncoding("UTF-8"); //3、判断提交上来的数据是否是上传表单的数据 if(!ServletFileUpload.isMultipartContent(request)){ //按照传统方式获取数据 return; } //设置上传单个文件的大小的最大值,目前是设置为1024*1024字节,也就是1MB upload.setFileSizeMax(1024*1024); //设置上传文件总量的最大值,最大值=同时上传的多个文件的大小的最大值的和,目前设置为10MB upload.setSizeMax(1024*1024*10); //4、使用ServletFileUpload解析器解析上传数据,解析结果返回的是一个List<FileItem>集合,每一个FileItem对应一个Form表单的输入项 List<FileItem> list = upload.parseRequest(request); for(FileItem item : list){ //如果fileitem中封装的是普通输入项的数据 if(item.isFormField()){ String name = item.getFieldName(); //解决普通输入项的数据的中文乱码问题 String value = item.getString("UTF-8"); //value = new String(value.getBytes("iso8859-1"),"UTF-8"); System.out.println(name + "=" + value); }else{//如果fileitem中封装的是上传文件 //得到上传的文件名称, String filename = item.getName(); System.out.println(filename); if(filename==null || filename.trim().equals("")){ continue; } //注意:不同的浏览器提交的文件名是不一样的,有些浏览器提交上来的文件名是带有路径的,如: c:\a\b\1.txt,而有些只是单纯的文件名,如:1.txt //处理获取到的上传文件的文件名的路径部分,只保留文件名部分 filename = filename.substring(filename.lastIndexOf("\\")+1); //得到上传文件的扩展名 String fileExtName = filename.substring(filename.lastIndexOf(".")+1); //如果需要限制上传的文件类型,那么可以通过文件的扩展名来判断上传的文件类型是否合法 System.out.println("上传的文件的扩展名是:"+fileExtName); //获取item中的上传文件的输入流 InputStream in = item.getInputStream(); //得到文件保存的名称 String saveFilename = makeFileName(filename); //得到文件的保存目录 String realSavePath = makePath(saveFilename, savePath); //创建一个文件输出流 FileOutputStream out = new FileOutputStream(realSavePath + "\\" + saveFilename); //创建一个缓冲区 byte buffer[] = new byte[1024]; //判断输入流中的数据是否已经读完的标识 int len = 0; //循环将输入流读入到缓冲区当中,(len=in.read(buffer))>0就表示in里面还有数据 while((len=in.read(buffer))>0){ //使用FileOutputStream输出流将缓冲区的数据写入到指定的目录(savePath + "\\" + filename)当中 out.write(buffer, 0, len); } //关闭输入流 in.close(); //关闭输出流 out.close(); //删除处理文件上传时生成的临时文件 //item.delete(); message = "文件上传成功!"; } } }catch (FileUploadBase.FileSizeLimitExceededException e) { e.printStackTrace(); request.setAttribute("message", "单个文件超出最大值!!!"); request.getRequestDispatcher("/message.jsp").forward(request, response); return; }catch (FileUploadBase.SizeLimitExceededException e) { e.printStackTrace(); request.setAttribute("message", "上传文件的总的大小超出限制的最大值!!!"); request.getRequestDispatcher("/message.jsp").forward(request, response); return; }catch (Exception e) { message= "文件上传失败!"; e.printStackTrace(); } request.setAttribute("message",message); request.getRequestDispatcher("/message.jsp").forward(request, response); } /** * @Method: makeFileName * @Description: 生成上传文件的文件名,文件名以:uuid+"_"+文件的原始名称 * @Anthor:孤傲苍狼 * @param filename 文件的原始名称 * @return uuid+"_"+文件的原始名称 */ private String makeFileName(String filename){ //2.jpg //为防止文件覆盖的现象发生,要为上传文件产生一个唯一的文件名 return UUID.randomUUID().toString() + "_" + filename; } /** * 为防止一个目录下面出现太多文件,要使用hash算法打散存储 * @Method: makePath * @Description: * @Anthor:孤傲苍狼 * * @param filename 文件名,要根据文件名生成存储目录 * @param savePath 文件存储路径 * @return 新的存储目录 */ private String makePath(String filename,String savePath){ //得到文件名的hashCode的值,得到的就是filename这个字符串对象在内存中的地址 int hashcode = filename.hashCode(); int dir1 = hashcode&0xf; //0--15 int dir2 = (hashcode&0xf0)>>4; //0-15 //构造新的保存目录 String dir = savePath + "\\" + dir1 + "\\" + dir2; //upload\2\3 upload\3\5 //File既可以代表文件也可以代表目录 File file = new File(dir); //如果目录不存在 if(!file.exists()){ //创建目录 file.mkdirs(); } return dir; } public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } }
三、springmvc 实现
参考文献:http://www.cnblogs.com/qixiaoyizhan/p/5860224.html涉及的接口:
(I)
public interface MultipartFile extends InputStreamSource
A representation of an uploaded file received in a multipart request.
The file contents are either stored in memory or temporarily on disk. In either case, the user is responsible for copying file contents to a session-level or persistent store as and if desired. The temporary storages will be cleared at the end of request processing.
(II)
public interface MultipartHttpServletRequest extends HttpServletRequest,MultipartRequest
Provides additional methods for dealing with multipart content within a servlet request, allowing to access uploaded files. Implementations also need to override the standard ServletRequest methods for parameter access, making multipart parameters available.
A concrete implementation is DefaultMultipartHttpServletRequest. As an intermediate step,AbstractMultipartHttpServletRequest
can be subclassed.
注:最后也是通过MultipartRequest获取MultipartFile来实现。
(III)
public class CommonsMultipartResolver extends CommonsFileUploadSupport implementsMultipartResolver, ServletContextAware
Servlet-based MultipartResolver implementation for Apache Commons FileUpload 1.2 or above.
Provides "maxUploadSize", "maxInMemorySize" and "defaultEncoding" settings as bean properties (inherited from CommonsFileUploadSupport). See corresponding ServletFileUpload / DiskFileItemFactory properties
("sizeMax", "sizeThreshold", "headerEncoding") for details in terms of defaults and accepted values.
Saves temporary files to the servlet container's temporary directory. Needs to be initialized either by an application context or via the constructor that takes a ServletContext (for standalone usage).
1、springmvc文件配置
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"> <property name="maxUploadSize"> <value>104857600</value> </property> <property name="maxInMemorySize"> <value>4096</value> </property> <property name="defaultEncoding"> <value>utf-8</value> </property> </bean>
2、后台代码
package Utils; import org.springframework.web.multipart.MultipartFile; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.*; import java.text.SimpleDateFormat; import java.util.Date; import java.util.UUID; /** * Author:qixiao * Time:2016-9-2 23:47:51 */ public final class Files_Utils_DG { /** * private constructor method that make class can not be instantiation */ private Files_Utils_DG() { throw new Error("The class Cannot be instance !"); } /** * spring mvc files Upload method (transferTo method) * MultipartFile use TransferTo method upload * * @param request HttpServletRequest * @param multipartFile MultipartFile(spring) * @param filePath filePath example "/files/Upload" * @return */ public static String FilesUpload_transferTo_spring(HttpServletRequest request, MultipartFile multipartFile, String filePath) { if (multipartFile != null) { //get files suffix String suffix = multipartFile.getOriginalFilename().substring(multipartFile.getOriginalFilename().lastIndexOf(".")); //filePath+fileName the complex file Name String absolutePath = getAndSetAbsolutePath(request, filePath, suffix); //return relative Path String relativePath = getRelativePath(filePath, suffix); try { //save file multipartFile.transferTo(new File(absolutePath)); //return relative Path return relativePath; } catch (IOException e) { e.printStackTrace(); return null; } } else return null; } /** * user stream type save files * @param request HttpServletRequest * @param multipartFile MultipartFile support CommonsMultipartFile file * @param filePath filePath example "/files/Upload" * @return */ public static String FilesUpload_stream(HttpServletRequest request,MultipartFile multipartFile,String filePath) { if (multipartFile != null) { //get files suffix String suffix = multipartFile.getOriginalFilename().substring(multipartFile.getOriginalFilename().lastIndexOf(".")); //filePath+fileName the complex file Name String absolutePath = getAndSetAbsolutePath(request, filePath, suffix); //return relative Path String relativePath = getRelativePath(filePath, suffix); try{ InputStream inputStream = multipartFile.getInputStream(); FileOutputStream fileOutputStream = new FileOutputStream(absolutePath); byte buffer[] = new byte[4096]; //create a buffer long fileSize = multipartFile.getSize(); if(fileSize<=buffer.length){//if fileSize < buffer buffer = new byte[(int)fileSize]; } int line =0; while((line = inputStream.read(buffer)) >0 ) { fileOutputStream.write(buffer,0,line); } fileOutputStream.close(); inputStream.close(); return relativePath; }catch (Exception e){ e.printStackTrace(); } } else return null; return null; } /** * @param request HttpServletRequest * @param response HttpServletResponse * @param filePath example "/filesOut/Download/mst.txt" * @return */ public static void FilesDownload_stream(HttpServletRequest request, HttpServletResponse response, String filePath) { //get server path (real path) String realPath = request.getSession().getServletContext().getRealPath(filePath); File file = new File(realPath); String filenames = file.getName(); InputStream inputStream; try { inputStream = new BufferedInputStream(new FileInputStream(file)); byte[] buffer = new byte[inputStream.available()]; inputStream.read(buffer); inputStream.close(); response.reset(); // 先去掉文件名称中的空格,然后转换编码格式为utf-8,保证不出现乱码,这个文件名称用于浏览器的下载框中自动显示的文件名 response.addHeader("Content-Disposition", "attachment;filename=" + new String(filenames.replaceAll(" ", "").getBytes("utf-8"), "iso8859-1")); response.addHeader("Content-Length", "" + file.length()); OutputStream os = new BufferedOutputStream(response.getOutputStream()); response.setContentType("application/octet-stream"); os.write(buffer);// 输出文件 os.flush(); os.close(); } catch (Exception e) { e.printStackTrace(); } } //------------------------------------------------------------------------------- //return server absolute path(real path) public static String getServerPath(HttpServletRequest request, String filePath) { return request.getSession().getServletContext().getRealPath(filePath); } //return a dir that named date of today ; example:20160912 public static String getDataPath() { return new SimpleDateFormat("yyyyMMdd").format(new Date()); } //check if the path has exist if not create it public static void checkDir(String savePath) { File dir = new File(savePath); if (!dir.exists() || !dir.isDirectory()) { dir.mkdir(); } } //return an UUID Name parameter (suffix cover '.') example: ".jpg"、".txt" public static String getUUIDName(String suffix) { return UUID.randomUUID().toString() + suffix;// make new file name } //return server absolute path(real path) the style is “server absolute path/DataPath/UUIDName”filePath example "/files/Upload" public static String getAndSetAbsolutePath(HttpServletRequest request, String filePath, String suffix) { String savePath = getServerPath(request, filePath) + File.separator + getDataPath() + File.separator;//example:F:/qixiao/files/Upload/20160912/ checkDir(savePath);//check if the path has exist if not create it return savePath + getUUIDName(suffix); } //get the relative path of files style is “/filePath/DataPath/UUIDName”filePath example "/files/Upload" public static String getRelativePath(String filePath, String suffix) { return filePath + File.separator + getDataPath() + File.separator + getUUIDName(suffix);//example:/files/Upload/20160912/ } }
Controller代码:
package HelloSpringMVC.controller; import Utils.Files_Utils_DG; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.MultipartHttpServletRequest; import org.springframework.web.multipart.commons.CommonsMultipartResolver; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.util.Iterator; /** * Created by qixiao on 2016/8/30. */ @Controller @RequestMapping(value = "/FileUpload/*") public class FileUploadController { /* * 方式一 * 采用 fileUpload_multipartFile , file.transferTo 来保存上传的文件 */ @RequestMapping(value = "fileUpload_multipartFile") @ResponseBody public String fileUpload_multipartFile(HttpServletRequest request, @RequestParam("file_upload") MultipartFile multipartFile) { //调用保存文件的帮助类进行保存文件,并返回文件的相对路径 String filePath = Files_Utils_DG.FilesUpload_transferTo_spring(request, multipartFile, "/files/upload"); return "{'TFMark':'true','Msg':'upload success !','filePath':'" + filePath + "'}"; } /* * 方式二 * 采用 fileUpload_multipartRequest file.transferTo 来保存上传文件 * 参数不写 MultipartFile multipartFile 在request请求里面直接转成multipartRequest,从multipartRequest中获取到文件流 */ @RequestMapping(value = "fileUpload_multipartRequest") @ResponseBody public String fileUpload_multipartRequest(HttpServletRequest request) { //将request转成MultipartHttpServletRequest MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) request; //页面控件的文件流,对应页面控件 input file_upload MultipartFile multipartFile = multipartRequest.getFile("file_upload"); //调用保存文件的帮助类进行保存文件,并返回文件的相对路径 String filePath = Files_Utils_DG.FilesUpload_transferTo_spring(request, multipartFile, "/files/upload"); return "{'TFMark':'true','Msg':'upload success !','filePath':'" + filePath + "'}"; } /* * 方式三 * 采用 CommonsMultipartResolver file.transferTo 来保存上传文件 * 自动扫描全部的input表单 */ @RequestMapping(value = "fileUpload_CommonsMultipartResolver") @ResponseBody public String fileUpload_CommonsMultipartResolver(HttpServletRequest request) { //将当前上下文初始化给 CommonsMultipartResolver (多部分解析器) CommonsMultipartResolver multipartResolver = new CommonsMultipartResolver(request.getSession().getServletContext()); //检查form中是否有enctype="multipart/form-data" if (multipartResolver.isMultipart(request)) { //将request变成多部分request MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) request; //获取multiRequest 中所有的文件名 Iterator iter = multipartRequest.getFileNames(); while (iter.hasNext()) { //一次遍历所有文件 MultipartFile multipartFile = multipartRequest.getFile(iter.next().toString()); //调用保存文件的帮助类进行保存文件,并返回文件的相对路径 String fileName = Files_Utils_DG.FilesUpload_transferTo_spring(request, multipartFile, "/files/upload"); System.out.println(fileName); } } return "upload success ! "; } /* * 方式四 * 通过流的方式上传文件 */ @RequestMapping("fileUpload_stream") @ResponseBody public String upFile(HttpServletRequest request, @RequestParam("file_upload") MultipartFile multipartFile){ String filePath= Files_Utils_DG.FilesUpload_stream(request,multipartFile,"/files/upload"); return "{'TFMark':'true','Msg':'upload success !','filePath':'" + filePath + "'}"; } /* * 方式五 * 采用 fileUpload_ajax , file.transferTo 来保存上传的文件 异步 */ @RequestMapping(value = "fileUpload_ajax",method = RequestMethod.POST,produces = {"application/json;charset=UTF-8"}) @ResponseBody public String fileUpload_ajax(HttpServletRequest request, @RequestParam("file_AjaxFile") MultipartFile multipartFile) { //调用保存文件的帮助类进行保存文件,并返回文件的相对路径 String filePath = Files_Utils_DG.FilesUpload_transferTo_spring(request, multipartFile, "/files/upload"); return "{'TFMark':'true','Msg':'upload success !','filePath':'" + filePath + "'}"; } /* * 多文件上传 * 采用 MultipartFile[] multipartFile 上传文件方法 */ @RequestMapping(value = "fileUpload_spring_list") @ResponseBody public String fileUpload_spring_list(HttpServletRequest request, @RequestParam("file_upload") MultipartFile[] multipartFile) { //判断file数组不能为空并且长度大于0 if (multipartFile != null && multipartFile.length > 0) { //循环获取file数组中得文件 try { for (int i = 0; i < multipartFile.length; i++) { MultipartFile file = multipartFile[i]; //保存文件 String fileName = Files_Utils_DG.FilesUpload_transferTo_spring(request, file, "/files/upload"); System.out.println(fileName); } return "{'TFMark':'true','Msg':'upload success !'}"; } catch (Exception ee) { return "{'TFMark':'false','Msg':'参数传递有误!'}"; } } return "{'TFMark':'false','Msg':'参数传递有误!'}"; } /** * 文件下载 * * @param response */ @RequestMapping(value = "fileDownload_servlet") public void fileDownload_servlet(HttpServletRequest request, HttpServletResponse response) { Files_Utils_DG.FilesDownload_stream(request,response,"/files/download/mst.txt"); } }