使用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; ...
随机推荐
- detours3.0文档翻译
拦截二进制函数 Detours库可以在运行过程中动态拦截函数调用.detours将目标函数前几个指令替换为一个无条件跳转,跳转到用户定义的detour函数.被拦截的函数保存在trampoline函数中 ...
- KdPrint/DbgPrint and UNICODE_STRING/ANSI_STRING
typedef struct _UNICODE_STRING { USHORT Length; USHORT MaximumLength; PWSTR Buffer; } UNICODE_STRING ...
- LeetCode 1037. Valid Boomerang (有效的回旋镖)
题目标签:Math 题目给了我们三个点,让我们判断这三个点是否在一条直线上. 利用斜率 k = (y1 - y0) / (x1 - x0) 来判断,如果 三个点 abc, ab 的斜率 = bc 的斜 ...
- java could not open `C|D|E|F:\jre\lib\amd64\jvm.cfg' 解决方案与原因
因为安装了 jdk 后发现有多个 jre 一个是安装目录下的. 还有一个是安装后的自动安装的注意路径都不一样. 由于本人有强迫症所有不能容忍有两个 jre 目录的存在,所以果断删除了 D 盘下的.谨慎 ...
- spark2.+ sql 性能调优
1.在内存中缓存数据 性能调优主要是将数据放入内存中操作,spark缓存注册表的方法 版本 缓存 释放缓存 spark2.+ spark.catalog.cacheTable("tableN ...
- 为什么 TCP 建立连接是三次握手,关闭连接确是四次挥手呢?
Java技术栈 www.javastack.cn 优秀的Java技术公众号 作者:小书go https://blog.csdn.net/qzcsu/article/details/72861891 背 ...
- sql 递归显示所有父节点
1.我先建两个表 一个表示项目及级别 另一个表示项目最后一级中包含内容.两个表的数据如图 CREATE TABLE [dbo].[yq_Project]( ,) primary key, ) NOT ...
- 【Latex】一些使用
http://www.mohu.org/info/symbols/symbols.htm GG..研究个公式就搞这么晚了..还不知道那三个报错的是怎么回事.. 意识模糊..睡了睡了. # Latex ...
- 2018湘潭大学程序设计竞赛【B】
题目链接: https://www.nowcoder.com/acm/contest/105/B 题意: 给你一个字母矩阵,和测试组数,让你统计字符串的字符累计出现的次数,然后让你找出需要找的字符,这 ...
- Winform 获取桌面设备上下文
//获得桌面设备上下文 us(Graphics g = Graphics.FromHwnd(IntPtr.Zero)) { g.DrawLine(Pens.Red, , , , ); }