准备工作
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();
}
}
}