FileUpload
就是将本地的文件上传到服务器上面去。就是利用请求报文,将文件的二进制数据放在里面,然后传输到服务器,服务器进行解析,获取里面的资源
二进制数据是在哪里?请求体
哪个API是获取请求体的?
request.getInputStream()
文件上传准备
1.form表单 method = post
2.input type=file
Content-Length: 37
该请求头代表了什么意思?
Content-Length: 11
遇到的第一个问题
仅上传文件名,不会上传文件的数据
解决方案:form表单需要设置enctype=multipart/form-data
处理文件的逻辑
package com.cskaoyan.upload;
import javax.servlet.ServletException;
import javax.servlet.ServletInputStream;
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.FileOutputStream;
import java.io.IOException;
@WebServlet("/upload")
public class UploadServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//准备工作就绪之后,点击form表单的提交,浏览器会自动帮我们将文件的信息写入到请求体中
//服务器拿到请求报文,会帮我们进行解析处理,将请求体封装到指定的API中,我们只需要去调用对应的API即可
ServletInputStream inputStream = request.getInputStream();
String realPath = getServletContext().getRealPath("upload/1.png");
File file = new File(realPath);
if(!file.getParentFile().exists()){
//当前file文件的父级目录如果不存在
file.getParentFile().mkdirs();
}
FileOutputStream outputStream = new FileOutputStream(file);
int length = 0;
byte[] bytes = new byte[1024];
while ((length = inputStream.read(bytes)) != -1){
outputStream.write(bytes, 0, length);
}
outputStream.close();
inputStream.close();
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
}
}
文件经过上传之后,多出了很多内容;如果是文本文件,就会多出了很多东西;如果是二进制数据,这些内容进入到文件内部,会直接导致文件的损坏。
这些东西哪来的?
面临的问题二
是因为如果我们需要进行文件上传,那么就必须要使用enctype=multipart/form-data,但是使用了该enctype之后,请求体里面会多出来很多的字符。对于二进制数据来说,直接导致文件损坏,对于文本数据来说,多出很多分隔符
面临的问题三
之前可以正常获取请求参数的API也不再适用了,无法获取到请求参数,请求参数会和文件一起处理到文件中。
原因在于获取请求参数的API只能够获取key=value&key=value型数据,如果数据结构发生了变化,那么就无法获取到了。
总结:
目前面临的诸多问题,均和引入了enctype有关,但是又不能不引入,因为如果不引入,那么只会上传文件的名称,不会上传文件的内容;需要手动地将这些分割字符剔除掉。
不要重复造轮子。
commons-fileupload jar包
引入组件之前的这些操作以及遇到的问题,是需要大家掌握的。
文件上传组件的引入(了解使用)
看着API文档,来解决你的实际问题即可。
package com.cskaoyan.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.ServletException;
import javax.servlet.ServletInputStream;
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.FileOutputStream;
import java.io.IOException;
import java.util.Iterator;
import java.util.List;
@WebServlet("/upload2")
public class UploadServlet2 extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//看着API文档,一步一步完成我们的功能即可
//这行代码的含义是判断当前的请求是否是multipart/form-data
response.setContentType("text/html;charset=utf-8");
boolean multipartContent = ServletFileUpload.isMultipartContent(request);
if(!multipartContent){
response.getWriter().println("当前请求不含上传的文件");
return;
}
DiskFileItemFactory factory = new DiskFileItemFactory();
File repository = (File) getServletContext().getAttribute("javax.servlet.context.tempdir");
factory.setRepository(repository);
ServletFileUpload upload = new ServletFileUpload(factory);
try {
//items其实就是对于前端提交过来啊input的封装
//提交过来多少个input,那么这边items就有多少个
List<FileItem> items = upload.parseRequest(request);
Iterator<FileItem> iterator = items.iterator();
while (iterator.hasNext()){
FileItem item = iterator.next();
if(item.isFormField()){
//当前item是常规的form表单
processFormField(item);
}else {
//当前item是上传的文件
processUploadedFile(item);
}
}
} catch (FileUploadException e) {
e.printStackTrace();
}
}
/**
* 处理上传的文件逻辑
* @param item
*/
private void processUploadedFile(FileItem item) {
String fieldName = item.getFieldName();
String fileName = item.getName();
// String contentType = item.getContentType();
// boolean isInMemory = item.isInMemory();
// long sizeInBytes = item.getSize();
// System.out.println("File " + fieldName + ":" + fileName + ":" + contentType + ":" + isInMemory + ":" + sizeInBytes);
String realPath = getServletContext().getRealPath("upload/" + fileName);
File file = new File(realPath);
if(!file.getParentFile().exists()){
file.getParentFile().mkdirs();
}
try {
item.write(file);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 处理常规form表单的逻辑
* @param item
*/
private void processFormField(FileItem item) {
String name = item.getFieldName();
String value = item.getString();
System.out.println("formField " + name + ":" + value);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
}
}
中文乱码问题
普通form表单数据乱码
//这行代码针对文件上传时form表单数据乱码也失效了,也不再适用了
request.setCharacterEncoding("utf-8");
String getString(String encoding)
throws UnsupportedEncodingException
Returns the contents of the file item as a String, using the specified encoding. This method uses get()
to retrieve the contents of the item.
- Parameters:
encoding
- The character encoding to use. - Returns:
The contents of the item, as a string. - Throws:
UnsupportedEncodingException
- if the requested character encoding is not available.
上传的文件名有没有可能乱码
//上传的文件名如果有中文的乱码问题
upload.setHeaderEncoding("utf-8");
封装数据到Bean(掌握)
package com.cskaoyan.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.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.util.Iterator;
import java.util.List;
@WebServlet("/upload3")
public class UploadServlet3 extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//看着API文档,一步一步完成我们的功能即可
//这行代码的含义是判断当前的请求是否是multipart/form-data
request.setCharacterEncoding("utf-8");
response.setContentType("text/html;charset=utf-8");
boolean multipartContent = ServletFileUpload.isMultipartContent(request);
if(!multipartContent){
response.getWriter().println("当前请求不含上传的文件");
return;
}
DiskFileItemFactory factory = new DiskFileItemFactory();
File repository = (File) getServletContext().getAttribute("javax.servlet.context.tempdir");
factory.setRepository(repository);
ServletFileUpload upload = new ServletFileUpload(factory);
//上传的文件名如果有中文的乱码问题
upload.setHeaderEncoding("utf-8");
// 1024 bytes
//upload.setSizeMax(1024);
try {
//items其实就是对于前端提交过来啊input的封装
//提交过来多少个input,那么这边items就有多少个
List<FileItem> items = upload.parseRequest(request);
Iterator<FileItem> iterator = items.iterator();
while (iterator.hasNext()){
FileItem item = iterator.next();
if(item.isFormField()){
//当前item是常规的form表单
processFormField(item);
}else {
//当前item是上传的文件
processUploadedFile(item);
}
}
} catch (FileUploadException e) {
e.printStackTrace();
}
}
/**
* 处理上传的文件逻辑
* ReflectionUtils.toBean(Object o, String key, String value);
* (user, username, admin)
* (user, password, admin123)
* (user, image, upload/1.jpg)
* 有兴趣的同学可以课后实现它
* 反射的代码有难度,能不能反射这部分代码交给BeanUtils来做呢
* BeanUtils.populate(user, map);
* @param item
*/
private void processUploadedFile(FileItem item) {
String fieldName = item.getFieldName();
String fileName = item.getName();
// String contentType = item.getContentType();
// boolean isInMemory = item.isInMemory();
// long sizeInBytes = item.getSize();
// System.out.println("File " + fieldName + ":" + fileName + ":" + contentType + ":" + isInMemory + ":" + sizeInBytes);
String realPath = getServletContext().getRealPath("upload/" + fileName);
File file = new File(realPath);
if(!file.getParentFile().exists()){
file.getParentFile().mkdirs();
}
try {
item.write(file);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 处理常规form表单的逻辑
* @param item
*/
private void processFormField(FileItem item) {
//返回的就是input的name属性
//根据不同的name属性,判断,根据不同的name属性值,调用不同的set方法,完成赋值,这个方法比较臃肿一些
String name = item.getFieldName();
String value = null;
try {
value = item.getString("utf-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
System.out.println("formField " + name + ":" + value);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
}
}
改进
package com.cskaoyan.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.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.Iterator;
import java.util.List;
import java.util.Map;
@WebServlet("/upload3")
public class UploadServlet3 extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//看着API文档,一步一步完成我们的功能即可
//这行代码的含义是判断当前的请求是否是multipart/form-data
request.setCharacterEncoding("utf-8");
response.setContentType("text/html;charset=utf-8");
boolean multipartContent = ServletFileUpload.isMultipartContent(request);
if(!multipartContent){
response.getWriter().println("当前请求不含上传的文件");
return;
}
DiskFileItemFactory factory = new DiskFileItemFactory();
File repository = (File) getServletContext().getAttribute("javax.servlet.context.tempdir");
factory.setRepository(repository);
ServletFileUpload upload = new ServletFileUpload(factory);
//上传的文件名如果有中文的乱码问题
upload.setHeaderEncoding("utf-8");
// 1024 bytes
//upload.setSizeMax(1024);
User user = new User();
Map<String, Object> map = new HashMap<>();
try {
//items其实就是对于前端提交过来啊input的封装
//提交过来多少个input,那么这边items就有多少个
List<FileItem> items = upload.parseRequest(request);
Iterator<FileItem> iterator = items.iterator();
while (iterator.hasNext()){
FileItem item = iterator.next();
if(item.isFormField()){
//当前item是常规的form表单
processFormField(item, map);
}else {
//当前item是上传的文件
processUploadedFile(item, map);
}
}
BeanUtils.populate(user, map);
System.out.println(user);
} catch (FileUploadException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
/**
* 处理上传的文件逻辑
* ReflectionUtils.toBean(Object o, String key, String value);
* (user, username, admin)
* (user, password, admin123)
* (user, image, upload/1.jpg)
* 有兴趣的同学可以课后实现它
* 反射的代码有难度,能不能反射这部分代码交给BeanUtils来做呢
* BeanUtils.populate(user, map);
* @param item
* @param map
*/
private void processUploadedFile(FileItem item, Map<String, Object> map) {
String fieldName = item.getFieldName();
String fileName = item.getName();
// String contentType = item.getContentType();
// boolean isInMemory = item.isInMemory();
// long sizeInBytes = item.getSize();
// System.out.println("File " + fieldName + ":" + fileName + ":" + contentType + ":" + isInMemory + ":" + sizeInBytes);
String uploadPath = "upload/" + fileName;
String realPath = getServletContext().getRealPath(uploadPath);
File file = new File(realPath);
if(!file.getParentFile().exists()){
file.getParentFile().mkdirs();
}
try {
item.write(file);
map.put(fieldName, uploadPath);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 处理常规form表单的逻辑
* @param item
* @param map
*/
private void processFormField(FileItem item, Map<String, Object> map) {
//返回的就是input的name属性
String name = item.getFieldName();
String value = null;
try {
value = item.getString("utf-8");
map.put(name, value);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
System.out.println("formField " + name + ":" + value);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
}
}
进一步优化
假如今后我们还需要进行商品发布、用户注册等其他行为,这个时候也需要使用文件上传。需要写在其他servlet中,再写一遍把
能不呢将一些代码抽提出来,形成一个工具类,后面的话可以复用。
package com.cskaoyan.upload.utils;
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.http.HttpServletRequest;
import java.io.File;
import java.io.UnsupportedEncodingException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
public class FileUploadUtils {
public static Map<String, Object> parseRequest(HttpServletRequest request){
DiskFileItemFactory factory = new DiskFileItemFactory();
File repository = (File) request.getServletContext().getAttribute("javax.servlet.context.tempdir");
factory.setRepository(repository);
Map<String, Object> map = new HashMap<>();
ServletFileUpload upload = new ServletFileUpload(factory);
//上传的文件名如果有中文的乱码问题
upload.setHeaderEncoding("utf-8");
// 1024 bytes
//upload.setSizeMax(1024);
try {
//items其实就是对于前端提交过来啊input的封装
//提交过来多少个input,那么这边items就有多少个
List<FileItem> items = upload.parseRequest(request);
Iterator<FileItem> iterator = items.iterator();
while (iterator.hasNext()){
FileItem item = iterator.next();
if(item.isFormField()){
//当前item是常规的form表单
processFormField(item, map);
}else {
//当前item是上传的文件
processUploadedFile(item, map, request);
}
}
} catch (FileUploadException e) {
e.printStackTrace();
}
return map;
}
private static void processUploadedFile(FileItem item, Map<String, Object> map, HttpServletRequest request) {
String fieldName = item.getFieldName();
String fileName = item.getName();
// String contentType = item.getContentType();
// boolean isInMemory = item.isInMemory();
// long sizeInBytes = item.getSize();
// System.out.println("File " + fieldName + ":" + fileName + ":" + contentType + ":" + isInMemory + ":" + sizeInBytes);
String uploadPath = "upload/" + fileName;
String realPath = request.getServletContext().getRealPath(uploadPath);
File file = new File(realPath);
if(!file.getParentFile().exists()){
file.getParentFile().mkdirs();
}
try {
item.write(file);
map.put(fieldName, uploadPath);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 处理常规form表单的逻辑
* @param item
* @param map
*/
private static void processFormField(FileItem item, Map<String, Object> map) {
//返回的就是input的name属性
String name = item.getFieldName();
String value = null;
try {
value = item.getString("utf-8");
map.put(name, value);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
System.out.println("formField " + name + ":" + value);
}
}
package com.cskaoyan.upload;
import com.cskaoyan.upload.utils.FileUploadUtils;
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.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.Iterator;
import java.util.List;
import java.util.Map;
@WebServlet("/upload4")
public class UploadServlet4 extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html;charset=utf-8");
boolean multipartContent = ServletFileUpload.isMultipartContent(request);
if(!multipartContent){
response.getWriter().println("当前请求不含上传的文件");
return;
}
User user = new User();
Map<String, Object> map = FileUploadUtils.parseRequest(request);
try {
BeanUtils.populate(user, map);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
}
}
文件重名
文件改名问题。 wxid
利用时间戳 + username + 文件名
随机字符串重命名
String uuid = UUID.randomUUID().toString();
String fileName = item.getName();
//文件有后缀名的
fileName = uuid + fileName;
目录内文件数过多
hash散列
hashcode:尽可能散列的开,均匀。
文件名———hashcode———-int 数字——-16进制 8位 0x 1 2 3 4 5 6 a f
利用时间来划分—-年度、月份、日期,概率问题,如果出现某些大促,很可能需要进一步细分。
微信头像加国旗。去年 70周年
加上国旗的头像上传——-服务器
自己看自己的头像时改成功的,但是别人看起来,依然是之前的头像。经过了半天左右,头像终于同步了,别人看到你的头像也是新的了。
案例
写一个注册页面,包含图片上传,然后将上传的资源进行回显,回显图片