一、概述
文件上传:将本地的文件通过流写入到服务器的过程
二、应用
- 实际开发中有很多应用:
- QQ空间上传图片
- 招聘网站上传简历
三、文件上传的技术
- JSPSmartUpload :应用在JSP上的文件上传和下载的组件。
- FileUpload :应用在Java环境上的文件上传的功能。
- Servlet3.0 :提供文件上传的功能
- Struts2 :提供文件上传的功能
四、文件上传的要素
- 文件上传的三个要素
- 表单的提交的方式需要是POST(get方式有大小限制)
- 表单中需要有<input type=”file”>元素,需要有name属性和值。
- 表单enctype=”multipart/form-data”
五、文件上传的原理分析
5.1 加enctype与不加的区别
新建jsp页面:
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<form action="" method="post" enctype="multipart/form-data">
文件描述:<input type="text" name="miaoshu"><br>
文件上传:<input type="file" name="upload"><br>
<input type="submit" value="提交"><br>
</form>
</body>
</html>
没设置enctype时候的请求部分:只有描述和上传的文件名 ,没有文件上传中的文件的具体的内容
设置enctype后:可看到文件内容
5.2 文件上传原理分析
六、文件上传的入门
第一步:引入文件上传的相关的jar包
第二步:编写文件上传的页面
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<form action="${pagecontext.request.contextPath}/uploadServlet" method="post" enctype="multipart/form-data">
文件描述:<input type="text" name="miaoshu"><br>
文件上传:<input type="file" name="upload"><br>
<input type="submit" value="提交"><br>
</form>
</body>
</html>
第三步:编写文件上传的Servlet
第四步:访问http://localhost/demo7/jsp/upload.jsp,已上传成功。本地工程下无,需要到Tomcat目录下找
注意:中间执行时报错,发现需要额外引入commons-io包
七、文件上传的API
7.1 DiskFileItemFactory:磁盘文件项工厂
构造方法
- DiskFileItemFactory()
- DiskFileItemFactory(int sizeThreshold,File repostory)
- sizeThreshold :设置文件上传的缓冲区的大小,默认值为10kb。
- repository :设置文件上传过程中产生临时文件存放的路径
API使用的代码
注:超过缓冲区大小则会产生临时文件。
7.2 ServletFileUpload:核心解析类
构造方法:
ServeltFileUpload(FileItemFactory fileItemFactory)
方法:
是用来判断表单的enctype属性是否正确。
解析Request对象,返回一个List集合(每个部分的对象FileItem)
设置单个文件的大小
设置上传的文件的总大小
设置中文文件名上传的乱码的问题。
设置监听文件上传的进度
7.3 FileItem文件项
方法:
判断表单项是普通项还是文件上传项。如果为true代表是普通项
普通项的方法:
- 获得普通项的名称
2.获得普通项的值
文件上传项的方法:
1.获得文件上传的文件名的方法
2.获得文件上传的文件内容的方法
3.获得文件上传的文件的大小
4.删除文件上传过程中的临时文件
7.4 JS控制多文件上传
案例需求
案例实现:
upload.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<script type="text/javascript">
function add(){
console.log("add");
//获得id为div1的元素
var div1Element = document.getElementById("div1");
div1Element.innerHTML += "<div><input type='file' name='upload'/><input type='button' value='删除' onclick='del(this)'/></div>";
}
function del(who){
//who代表的是删除按钮,找到删除按钮的爷爷节点,并删除其子节点。
who.parentNode.parentNode.removeChild(who.parentNode);
}
</script>
</head>
<body>
<h1>多文件上传</h1>
<form action="${pageContext.request.contextPath}/uploadServlet" method="post" enctype="multipart/form-data">
<input type="button" value="添加" onclick="add()"/>
<input type="submit" value="上传"/><br>
<div id="div1">
</div>
</form>
</body>
</html>
uploadSevlet.java
package com.itheima.upload;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
// 此处注意包的引用,为commons,不是tomcat
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
/**
* Servlet implementation class uploadServlet
*/
@WebServlet("/uploadServlet")
public class uploadServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
try {
DiskFileItemFactory diskFileItemFactory = new DiskFileItemFactory();
ServletFileUpload servletFileUpload = new ServletFileUpload(diskFileItemFactory);
List<FileItem> list = servletFileUpload.parseRequest(request);
for(FileItem item:list) {
if(item.isFormField()) {
String fieldName = item.getFieldName();
String value = item.getString("UTF-8");
System.out.println(fieldName+" "+value);
}else {
String name = item.getName();
InputStream inputStream = item.getInputStream();
String realPath = getServletContext().getRealPath("/upload");
FileOutputStream fileOutputStream = new FileOutputStream(realPath+"/"+name);
int len=0;
byte[] b = new byte[1024];
while((len = inputStream.read()) != -1) {
fileOutputStream.write(b, 0, len);
}
inputStream.close();
fileOutputStream.close();
}
}
}catch(Exception e) {
e.printStackTrace();
}
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
此处代码出现问题,解决方法2个:
(1)引入jar包
(2)导包不对,应该是common,而不是tomcat
7.5 文件上传兼容浏览器问题及解决
7.5.1 问题描述
如果使用IE老版本的浏览器出现一个文件名称获取错误问题。IE老版本获取文件名称的时候,会带有路径
7.5.2 问题解决
7.7 文件上传同一个目录下文件同名的问题及解决
7.7.1 问题描述
张三向服务器上传了一个文件aa.txt内容是hello world。李四向服务器上传了一个文件aa.txt内容hello Java。后上传的文件将先上传的文件覆盖
7.7.2 解决办法
UUID:和时间等有关,唯一标识码
用法:步骤一:新建UploadUtils工具类
步骤二:Servlet中修改如下
7.8 文件上传同一个目录下存放文件过多的问题及解决
7.8.1 问题描述
现在所有的用户都上传文件,如果网站访问量比较大,如果都上传到同一个目录下,在同一个目录下存放的文件太多了,也会对程序有影响(其实打开该目录的时候,都会很卡,更别说读写操作)
7.8.2 问题解决
- 目录分离
- 按时间分离 :按月、周、天、小时。
- 按用户分离 :按张三、李四。
- 按个数分离 :一个目录下存放3000个文件。
- 按目录分离算法 :按照某种特定算法进行分离。
- 上传一个文件,得到一个唯一的文件名。
- 唯一文件名获取其hashCode值。-----int类型的值(32位)
- 让hashCode的值 & 0xf;-----得出的这个值作为一级目录。
- 让hashCode右移4位 & 0xf;----得出的这个值作为二级目录。
- 以此类推。
算法实现:
步骤一:目录工具类
步骤二:Servlet
package com.itheima.upload;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import com.itheima.utils.getPath;
import com.itheima.utils.getUuid;
/**
* Servlet implementation class uploadServlet
*/
@WebServlet("/uploadServlet")
public class uploadServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
try {
DiskFileItemFactory diskFileItemFactory = new DiskFileItemFactory();
ServletFileUpload servletFileUpload = new ServletFileUpload(diskFileItemFactory);
List<FileItem> list = servletFileUpload.parseRequest(request);
for(FileItem item:list) {
if(item.isFormField()) {
String fieldName = item.getFieldName();
String value = item.getString("UTF-8");
System.out.println(fieldName+" "+value);
}else {
String filename = item.getName();
System.out.println("获取到的原始文件名:"+filename);
//第一个\为转义字符
int lastIndexOf = filename.lastIndexOf("\\");
if(lastIndexOf != -1) {
// 使用老版IE浏览器
filename = filename.substring(lastIndexOf+1);
System.out.println("对于老版IE浏览器处理后的文件名:"+filename);
}
String name = getUuid.getUuid(filename);
System.out.println("UUID处理后的文件名:"+name);
InputStream inputStream = item.getInputStream();
String realPath = getServletContext().getRealPath("/upload");
// 创建目录
String path = getPath.getRealPath(name);
realPath += path;
File file = new File(realPath);
System.out.println("上传路径:"+realPath);
// 目录不存在,则创建
if(!file.exists()) {
file.mkdirs();
}
FileOutputStream fileOutputStream = new FileOutputStream(realPath+"/"+name);
int len=0;
byte[] b = new byte[1024];
while((len = inputStream.read()) != -1) {
fileOutputStream.write(b, 0, len);
}
inputStream.close();
fileOutputStream.close();
}
}
}catch(Exception e) {
e.printStackTrace();
}
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}