准备工作
1.需要form表单,input type=file 组件
2.form表单方法post
3.form表单必须要设置enctype=multipart/form-data,可以上传除了表单数据(文本)以外的数据
4.如果要使用JavaBean来封装的话,那么这个file属性的组件的name要和封装的对象中的属性相同
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <form action="/servlet/upload" enctype="multipart/form-data" method="post"> <input type="text" name="username"><br> <input type="password" name="password"><br> <input type="file" name="image"><br> <input type="submit"> </form> </body> </html>
此时注意,由于设置了enctype=multipart/form-data导致请求体报文的格式改变,那么就不能使用普通的读取方式了,否则会导致在二进制文件中写入字符而报错
------WebKitFormBoundaryrYApx85v3VoXeztu Content-Disposition: form-data; name="username" admin ------WebKitFormBoundaryrYApx85v3VoXeztu Content-Disposition: form-data; name="password" admin123 ------WebKitFormBoundaryrYApx85v3VoXeztu Content-Disposition: form-data; name="image"; filename="1.txt" Content-Type: text/plain hello 你好 ------WebKitFormBoundaryrYApx85v3VoXeztu--
必须要剔除分隔符的部分,需要引入第三方组件来解决
使用组件完成文件上传
使用组件整体的思路:
- 首先需要判断请求是否包含上传的文件,否则使用该组件没有意义
boolean multipartContent = ServletFileUpload.isMultipartContent(request);
- 创建工厂,设置文件缓存,获取upload对象
DiskFileItemFactory factory = new DiskFileItemFactory();//创建工厂 ServletContext servletContext = getServletContext();//获取Context域,用于缓存 File repository = (File)servletContext.getAttribute("javax.servlet.context.tempdir");//创建一个缓存的中转站 factory.setRepository(repository); ServletFileUpload upload = new ServletFileUpload(factory);//利用工厂创建一个upload对象,功能是帮我们处理请求报文中上传的部分 upload.setHeaderEncoding("utf-8");
- 使用upload对象对请求报文进行解析,将每一个input框中的内容解析成FileItem,得到的是一个List
- item实际上就是input中上传的文本参数或者是文件,根据不同的文件来实现具体的业务逻辑,分发给不同的方法来进行处理
try { List<FileItem> items = upload.parseRequest(request);//处理请求报文对象 for (FileItem item : items) { //item本质上是页面上的input框中包含的文本、文件 if (item.isFormField()){ //如果是普通的表单数据,作出表单数据的处理 processFormField(item); }else { //处理上传文件 processUploadFile(item); } } } catch (FileUploadException e) { e.printStackTrace(); }
- 处理表单数据的方法
private void processFormField(FileItem item) { //具体处理表单数据的方法 String fieldName = item.getFieldName();//相当于获取请求参数中的key String value = null; //获取value try { value = item.getString("utf-8");//处理表单数据的中文乱码 } catch (UnsupportedEncodingException e) { e.printStackTrace(); } System.out.println(fieldName+":"+value); }
- 处理上传数据
private void processUploadFile(FileItem item) { String fieldName = item.getFieldName();//文件属性名 String fileName = item.getName();//文件名 // String contentType = item.getContentType();//获取文件类型名mni // long size = item.getSize();//文件大小 // boolean inMemory = item.isInMemory();//是否在内存中 String realPath = getServletContext().getRealPath("image/" + fileName);//获取该文件要存放在服务器硬盘上的绝对路径 File file = new File(realPath); if (!file.getParentFile().exists()){ //如果该文件的父路径不存在,那么需要进行创建 file.getParentFile().mkdirs(); } try { item.write(file); //直接将该item写入硬盘 } catch (Exception e) { e.printStackTrace(); } }
中文乱码
表单中文乱码
//表单中文乱码的解决方案 value = item.getString("utf-8");
setEncoding失效,因为此时文件的格式已经变了,之前都是key=value
上传的文件名中文乱码
//设置上传的文件名中文乱码 upload.setHeaderEncoding("utf-8");
将数据保存到Java Bean
思路:在具体分发的处理方法中,传入一个map对象,用于保存键值对,键可以设置为JAVA Bean中的属性名,值设置为一个object,用于保存具体的参数类型;最后在所有的Item处理完毕后,将map使用BeanUtils进行处理,就完成了封装
package com.fh.upload; import org.apache.commons.beanutils.BeanUtils; import org.apache.commons.fileupload.FileItem; import org.apache.commons.fileupload.FileUploadException; import org.apache.commons.fileupload.disk.DiskFileItemFactory; import org.apache.commons.fileupload.servlet.ServletFileUpload; import javax.servlet.ServletContext; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.File; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.lang.reflect.InvocationTargetException; import java.util.HashMap; import java.util.List; import java.util.Map; @WebServlet("/upload2") public class UploadServlet2 extends HttpServlet { protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("text/html;charset=utf-8"); // request.setCharacterEncoding("utf-8"); boolean multipartContent = ServletFileUpload.isMultipartContent(request);//判断是否有上传的文件 if (!multipartContent){ //如果不存在该文件,那就直接返回 System.out.println("你没有上传文件!"); return; } DiskFileItemFactory factory = new DiskFileItemFactory();//创建工厂 ServletContext servletContext = getServletContext();//获取Context域,用于缓存 File repository = (File)servletContext.getAttribute("javax.servlet.context.tempdir");//创建一个缓存的中转站 factory.setRepository(repository); ServletFileUpload upload = new ServletFileUpload(factory);//利用工厂创建一个upload对象,功能是帮我们处理请求报文中上传的部分 upload.setHeaderEncoding("utf-8"); //解决上传文件名的中文乱码 Map<String,Object> map = new HashMap<>(); User user = new User(); try { List<FileItem> items = upload.parseRequest(request);//处理请求报文对象 for (FileItem item : items) { //item本质上是页面上的input框中包含的文本、文件 if (item.isFormField()){ //如果是普通的表单数据,作出表单数据的处理 processFormField(item,map); }else { //处理上传文件 processUploadFile(item,map); } } } catch (FileUploadException e) { e.printStackTrace(); } try { BeanUtils.populate(user,map); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } getServletContext().setAttribute("user",user);//将user传到共享域 response.setHeader("refresh","6;url="+request.getContextPath()+"/view"); } private void processUploadFile(FileItem item, Map<String, Object> map) { String fieldName = item.getFieldName();//文件属性名 String fileName = item.getName();//文件名 // String contentType = item.getContentType();//获取文件类型名mni // long size = item.getSize();//文件大小 // boolean inMemory = item.isInMemory();//是否在内存中 String relativePath = "image/"+fileName; String realPath = getServletContext().getRealPath(relativePath);//获取该文件要存放在服务器硬盘上的绝对路径 File file = new File(realPath); if (!file.getParentFile().exists()){ //如果该文件的父路径不存在,那么需要进行创建 file.getParentFile().mkdirs(); } try { item.write(file); //直接将该item写入硬盘 map.put("img",relativePath); } catch (Exception e) { e.printStackTrace(); } } private void processFormField(FileItem item, Map<String, Object> map) { //具体处理表单数据的方法 String fieldName = item.getFieldName();//相当于获取请求参数中的key String value = null; //获取value try { value = item.getString("utf-8");//处理表单数据的中文乱码 } catch (UnsupportedEncodingException e) { e.printStackTrace(); } map.put(fieldName,value); } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { } }
工具类
package com.fh.upload; import org.apache.commons.fileupload.FileItem; import org.apache.commons.fileupload.FileUploadException; import org.apache.commons.fileupload.disk.DiskFileItemFactory; import org.apache.commons.fileupload.servlet.ServletFileUpload; import javax.servlet.ServletContext; import javax.servlet.http.HttpServletRequest; import java.io.File; import java.io.UnsupportedEncodingException; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.UUID; public class FileUploadUtil { public static Map<String, Object> parseRequest(HttpServletRequest request){ //解析请求报文,终极目标是返回一个该请求报文中所有参数的列表,并完成文件上传的工作 DiskFileItemFactory factory = new DiskFileItemFactory();//创建工厂 ServletContext servletContext = request.getServletContext();//获取Context域,用于缓存 File repository = (File)servletContext.getAttribute("javax.servlet.context.tempdir");//创建一个缓存的中转站 factory.setRepository(repository); ServletFileUpload upload = new ServletFileUpload(factory);//利用工厂创建一个upload对象,功能是帮我们处理请求报文中上传的部分 upload.setHeaderEncoding("utf-8"); //解决上传文件名的中文乱码 Map<String,Object> map = new HashMap<>(); try { List<FileItem> items = upload.parseRequest(request);//处理请求报文对象 for (FileItem item : items) { //item本质上是页面上的input框中包含的文本、文件 if (item.isFormField()){ //如果是普通的表单数据,作出表单数据的处理 processFormField(item,map); }else { //处理上传文件 processUploadFile(item,map,request); } } } catch (FileUploadException e) { e.printStackTrace(); } return map; } private static void processUploadFile(FileItem item, Map<String, Object> map, HttpServletRequest request) { String fieldName = item.getFieldName();//文件属性名 String fileName = item.getName();//文件名 fileName = UUID.randomUUID().toString() + "-" + fileName; //随机生成一个文件名,防止重复 //对文件上传进行散列 int hashCode = fileName.hashCode(); String hexString = Integer.toHexString(hashCode); //生成一个16进制的哈希值,每一位都分别创建子目录,完成散列 char[] chars = hexString.toCharArray(); String basePath = fieldName; //总的一个父路径,所有下属的文件夹都放在这个父路径里,根据不同的文件类型生成 for (char aChar : chars) { basePath = basePath + "/" + aChar; } String relativePath = basePath + "/" + fileName; //最终用文件总的父路径拼接文件名,那么就得到了文件要存放在硬盘上的相对路径 //需要根据相对路径获取存储到服务器磁盘的绝对路径,显然需要一个request对象 String realPath = request.getServletContext().getRealPath(relativePath); File file = new File(realPath); if (!file.getParentFile().exists()){ //父路径不存在,那就创建 file.getParentFile().mkdirs(); } try { item.write(file); map.put(fieldName,relativePath); } catch (Exception e) { e.printStackTrace(); } } private static void processFormField(FileItem item, Map<String, Object> map) { String name = item.getFieldName(); String value = null; try { //表单中文乱码的解决方案 value = item.getString("utf-8"); map.put(name, value); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } } }