1、文件的上传
(1)什么是上传:把客户端的文件 上传 到服务器上 。比如云盘
(2)实现上传:servlet里面没有提供上传的技术。就需要使用第三方组件,实现上传
(3)实现上传的技术:
jspSmartUpload:适于嵌入执行上传下载操作的JSP文件中
针对jsp+javabean模式的,模型一
fileUpload:mvc的开发模式,模型二
FileUpload 是 Apache commons下面的一个子项目,用来实现Java环境下面的文件上传功能
组件FileUpload依赖于Commons IO组件
实现上传使用的第三方的技术,首先需要导入jar包
(4)如何实现上传(要求):
第一个要求:有表单,但是method必须是post
第二个要求:必须要有文件输入项,在文件输入项里面必须要name属性 <input type="file" name="file"/>
第三个要求:在表单里面 enctype :multipart/form-data
enctype默认值:application/x-www-form-urlencoded
(5)文件上传的实现过程(画图)
(6)使用代码实现文件的上传
导入fileupload的jar包
使用到类 DiskFileItemFactory:磁盘文件项工厂
ServletFileUpload:核心上传类
FileItem:文件项
步骤(固定)
1、创建磁盘文件项工厂 new DiskFileItemFactory()
2、创建核心的上传类 new ServletFileUpload(FileItemFactory fileItemFactory)
3、使用核心上传类解析request对象 parseRequest(javax.servlet.http.HttpServletRequest request)
返回list集合 泛型 FileItem
4、遍历list集合
5、判断FileItem是普通项还是文件上传项
如果是一个普通项,得到普通项的名称和值
如果是文件上传项,实现文件上传的代码
(7)核心api的使用
DiskFileItemFactory:磁盘文件项工厂
构造方法
DiskFileItemFactory()
有两个方法设置
setSizeThreshold(int sizeThreshold)
setRepository(java.io.File repository)
DiskFileItemFactory(int sizeThreshold, java.io.File repository)
第一个参数:缓冲区的大小 10k ;
第二个参数:临时文件存储路径,上传的文件大小超过缓冲区的大小产生临时文件
ServletFileUpload:核心上传类
构造方法 ServletFileUpload(FileItemFactory fileItemFactory)
解析request对象:parseRequest(javax.servlet.http.HttpServletRequest request)
返回list集合,泛型FileItem
设置文件名称有中文,设置编码:setHeaderEncoding(java.lang.String encoding)
设置单个文件上传的大小:setFileSizeMax(long fileSizeMax)
设置所有文件上传的总的大小:setSizeMax(long sizeMax)
FileItem接口:文件项
isFormField():判断是普通项还是文件项 true和false,如果true表示普通项,如果false表示文件上传项
getFieldName():获取普通项的name的名称
getString():获取普通项输入的值
getString(java.lang.String encoding):设置编码
getName():获取上传文件名称(加路径)
在某些浏览器里面,得到只是文件名称 没有路径 1.txt
getInputStream() :得到提交文件的输入流
delete():删除临时文件
(8)练习:js控制多文件的上传
有两个按钮:上传和增加
当点击增加,增加一个文件上传项和删除按钮
单点击删除,把删除按钮所在的那一行删除
点击上传,把页面上所有的文件上传到服务器
js代码
//增加方法
function add1() {
//得到div
var divv = document.getElementById("divv");
//向div里面写html代码
divv.innerHTML += "<div><input type=‘file‘ name=‘file1‘ /> <input type=‘button‘ value=‘del‘ onclick=‘del1(this);‘/></div>";
}
//删除操作
function del1(who) {
//得到del按钮的所在的div
var div = who.parentNode;
//删除div
var divv = div.parentNode;
divv.removeChild(div);
}
(9)上传的问题解决
文件的重名的问题
如果多次上传相同的文件名称的文件,最后一次上传的文件会把之前文件覆盖
解决方式:
在文件名称后面加一个随机的字符串 abbcdfdfer_1.txt
得到随机的字符串: uuid得到唯一的字符串
//得到唯一的字符串 使用uuid
String uuid = UUID.randomUUID().toString();
//在文件之前添加uuid的值 uuid_filename
filename = uuid+"_"+filename;
上传的所有的文件都会放到一个文件夹里面,如果文件很多,读写很慢
在企业级开发中
根据时间进行分离: 根据年 月 天
根据用户进行分离:比如张三上传文件存到张三
根据文件数量进行分离:每3000个文件存到一个文件夹
使用目录分离的算法实现
首先得到文件的唯一的文件名
根据这个唯一的文件名.hashcode()操作,得到唯一的int值
使用得到的唯一的值,& 0xf 把结果作为第一级目录
使用int值,右移四位,再& 0xf,把结果作为第二级目录
int 32位,在计算机里面如何表示?
每四位表示一段 ,8段
2、文件的下载
(1)文件下载几种方式:两种
直接使用超链接实现 <a href="文件路径"></a>
这种方式不好,图片格式直接打开,而zip格式才会提示下载
写代码实现下载
步骤
//创建磁盘文件项工厂
DiskFileItemFactory factory = new DiskFileItemFactory();
//创建核心上传类
ServletFileUpload upload = new ServletFileUpload(factory);
upload.setHeaderEncoding("utf-8");
//解析request对象
List<FileItem> list = upload.parseRequest(request);
//创建Resource对象
Resource res = new Resource();
//遍历list集合
for (FileItem fileItem : list) {
//判断是否普通输入项
if(fileItem.isFormField()) {
String des = fileItem.getString("utf-8");
System.out.println(des);
res.setDes(des);
} else {//文件上传项
//得到上传文件名称 c:\a\a.txt
String filename = fileItem.getName();
int lens = filename.lastIndexOf("\\");
if(lens != -1) {
filename = filename.substring(lens+1);
}
//得到随机的字符串
String uuid = UUID.randomUUID().toString();
String uuidname = uuid+"_"+filename;
//得到上传文件夹的完全路径
String path = getServletContext().getRealPath("/uploadfile");
//得到分离之后的路径
String url = UploadUtils.getPath1(uuidname);
//判断文件夹是否存在,如果不存在,需要创建
File file = new File(path+url);
if(!file.exists()) {
file.mkdirs();
}
//得到上传文件
InputStream in = fileItem.getInputStream();
//使用输出流把文件写到文件夹里面
OutputStream out = new FileOutputStream(path+url+"/"+uuidname);
//流对接
int len = 0;
byte[] b = new byte[1024];
while((len=in.read(b))!=-1) {
out.write(b, 0, len);
}
//关闭流
in.close();
out.close();
//设置上传的信息
res.setUuidname(uuidname);
res.setRealname(filename);
res.setSavepath(path+url);
(2)代码实现文件的下载
//得到要下载文件的路径
String path = request.getParameter("filename");
path = new String(path.getBytes("iso8859-1"),"utf-8");
// System.out.println("path:: "+path); c:\1\a.mp3
//得到文件名称
String filename = path.substring(path.lastIndexOf("\\")+1);
//处理不同的浏览器中文问题
//解决中文乱码问题
//得到当前请求的浏览器的类型
String agent = request.getHeader("User-Agent");
//如果是火狐浏览器
if(agent.contains("Firefox")) {
//base64编码
filename = "=?UTF-8?B?"+
new BASE64Encoder().encode(filename.getBytes("utf-8"))+"?=";
} else {//ie浏览器
filename = URLEncoder.encode(filename, "utf-8");
}
//设置mime类型
String type = getServletContext().getMimeType(filename);
response.setContentType(type);
//设置头信息
response.setHeader("Content-Disposition", "attachment;filename="+filename);
//得到要下载文件的输入流
InputStream in = new FileInputStream(path);
//使用输出流把写到浏览器
OutputStream out = response.getOutputStream();
int len = 0;
byte[] b = new byte[1024];
while((len=in.read(b))!=-1) {
out.write(b, 0, len);
}
in.close();
(3)练习:给定任意的一个目录,把目录里面的所有的文件都显示出来(无论有多少层)
实现思路:
使用到树
树的遍历:深度优先遍历和广度优先遍历
可以使用队列实现树的遍历,队列特点是FIFO,先进先出
1、创建队列
- Queue<File> que = new LinkedList<File>();
- 入队:offer(E e)
- 出队:poll()
<%
//创建文件
File root = new File("f:\\1");
//创建队列
Queue<File> qu = new LinkedList<File>();
//把根节点入队
qu.offer(root);
//判断如果队列不为空
while(!qu.isEmpty()) {
//把根节点出队
File file = qu.poll();
//得到所有的文件和文件夹
File[] files = file.listFiles();
//遍历
for(File f : files) {
//判断是否是文件,如果是文件直接显示,不是文件继续入队
if(f.isFile()) {
%>
<a href="/day20/downlist?filename=<%=f.getCanonicalPath()%>"><%=f.getName() %></a> <br/>
<%
} else {//不是文件
//入队
qu.offer(f);
}
}
}
%>
2、把根节点入队,再把根节点出队
3、根节点出队之后,可以得到根节点的子节点
4、把子节点入队,在出队,获得子节点的子节点
依次类推....
实现文件的下载
//对文件名进行hashcode操作,返回int类型
int code1 = filename.hashCode();
//使用int值 & oxf
int d1 = code1 & 0xf;
//int值右移四位
int code2 = code1 >>> 4;
//把code2再&0xf
int d2 = code2 & 0xf;
return "/"+d1+"/"+d2;
}