1.文件上传的前提
提供form表单,method必须是post
form表单的enctype必须是multipart/form-data(enctype 属性规定在发送到服务器之前应该如何对表单数据进行编码,multipart/form-data表示不对字符编码,在使用包含文件上传控件的表单时,必须使用该值)
提供input type="file"类的上传输入域
2.借助第三方上传组件fileupload实现上传
两个jar包:
在服务器端
1.创建DiskFileItemFactory
2.创建ServletFileUpload
3.通过ServletFileUpload的parseRequest方法得到所有的FileItem
FileItem对象对应一个表单项(表单字段)。可以是文件字段或普通字段
- boolean isFormField():判断当前表单字段是否为普通文本字段,如果返回false,说明是文件字段;
- String getFieldName():获取字段名称,例如:<input type=”text” name=”username”/>,返回的是username;
- String getString():获取字段的内容,如果是文件字段,那么获取的是文件内容,当然上传的文件必须是文本文件;
- String getName():获取文件字段的文件名称;(a.txt)
- String getContentType():获取上传的文件的MIME类型,例如:text/plain。
- int getSize():获取上传文件的大小;
- InputStream getInputStream():获取上传文件对应的输入流;
- void write(File):把上传的文件保存到指定文件中。
- delete();
文件上传时要考虑的几个问题
a、保证服务器的安全
把保存上传文件的目录放在用户直接访问不到的地方。
b、避免文件被覆盖
让文件名唯一即可
c、避免同一个文件夹中的文件过多
方案一:按照日期进行打散存储目录
方案二:用文件名的hashCode计算打散的存储目录:二级目录
d、限制文件的大小:web方式不适合上传大的文件
单个文件大小:
ServletFileUpload.setFileSizeMax(字节)
总文件大小:(多文件上传)
ServletFileUpload.setSizeMax(字节)
e、上传字段用户没有上传的问题
通过判断文件名是否为空即可
f、临时文件的问题
DiskFileItemFactory:
作用:产生FileItem对象
内部有一个缓存,缓存大小默认是10Kb。如果上传的文件超过10Kb,用磁盘作为缓存。
存放缓存文件的目录在哪里?默认是系统的临时目录。
如果自己用IO流实现的文件上传,要在流关闭后,清理临时文件。
FileItem.delete();
文件上传demo
package com.shenhesoft.web.servlet;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.text.SimpleDateFormat;
import java.util.Date;
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.FileUploadException;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.apache.commons.io.FilenameUtils;
public class UploadServlet extends HttpServlet {
/**
* 文件上传的步骤
*/
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
request.setCharacterEncoding("UTF-8");
response.setContentType("text/html;charset=UTF-8");
// 1.创建工厂类DiskFileItemFactory对象
DiskFileItemFactory factory = new DiskFileItemFactory();
// 2.使用工厂创建解析器对象
ServletFileUpload upload = new ServletFileUpload(factory);
// 解决中文乱码问题
upload.setHeaderEncoding("UTF-8");
try {
// 设置上传文件的大小
upload.setFileSizeMax(1024 * 1024 * 3);// 上传的文件不得超过3M
// 上传文件的总大小
// upload.setSizeMax(1024 * 1024 * 6);
// 3.使用解析器来解析request对象
List<FileItem> fileItems = upload.parseRequest(request);
// 遍历表单项数据
for (FileItem fileItem : fileItems) {
// 判断是否是普通表单项,还是上传表单项
if (fileItem.isFormField()) { // 普通表单项
processFormField(fileItem);
} else { // 上传表单项
processUploadField(fileItem);
}
}
} catch (FileUploadBase.FileSizeLimitExceededException e) {
throw new RuntimeException("上传的文件过大不能超过,请重新上传");
} catch (FileUploadException e) {
e.printStackTrace();
}
}
// 文件表单项
private void processUploadField(FileItem fileItem) {
// 获取文件上传的名字
String fileName = fileItem.getName();
// 得到文件输入流
try {
InputStream inputStream = fileItem.getInputStream();// 将上传的文件读取到程序中
// 创建一个目录存放获取到的文件
// ServletContext.getRealPath() 是从当前servlet 在tomcat 中的存放文件夹开始计算起的
// 在当前servlet 在tomcat 中的存放文件夹中创建一个upload文件
File storeDirectory = new File(this.getServletContext().getRealPath("/WEB-INF/upload"));// 让外界访问不到,保证服务器安全,上传的文件放在WEB-INF下
// 如果文件不存在,则创建文件
if (!storeDirectory.exists()) {
storeDirectory.mkdirs();
}
// 在当前servlet 在tomcat
// 中的存放文件夹中创建一个upload文件,的基础上,创建一个完整的位置存放获取到的文件的存放位置
// 处理上传的文件的文件名fileName(遇到的问题:不同浏览器的使用fileItem.getName()得到的文件名不同,IE的是C:\Users\Administrator\Desktop\2AWA0[$`YCL7%AIZ%SD0$[P.jpg,其他的有的是只有文件名,不加路径)
if (fileName != null) {
fileName = FilenameUtils.getName(fileName);
}
// 避免文件被覆盖,解决文件同名问题
fileName = UUID.randomUUID() + "_" + fileName;
// 避免同一个文件里面文件过多,将目录打散
/* String childDirectory = makeChildDirectory(storeDirectory); */// 创建日期的子目录
String childDirectory = makeChildDirectory(storeDirectory, fileName);
File file = new File(storeDirectory, childDirectory + File.separator + fileName);// 这个位置就是.../upload/日期/获取到的文件名字
// 将从程序中读取到的数据写入到创建的文件位置file
/*
* FileOutputStream outStream = new FileOutputStream(file); int len
* = 0; byte[] b = new byte[1024]; while ((len =
* inputStream.read(b)) != -1) { outStream.write(b, 0, len); }
* outStream.close();
*/
try {
fileItem.write(file);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 使用UUID的hashCode编码值创建目录
*
* @param storeDirectory
* @param fileName
* @return s
*/
private String makeChildDirectory(File storeDirectory, String fileName) {
int code = fileName.hashCode();// 得到一个32位的整数
String hexString = Integer.toHexString(code);// 将得到的code转换成16进制的字符串
String s = hexString.charAt(0) + File.separator + hexString.charAt(1);// 格式:5/6
// 使用上面得到的字符串创建二级目录
File file = new File(storeDirectory, s);
if (!file.exists()) {
file.mkdirs();
}
return s;
}
/**
* 创建带日期的子目录
*
* @param storeDirectory
* @return date
*//*
* private String makeChildDirectory(File storeDirectory) { //格式化日期
* SimpleDateFormat sf=new SimpleDateFormat("yyyy-MM-dd"); String date =
* sf.format(new Date()); //创建一个新的目录:原来的storeDirectory+日期 File file=new
* File(storeDirectory,date); if (!file.exists()) { file.mkdirs(); }
* return date; }
*/
// 普通表单项
private void processFormField(FileItem fileItem) {
// 字段名
String fieldName = fileItem.getFieldName();
// 字段值
String fieldValue;
try {
fieldValue = fileItem.getString("UTF-8");
System.out.println(fieldName + "===" + fieldValue);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
}