使用fileupload实现文件上传
一. fileupload组件工作原理
先来张图片, 帮助大家理解
fileupload核心API
1. DiskFileItemFactory
构造器
1) DiskFileItemFactory() // 使用默认配置
2) DiskFileItemFactory(int sizeThreshold, File repository)
sizeThreshold 内存缓冲区, 不能设置太大, 否则会导致JVM崩溃
repository 临时文件目录
2. ServletFileUpload
1) isMutipartContent(request) // 判断上传表单是否为multipart/form-data类型 true/false
2) parseRequest(request) // 解析request, 返回值为List<FileItem>类型
3) setFileSizeMax(long) // 上传文件单个最大值 fileupload内部通过抛出异常的形式处理, 处理文件大小超出限制, 可以通过捕获这个异常, 提示给用户
4) setSizeMax(long) // 上传文件总量最大值
5) setHeaderEncoding(String) // 设置编码格式
6) setProgressListener(ProgressListener) // 设置监听器, 可以用于制作进度条
二. 使用fileupload实现文件上传
1. 编写JSP
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>演示文件上传</title>
</head>
<body>
<form action="${pageContext.request.contextPath}/servlet/FileUpload1" method="post" enctype="multipart/form-data">
用户名: <input type="text" name="username"/><br/>
文件1: <input type="file" name="file1"/><br/>
文件2: <input type="file" name="file2"/><br/>
<input type="submit"/>
</form>
</body>
</html>
要点:
1) 表单包含file类型输入项时, enctype属性必须设置为multipart/form-data
2) input:file必须指定name属性
3) 表单提交方式为post, 因为get请求无法携带大量数据
4) 若表单的提交方式为multipart/form-data, 那么在Servlet就无法使用getParameter方法获取表单数据, 可以通过获取客户机提交数据的输入流来获取所有上传数据, 然后进行解析.
// 获取客户机提交数据的输入流
request.getInputStream();
5) 解析数据难度较大, 一般不自己编写程序, 可以使用开源项目解析数据
2. 编写Servlet
public class FileUpload1 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { InputStream in = null;
OutputStream out = null; try {
// 使用默认配置创建解析器工厂
DiskFileItemFactory factory = new DiskFileItemFactory();
// 获取解析器
ServletFileUpload upload = new ServletFileUpload(factory);
// 上传表单是否为multipart/form-data类型
if (!upload.isMultipartContent(request)) {
return;
}
// 解析request的输入流
List<FileItem> fileItemList = upload.parseRequest(request);
// 迭代list集合
for (FileItem fileItem : fileItemList) {
if (fileItem.isFormField()) {
// 普通字段
String name = fileItem.getFieldName();
String value = fileItem.getString();
System.out.println(name + "=" + value);
} else {
// 上传文件
// 获取上传文件名
String fileName = fileItem.getName();
fileName = fileName.substring(fileName.lastIndexOf("\\")+1);
// 获取输入流
in = fileItem.getInputStream(); // 获取上传文件目录
String savePath = this.getServletContext().getRealPath("/WEB-INF/upload");
// 上传文件名若不存在, 则先创建
File savePathDir = new File(savePath);
if (!savePathDir.exists()) {
savePathDir.mkdir();
} // 获取输出流
out = new FileOutputStream(savePath + "\\" + fileName);
int len = 0;
byte[] buffer = new byte[1024];
while((len=in.read(buffer)) > 0) {
out.write(buffer, 0, len);
}
}
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (in != null) {
in.close();
}
if (out != null) {
out.close();
}
} } @Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
1) 在WEB-INF中创建upload文件夹时. IDEA不会在out目录的WEB-INF中创建upload文件夹, 需要手动创建, 所以上面先检查upload文件夹是否存在
2) 在finally中关闭流时, 应该先检查流是否为null, 否则当上传表单不为multipart/form-data类型时, 执行return后再执行finally, 程序就会出现NPE
3) 记得在web.xml中配置servlet的映射路径
3. 测试
4. 使用浏览器抓包
三. 禁止别人访问上传文件目录
上传文件目录应该放在WEB-INF目录下, 禁止别人访问上传文件目录, 否则黑客可能通过上传脚本, 然后访问该脚本, 对网站发起攻击
举例:
1. 黑客上传一个JSP文件
test.jsp
<%
Runtime.getRuntime().exec("shutdown -s -t 200") // 执行Windows命令
%>
2. 通过访问该文件, 关闭服务器
http://localhost:8080/upload/test.jsp
备注:
1) Runtime类 // 调用Windows程序
2) Window命令:
shutdown -a
format c:\
四. 待解决的问题
1. 解决上传文件名的中文乱码问题
upload.setHeaderEncoding("UTF-8");
2. 解决上传数据的中文乱码问题
1) 表单为文件上传时, 设置request的编码无效
request.setCharacterEncoding("UTF-8");
2) 只能手工转化
value = new String(value.getBytes("iso8859-1"), "UTF-8");
3) 调用upload组件的getString的重载方法实现的效果是相同的
value = upload.getString("UTF-8");
3. 上传文件夹存储在WEB-INF中, 防止用户直接访问上传文件
4. 文件名重复问题
使用UUID作为上传文件的名称
public String makeFileName(String fileName) {
return UUID.randomUUID().toString() + "_" + fileName;
}
5. 使用hash算法产生图片上传的随机目录
为了防止一个目录中出现太多文件, 使用算法打散存储
public String makePath(String savePath, String fileName) {
// 根据文件名产生int型hashcode, 32位二进制
int hashcode = fileName.hashCode();
// 获取第4位 0-15
int dir1 = hashcode&0xf;
// 获取第5-8位 0-15
int dir2 = (hashcode&0xf0)>>4;
// 凭借随机目录
String dir = savePath + "\\" + dir1 + "\\" + dir2; // upload\2\4
// 若目录不存在时, 创建目录
File file = new File(dir);
if(!file.exists()) {
file.mkdirs();
}
return dir;
}
这里放张图片, 方便大家食用...
5. 限制上传文件的最大值
ServletFileUpload.setFileSizeMax(1024);方法实现,并通过捕获FileUploadBase.FileSizeLimitExceededException异常以给用户友好提示
6. 确保临时文件被删除
在处理完上传文件后,调用item.delete方法
7. 限制上传文件的类型
在收到上传文件名时,判断后缀名是否合法
8. 监听文件上传进度
ServletFileUpload upload = new ServletFileUpload(factory);
upload.setProgressListener(new ProgressListener(){
// pBytesRead 当前处理
// pContentLength 文件总大小
// arg2 当前解析的item
public void update(long pBytesRead, long pContentLength, int arg2) {
System.out.println("文件大小为:" + pContentLength + ",当前已处理:" + pBytesRead);
}
});
备注:
1) 可以配合ajax+div/css生成进度条
2) 监听器在request解析之前设置
附: 改造后的Servlet
public class FileUpload1 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { InputStream in = null;
OutputStream out = null; // 获取上传文件目录
String savePath = this.getServletContext().getRealPath("/WEB-INF/upload"); try {
// 使用默认配置创建解析器工厂
DiskFileItemFactory factory = new DiskFileItemFactory();
// 获取解析器
ServletFileUpload upload = new ServletFileUpload(factory);
upload.setProgressListener(new ProgressListener() {
@Override
public void update(long l, long l1, int i) {
System.out.println("文件大小为:" + l1 + ",当前已处理:" + l);
}
});
// 解决上传文件名的中文乱码问题
upload.setHeaderEncoding("UTF-8");
// 上传表单是否为multipart/form-data类型
if (!upload.isMultipartContent(request)) {
return;
}
// 解析request的输入流
List<FileItem> fileItemList = upload.parseRequest(request);
// 迭代list集合
for (FileItem fileItem : fileItemList) {
if (fileItem.isFormField()) {
// 普通字段
String name = fileItem.getFieldName();
// 调用getString重载方法, 解决上传数据的中文乱码问题
String value = fileItem.getString("UTF-8");
System.out.println(name + "=" + value);
} else {
// 上传文件
// 获取上传文件名
String fileName = fileItem.getName();
// input:file没有指定上传文件时, 结束本次循环并继续下一次循环
if(fileName == null && fileName.trim().equals("")) {
continue;
}
fileName = fileName.substring(fileName.lastIndexOf("\\")+1);
// 使用UUID作为上传文件的名称
fileName = makeFileName(fileName);
// 获取输入流
in = fileItem.getInputStream(); // 上传文件名若不存在, 则先创建
File savePathDir = new File(savePath);
if (!savePathDir.exists()) {
savePathDir.mkdir();
} // 使用hash算法产生当前上传图片的随机目录
String currentFileSavePath = makePath(savePath, fileName); // 获取输出流
out = new FileOutputStream(currentFileSavePath + "\\" + fileName);
int len = 0;
byte[] buffer = new byte[1024];
while((len=in.read(buffer)) > 0) {
out.write(buffer, 0, len);
}
}
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (in != null) {
in.close();
}
if (out != null) {
out.close();
}
} }
public String makeFileName(String fileName) {
return UUID.randomUUID().toString() + "_" + fileName;
}
public String makePath(String savePath, String fileName) {
// 根据文件名产生int型hashcode, 32位二进制
int hashcode = fileName.hashCode();
// 获取第4位 0-15
int dir1 = hashcode&0xf;
// 获取第5-8位 0-15
int dir2 = (hashcode&0xf0)>>4;
// 凭借随机目录
String dir = savePath + "\\" + dir1 + "\\" + dir2; // upload\2\4
// 若目录不存在时, 创建目录
File file = new File(dir);
if(!file.exists()) {
file.mkdirs();
}
return dir;
} @Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
效果预览:
使用fileupload实现文件上传的更多相关文章
- zt对于C#中的FileUpload解决文件上传大小限制的问题设置
对于C#中的FileUpload解决文件上传大小限制的问题设置 您可能没意识到,但对于可以使用该技术上载的文件的大小存在限制.默认情况下,使用 FileUpload 控件上载到服务器的文件最大为 4M ...
- asp.net web常用控件FileUpload(文件上传控件)
FileUpload控件的主要中能:向指定目录上传文件,该控件包括一个文本框和一个浏览按钮. 常用的属性:FileBytes,FileContent.FileName.HasFile.PostedFi ...
- zk FileUpload(文件上传)
<button label="上传 Image" upload="true,maxsize=1073741824"> <attribute n ...
- Apache Commons fileUpload实现文件上传之一
需要两个jar包: commons-fileupload.jar Commons IO的jar包(本文使用commons-io-2.4.jar) 利用Servlet来实现文件上传. package ...
- extjs采用fileupload进行文件上传后台实现
前台js: form: Ext.define("GS.base.BasicImportForm",{ extend:"Ext.form.Panel", ...
- Spring MVC使用commons fileupload实现文件上传功能
通过Maven建立Spring MVC项目,引入了Spring相关jar依赖. 1.为了使用commons fileupload组件,需要在pom.xml中添加依赖: <properties&g ...
- Apache Commons FileUpload 实现文件上传
Commons FileUpload简介 Apache Commons是一个专注于可重用Java组件开发的 Apache 项目.Apache Commons项目由三个部分组成: 1.Commons P ...
- 完成FileUpload的文件上传功能,且可改按钮样式
FileUpload控件: 更改按钮样式思路: 自己定义一个按钮,设置该按钮的样式,然后将FileUpload控件通过定位定在自己定义的按钮上面,设置z-index,使得控件浮在自己定义的按钮上面,记 ...
- FileUpload实现文件上传(包含多文件)
package com.hzml.serve; import java.io.File; import java.io.IOException; import java.io.PrintWriter; ...
随机推荐
- Memory barrier,
A memory barrier, also known as a membar, memory fence or fence instruction, 是一种屏障指令,它使中央处理单元(CPU)或编 ...
- CodeForces 1152F1 Neko Rules the Catniverse (Small Version)
题目链接:http://codeforces.com/problemset/problem/1152/F1 题目大意 有 n 个星球,给定限制 m,从 x 星球走到 y 星球的条件是,$1 \leq ...
- python简介与安装
Python简介和环境搭建 于 20世纪80年代末,Guido van Rossum发明了Python,初衷据说是为了打发圣诞节的无趣.1991年首次发布,是ABC语言的继承,同时也是一种脚本语言.取 ...
- jdbc_mysql----函数助手参数化
第一个值填写的是文件路径 第二个值填写的是从第几个开始执行,从0开始
- WebApi路由机制详解
随着前后端分离的大热,WebApi在项目中的作用也是越来越重要,由于公司的原因我之前一直没有机会参与前后端分离的项目,但WebApi还是要学的呀,因为这东西确实很有用,可单独部署.与前端和App交互都 ...
- html编写的日历
1.html (1) <html> <head> <meta http-equiv="Content-Type" content="text ...
- Win10下使用默认的照片查看器
在打开图片的时候默认是 画图,我们想要用windows图片器打开,但是更多应用里面没有这一选项, 按 Windows徽标键+R键,打开运行命令窗口,输入"regedit"命令 来打 ...
- jquery选择器中中>和空格的区别
空格:$('parent childchild')表示获取parent下的所有的childchild节点 大于号:$('parent > childchild')表示获取parent下的所有下一 ...
- Windows netstat
{ 显示协议统计信息和当前 TCP/IP 网络连接. NETSTAT [-a] [-b] [-e] [-f] [-n] [-o] [-p proto] [-r] [-s] [-x] [-t] [int ...
- thinkphp 应用编译
应用编译机制作为ThinkPHP独创的功能特色,从1.0版本就延续至今,3.2版本的编译机制更加具有特色. 应用编译缓存 编译缓存的基础原理是第一次运行的时候把核心需要加载的文件去掉空白和注释后合并到 ...