原因
1. 上传文件,方法一定要用post
2. 上传文件仅上传文件名,不上传文件的内容,
需要设置enctype属性 enctype="multipart/form-data",这时上传的文件中还包含一些不属于文件的东西,需要分割
3. 这个时候如果还上传了普通的表单数据,会和文件资源一起被保存到文件中
4. request.getInputStream()获得是整个请求体的内容 ,需要将不属于文件的内容分出去
此时,request获取请求参数的API也不能用了,即request.getPramaer(name)不可用
5. 要使用专门的文件上传组件
文件上传组件
依赖包
Commons-fileupload和commons-io
实现步骤
1、创建DiskFileItemFactory对象,设置缓冲区大小和临时文件目录
//工厂约束
public DiskFileItemFactory(int sizeThreshold, java.io.File repository) //构造函数
public void setSizeThreshold(int sizeThreshold) //设置内存缓冲区的大小,默认值为10K。当上传文件大于缓冲区大小时, fileupload组件将使用临时文件缓存上传文件。
public void setRepository(java.io.File repository) //指定临时文件目录,默认值为System.getProperty("java.io.tmpdir").
factory.setSizeThreshold(yourMaxMemorySize);
factory.setRepository(yourTempDirectory);
2、使用DiskFileItemFactory 对象创建ServletFileUpload对象,并设置上传文件的大小限制。
List parseRequest(HttpServletRequest request)//解析request对象,并把表单中的每一个输入项包装成一个fileItem 对象,并返回一个保存了所有FileItem的list集合。
boolean isMultipartContent(HttpServletRequest request) //判断上传表单是否为multipart/form-data类型
//上传文件大小约束
setFileSizeMax(long fileSizeMax)//设置单个上传文件的最大值bytes
setSizeMax(long sizeMax) //设置上传文件总量的最大值(多个文件同时上传时文件最大值的总和)
setHeaderEncoding(java.lang.String encoding)//设置编码格式,解决上传文件名乱码问题
3、调用ServletFileUpload.parseRequest方法解析request对象,得到一个保存了所有上传内容的List对象。
boolean isFormField() //判断FileItem是一个文件上传对象还是普通表单对象
1.如果判断是一个普通表单对象
String getFieldName() //获得普通表单对象的name属性
String getString(String encoding) //获得普通表单对象的value属性
2.如果判断是一个文件上传对象
String getName() //获得上传文件的文件名(有些浏览器会携带客户端路径)
InputStream getInputStream() //获得上传文件的输入流
delete() //在关闭FileItem输入流后,删除临时文件
4、对list进行迭代,每迭代一个FileItem对象,调用其isFormField方法判断是否是上传文件
True 为普通表单字段,则调用getFieldName、getString方法得到字段名和字段值
False 为上传文件,则调用getInputStream方法得到数据输入流,从而读取上传数据。
//检查我们是否有文件上传请求
boolean isMultipart = ServletFileUpload.isMultipartContent(request);
示例:
1.创建一个工厂
DiskFileItemFactory factory = new DiskFileItemFactory();
2.配置存储文件的地址库
ServletContext servletContext = this.getServletConfig().getServletContext();
File repository = (File) servletContext.getAttribute("javax.servlet.context.tempdir");
factory.setRepository(repository);
3.创建ServletFileUpload对象,用于解析request
ServletFileUpload upload = new ServletFileUpload(factory);
4.解析request
List<FileItem> items = upload.parseRequest(request);
5.遍历上传的文件
Iterator<FileItem> iter = items.iterator();
while(iter.hasNext()){
FileItem item = iter.next();
if(item.isFormField){
processFormField(item);//处理表单
}else{
processUploadFile(item);//处理上传文件
}
}
6.处理表单
String name = item.getFieldName();//获取表单key
String value = item.getString("utf-8");//获取表单的值,可加参数解决表单数据中文乱码问题
7.处理文件上传
String fieldName = item.getFieldName();
String fileName = item.getName();
contentType = item.getContentType();
boolean isInMemory = item.isInMemory();
long sizeInBytes = item.getSize();
8.将文件存入磁盘
String upload = getServletContext().getRealPath("upload");
File file = new File(upload + "/" + fileName);
item.write(file);
细节
1.上传文件重名问题
//文件名称需要做一个处理,保证每一个文件不会与其他文件重名
方式一:用户的id -_ fileName
方式二:当前的时间戳加上一些序列数2019082411254790
方式三:加随机数uuid
String uuid = UUID.randomUUID().toString();
fileName = uuid + "-" + fileName;
2.文件数目过多
//使用散列分散文件
String upload = getServletContext().getRealPath("upload");
int hashCode = fileName.hashCode();
//int 32 16 8 1 2 3 4 a b f 3 16^8 40亿左右
String s = Integer.toHexString(hashCode);//转成16进制,八个文件夹
char[] chars = s.toCharArray();
for (char aChar : chars) {
upload = upload + "/" + aChar;
}
File file = new File(upload + "/" + fileName);