一. 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. git学习记录1(本地库管理)

    学习参考地址:https://www.liaoxuefeng.com/wiki/0013739516305929606dd18361248578c67b8067c8c017b000 本编随笔只是自己对 ...

  2. 剑指offer——21正则表达式匹配

    题目描述 请实现一个函数用来匹配包括'.'和'*'的正则表达式.模式中的字符'.'表示任意一个字符,而'*'表示它前面的字符可以出现任意次(包含0次). 在本题中,匹配是指字符串的所有字符匹配整个模式 ...

  3. 剑指offer——13矩阵中的路径

    题目描述 请设计一个函数,用来判断在一个矩阵中是否存在一条包含某字符串所有字符的路径.路径可以从矩阵中的任意一个格子开始,每一步可以在矩阵中向左,向右,向上,向下移动一个格子.如果一条路径经过了矩阵中 ...

  4. vue中excal表格的导入和导出

    注意:vue中要实现表格的导入与导出,首先要install两个依赖, npm install -S file-saver xlsx  和  npm install -D script-loader.其 ...

  5. USACO 2008 January Silver Telephone Lines /// 二分最短路 邻接表dijkstra oj22924

    题目大意: 一共有N (1 ≤ N ≤ 1,000)个电线杆,有P P (1 ≤ P ≤ 10,000)对电线杆是可以连接的, 用几条线连接在一起的电线杆之间都可相互通信,现在想要使得电线杆1和电线杆 ...

  6. docker 挂载文件出错

    docker不能挂载文件,只能挂载文件夹,所以先从一个容器中复制一份配置文件. docker run --name test -d idp docker cp test:/app/appsetting ...

  7. 【转】Java程序CPU飙升问题排查方法

    windows环境下cpu飙升问题 线上某台runtime机器(windows Server)cpu报警,这种情况初步就是代码里面死循环了,先把机器下线了保证不再有新的任务分配进来,然而cpu使用依然 ...

  8. (转)Google Protocol Buffer 的使用和原理

    转自:https://www.ibm.com/developerworks/cn/linux/l-cn-gpb/index.html   简介 什么是 Google Protocol Buffer? ...

  9. JS对象 数组连接 concat() 方法用于连接两个或多个数组。此方法返回一个新数组,不改变原来的数组。 语法 arrayObject.concat(array1,array2,.arrayN)

    concat() 方法用于连接两个或多个数组.此方法返回一个新数组,不改变原来的数组. 语法 arrayObject.concat(array1,array2,...,arrayN) 参数说明: 注意 ...

  10. Android开发 View_自定义圆环进度条View

    前言 一个实现,空心圆环的自定义View,已经封装完好,可以直接使用. 效果图 代码 import android.content.Context; import android.graphics.C ...