12、文件上传
程序员文章站
2024-02-26 17:53:40
...
文件上传概述
将本地的文件通过流写入到服务器的过程。
- QQ上传照片
- 招聘网站上传简历
文件上传技术
- JSPSmartUpload:应用在JSP上的文件上传和下载的组件。
- FileUpload:应用在java环境上的文件上传的功能。
- Servlet3.0:提供文件上传的功能。
- Struts2:提供文件上传的功能。
文件上传的要素:
- 表单提交的方式需要时POST
- 表单中需要有文件上传的组件,表单有< input type=“file” >元素,需要有name属性和值。
- 表单< enctype=“multipart/form-data” >
文件上传的原理分析
代码实现
- 引入jar包
- 编写页面
- 编写文件上传的Servlet
/**
* 文件上传的Servlet
*/
public class UploadServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
try {
//1、创建磁盘文件项的工厂
DiskFileItemFactory diskFileItemFactory = new DiskFileItemFactory();
//2、创建一个核心的解析类
ServletFileUpload fileUpload = new ServletFileUpload(diskFileItemFactory);
//3、利用核心类解析request,解析后会得到多个部分,返回一个List集合,List集合装的是每个部分的内容(FileItem文件项)
List<FileItem> list = fileUpload.parseRequest(request);
//4、遍历list集合,会得到代表每个部分的文件项的对象,根据文件判断是否是文件上传项。
for (FileItem fileItem : list) {
//判断文件是普通项还是上传项
if(fileItem.isFormField()) {
//普通项,接收普通的值(表单的enctype变更,所以不能用request.getParameter())
String name = fileItem.getFieldName();
//获得普通项的值
String value = fileItem.getString("UTF-8");
System.out.println(name+" "+value);
}else {
//文件上传项
//获得文件上传项文件的名称
String filename = fileItem.getName();
//获得文件上传项文件的数据
InputStream is = fileItem.getInputStream();
//获得文件上传的路径:磁盘绝对路径
String realPath = getServletContext().getRealPath("/upload");
//创建一个输出流,写入到设置的路径中
OutputStream os = new FileOutputStream(realPath+"/"+filename);
//两个流对接
int len = 0;
byte[] b = new byte[1024];
while((len = is.read(b)) != -1) {
os.write(b,0,len);
}
is.close();
os.close();
}
}
} catch (FileUploadException e) {
e.printStackTrace();
}
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
文件上传的API
- DiskFileItemFactory磁盘文件项工厂
- ServletFileUpload核心解析类
- 构造方法:
- ServletFileUpload();
- ServletFileUpload(FileItemFactory fileItemFactory);
- 方法:
- isMultipartContext():判断表单的enctype属性是否正确;
- parseRequest():解析Request对象,返回一个List集合(每个部分的对象FileItem);
- setFileSizeMax():设置单个文件上传大小;
- setSizeMax():设置文件上传总大小;
- setProgressLstener():设置监听文件上传进度;
- setHeaderEncoding():设置中文文件名的上传乱码问题
- 构造方法:
- FileItem文件项
- isFormFiled():判断表单是普通项还是文件上传项,如果为true则代表为普通项;
- getFileName():获得普通项名称;
- getString():获得普通项的值;
- getName():获得上传项的名称;
- getInputStream():获得上传的文件内容;
- getSize():获得文件上传的文件大小;
- delete():删除文件上传过程中的临时文件;
JS控制多文件上传
<%@ 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>
function add(){
var div1Element = document.getElementById("div1");
div1Element.innerHTML += "<div><input type='file' name='upload'><input type='button' value='删除' οnclick='del(this)'></div>";
}
function del(who){
var divv = who.parentNode;
divv.parentNode.removeChild(divv);
}
</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="上传">
<div id="div1"> </div> <br/>
</form>
</body>
</html>
文件上传中兼容浏览器的问题
IE老版本的浏览器会出现文件名获取错误的问题,因为获取文件名称的时候会带有路径。edge也有此问题。
解决代码:
//文件上传项
//获得文件上传项文件的名称
String filename = fileItem.getName();
//System.out.println("文件名:"+filename);
//找到最后一个斜杠
int idx = filename.lastIndexOf("\\");
if(idx != -1) {
//说明找到了,要截掉路径
filename = filename.substring(idx+1);
}
文件上传同一个目录下文件同名的问题
使用唯一文件名进行解决
- 编写工具类
package com.itheima.utils;
import java.util.UUID;
/**
* 文件上传的工具类
* @author wj156
*
*/
public class UploadUtils {
/**
* 传递一个文件名,返回一个唯一的文件名。
*/
public static String getUuidFileName(String fileName) {
//javaAPI 中有一个类UUID可以产生随机的字符串
//获得文件的扩展名
int index = fileName.lastIndexOf(".");
String extetions = fileName.substring(index);
//随机生成字符串并返回
return UUID.randomUUID().toString()+extetions;
}
}
- 引入工具类
//得到唯一文件名
String uuidFileName = UploadUtils.getUuidFileName(filename);
文件上传同一个目录下文件过多的问题
目录下文件过多,打开会卡顿,且影响读写操作。
解题思路:目录分离
- 按时间分离:按月、周、天等
- 按用户分离:按张三、李四
- 按个数分离:一个目录存放3000个文件
- 按目录分离算法:按某种特定算法进行分离
- 上传一个文件,得到唯一文件名,获取hashcode值(32位int类型的值),让hashcode的值&0xf,得出值为一级目录;让hashcode右移4位&0xf,得出这个值位二级目录,以此类推。
工具类中编写方法:
- 上传一个文件,得到唯一文件名,获取hashcode值(32位int类型的值),让hashcode的值&0xf,得出值为一级目录;让hashcode右移4位&0xf,得出这个值位二级目录,以此类推。
/**
* 目录分离算法的实现
*/
public static String getRealPath(String uuidFileName) {
int code1 = uuidFileName.hashCode();
int d1 = code1 & 0xf; //获得一级目录
int code2 = code1 >>> 4; //右移4位
int d2 = code2 & 0xf; //获得二级目录
// 。。。。
return "/"+d1+"/"+d2;
}
应用:
//进行目录分离:
String path = UploadUtils.getRealPath(uuidFileName);
String newPath = realPath+path;
File file = new File(newPath);
if(!file.exists()) {
file.mkdir();
}