一. 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实现文件上传的更多相关文章

  1. zt对于C#中的FileUpload解决文件上传大小限制的问题设置

    对于C#中的FileUpload解决文件上传大小限制的问题设置 您可能没意识到,但对于可以使用该技术上载的文件的大小存在限制.默认情况下,使用 FileUpload 控件上载到服务器的文件最大为 4M ...

  2. asp.net web常用控件FileUpload(文件上传控件)

    FileUpload控件的主要中能:向指定目录上传文件,该控件包括一个文本框和一个浏览按钮. 常用的属性:FileBytes,FileContent.FileName.HasFile.PostedFi ...

  3. zk FileUpload(文件上传)

    <button label="上传 Image" upload="true,maxsize=1073741824"> <attribute n ...

  4. Apache Commons fileUpload实现文件上传之一

      需要两个jar包: commons-fileupload.jar Commons IO的jar包(本文使用commons-io-2.4.jar) 利用Servlet来实现文件上传. package ...

  5. extjs采用fileupload进行文件上传后台实现

    前台js: form: Ext.define("GS.base.BasicImportForm",{    extend:"Ext.form.Panel",   ...

  6. Spring MVC使用commons fileupload实现文件上传功能

    通过Maven建立Spring MVC项目,引入了Spring相关jar依赖. 1.为了使用commons fileupload组件,需要在pom.xml中添加依赖: <properties&g ...

  7. Apache Commons FileUpload 实现文件上传

    Commons FileUpload简介 Apache Commons是一个专注于可重用Java组件开发的 Apache 项目.Apache Commons项目由三个部分组成: 1.Commons P ...

  8. 完成FileUpload的文件上传功能,且可改按钮样式

    FileUpload控件: 更改按钮样式思路: 自己定义一个按钮,设置该按钮的样式,然后将FileUpload控件通过定位定在自己定义的按钮上面,设置z-index,使得控件浮在自己定义的按钮上面,记 ...

  9. FileUpload实现文件上传(包含多文件)

    package com.hzml.serve; import java.io.File; import java.io.IOException; import java.io.PrintWriter; ...

随机推荐

  1. detours3.0文档翻译

    拦截二进制函数 Detours库可以在运行过程中动态拦截函数调用.detours将目标函数前几个指令替换为一个无条件跳转,跳转到用户定义的detour函数.被拦截的函数保存在trampoline函数中 ...

  2. KdPrint/DbgPrint and UNICODE_STRING/ANSI_STRING

    typedef struct _UNICODE_STRING { USHORT Length; USHORT MaximumLength; PWSTR Buffer; } UNICODE_STRING ...

  3. LeetCode 1037. Valid Boomerang (有效的回旋镖)

    题目标签:Math 题目给了我们三个点,让我们判断这三个点是否在一条直线上. 利用斜率 k = (y1 - y0) / (x1 - x0) 来判断,如果 三个点 abc, ab 的斜率 = bc 的斜 ...

  4. java could not open `C|D|E|F:\jre\lib\amd64\jvm.cfg' 解决方案与原因

    因为安装了 jdk 后发现有多个 jre 一个是安装目录下的. 还有一个是安装后的自动安装的注意路径都不一样. 由于本人有强迫症所有不能容忍有两个 jre 目录的存在,所以果断删除了 D 盘下的.谨慎 ...

  5. spark2.+ sql 性能调优

    1.在内存中缓存数据 性能调优主要是将数据放入内存中操作,spark缓存注册表的方法 版本 缓存 释放缓存 spark2.+ spark.catalog.cacheTable("tableN ...

  6. 为什么 TCP 建立连接是三次握手,关闭连接确是四次挥手呢?

    Java技术栈 www.javastack.cn 优秀的Java技术公众号 作者:小书go https://blog.csdn.net/qzcsu/article/details/72861891 背 ...

  7. sql 递归显示所有父节点

    1.我先建两个表 一个表示项目及级别 另一个表示项目最后一级中包含内容.两个表的数据如图 CREATE TABLE [dbo].[yq_Project]( ,) primary key, ) NOT ...

  8. 【Latex】一些使用

    http://www.mohu.org/info/symbols/symbols.htm GG..研究个公式就搞这么晚了..还不知道那三个报错的是怎么回事.. 意识模糊..睡了睡了. # Latex ...

  9. 2018湘潭大学程序设计竞赛【B】

    题目链接: https://www.nowcoder.com/acm/contest/105/B 题意: 给你一个字母矩阵,和测试组数,让你统计字符串的字符累计出现的次数,然后让你找出需要找的字符,这 ...

  10. Winform 获取桌面设备上下文

    //获得桌面设备上下文 us(Graphics g = Graphics.FromHwnd(IntPtr.Zero)) { g.DrawLine(Pens.Red, , , , ); }