文章目录
一、文件上传
文件上传:将本地的文件通过流写入到服务器的过程。
文件上传的三个要素
①表单的提交的方式需要是POST
②表单中需要有<input type=“file”>元素,需要有name属性和值。
③表单enctype=“multipart/form-data”
原理分析 :手动实现文件上传
1.获得分割线
2.获得请求体的所有内容request.getInputstream()
3.利用分割线将获得内容进行分割。
4.判断是普通项还是文件上传项
普通项:获得普通项名称和值
文件上传项:获得文件名称和文件内容
5.通过流写到服务器上。
1.入门案例
UploadServlet
package com.upload.demo;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.List;
import javax.servlet.ServletException;
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;
public class UploadServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
try {
DiskFileItemFactory diskFileItemFactory = new DiskFileItemFactory();
ServletFileUpload fileUpload = new ServletFileUpload(diskFileItemFactory);
List<FileItem> list = fileUpload.parseRequest(request);
for (FileItem fileItem : list) {
if (fileItem.isFormField()) {
//普通
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 (Exception e) {
e.printStackTrace();
}
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
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>
</head>
<body>
<h1>文件上传</h1>
<form action="${ pageContext.request.contextPath }/UploadServlet" method="post" enctype="multipart/form-data">
文件描述:<input type="text" name="info"><br/>
文件上传:<input type="file" name="upload"><br/>
<input type="submit" value="上传"/>
</form>
</body>
</html>
运行后可以上传文件,本地且可以接受,控制台显示文件信息
2.API方法介绍
DiskFileItemFactory : 磁盘文件项工厂
构造方法:
DiskFileltemFactory()
DiskFileltemFactory(int sizeThreshold,File repostory)
sizeThreshold : 设置文件上传的缓冲区的大小,默认值为10kb。
repostory : 设置文件上传过程中产生临时文件存放的路径。
方法:
setSizeThreshold() : 设置缓冲区的大小
setRepository() : 设置临时文件存放的路径
基本和入门代码一样,添加了这两个方法的使用:
package com.upload.demo;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.List;
import javax.servlet.ServletException;
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;
public class UploadServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
try {
//1.创建磁盘文件项工厂
DiskFileItemFactory diskFileItemFactory = new DiskFileItemFactory();
//1.1设置缓冲区大小
diskFileItemFactory.setSizeThreshold(3*1024*1024);//缓冲区大小为3M
//1.2设置临时文件存放路径:
//获得临时文件存放路径
String tempPath = getServletContext().getRealPath("/temp");
diskFileItemFactory.setRepository(new File(tempPath));
//2.创建核心解析类
ServletFileUpload fileUpload = new ServletFileUpload(diskFileItemFactory);
List<FileItem> list = fileUpload.parseRequest(request);
for (FileItem fileItem : list) {
if (fileItem.isFormField()) {
//普通
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 (Exception e) {
e.printStackTrace();
}
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
ServletFileUpload : 核心解析类
构造方法:
serveltFileUpload(FileltemFactory fileltemFactory);
方法:
ServletFileUpload.isMultipartContent(request);
是用来判断表单的enctype属性是否正确
List<FileItem> list = fileUpload.parseRequest(request);
解析Request 对象,返回一个List集合(每个部分的对象Fileltem)
fileUpload.setFileSizeMax(fileSizeMax);
设置单个文件的大小
fileUpload.setSizeMax(sizeMax);
设置上传的文件的总大小
fileUpload.setHeaderEncoding(encoding);
解决中文乱码的问题,如果中文乱码就使用
fileUpload.setProgressListener(pListener);
监听文件上传速度
FileItem : 文件项
方法:
fileItem.isFormField()
判断表单项是普通项还是文件上传项。如果为true代表是普通项
普通项方法:
fileItem.getFieldName();
获得普通项名称
fileItem.getString();
fileItem.getString(“UTF-8”); //如果乱码则使用这个来获得值
获得普通项值
文件上传项:
fileItem.getName();
获得文件上传的文件名的方法
fileItem.getInputStream();
获得文件内容
fileItem.getSize()
获得文件上传的文件大小
fileItem.delete();
删除文件上传过程中的临时文件
代码附上注释:
package com.upload.demo;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.List;
import javax.servlet.ServletException;
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;
public class UploadServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
try {
boolean flag = ServletFileUpload.isMultipartContent(request);
if(!flag) {
request.setAttribute("msg", "表单的格式不正确");
request.getRequestDispatcher("/day03/upload.jsp").forward(request, response);
return;
}
//1.创建磁盘文件项工厂
DiskFileItemFactory diskFileItemFactory = new DiskFileItemFactory();
//1.1设置缓冲区大小
diskFileItemFactory.setSizeThreshold(3*1024*1024);//缓冲区大小为3M
//1.2设置临时文件存放路径:
//获得临时文件存放路径
String tempPath = getServletContext().getRealPath("/temp");
diskFileItemFactory.setRepository(new File(tempPath));
//2.创建核心解析类
ServletFileUpload fileUpload = new ServletFileUpload(diskFileItemFactory);
//设置文件上传的文件大小
fileUpload.setSizeMax(5*1024*1024);
//3.利用核心解析类解析Request,解析后会得到多个部分。返回一个List集合。
//List集合装的是每个部分的内容(FileItem文件项)
List<FileItem> list = fileUpload.parseRequest(request);
//4.遍历List集合,会得到代表每个部分的文件项的对象。根据文件项判断是否是文件上传项
for (FileItem fileItem : list) {
//判断这个文件项是否是普通项还是文件上传项
if (fileItem.isFormField()) {
//普通项:
//接受普通项的值:(接收值不能使用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();
}
//删除临时文件
fileItem.delete();
}
}catch (Exception e) {
e.printStackTrace();
}
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
3.文件上传应用和问题的解决
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 type="text/javascript">
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){
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>
浏览页面如图:可以通过添加按钮实现多文件上传
文件上传兼容浏览器问题
如果使用IE老版本的浏览器出现一个文件名称获取错误问题。IE老版本获取文件名称的时都会带有路径。
解决方法如下:在获取文件名时候,检查一下是否有\然后截取即可
String filename = fileItem.getName();//获得文件名称
int idx = filename.lastIndexOf("\\");
if (idx!=-1) {
//使用的是老版本的浏览器
filename = filename.substring(idx+1);
}
文件上传同一个目录下文件同名的问题
张三向服务器上传了一个文件 aa.txt内容是hello world。李四向服务器上传了一个文件aa.txt 内容 hello Java。后上传的文件将先上传的文件覆盖了。
使用唯一文件名进行解决。
创建一个工具类:生成随机字符串
package com.upload.demo;
import java.util.UUID;
public class UploadUtils {
public static String getUuidFilename(String filename) {
//在java的API中有一个类UUID可以产生随机的字符串
UUID.randomUUID().toString();
int idx = filename.lastIndexOf(".");
String extetions = filename.substring(idx);
return UUID.randomUUID().toString().replace("-", "")+extetions;
}
}
在servlet中使用
//获得唯一文件名
String uuidFilename = UploadUtils.getUuidFilename(filename);
InputStream is = fileItem.getInputStream();
//获得文件上传写入路径,绝对路径
String realPath = getServletContext().getRealPath("/upload");
//创建输出流,写入到设置的路径中
OutputStream os = new FileOutputStream(realPath+"/"+uuidFilename);//这里也要修改。
文件上传同个目录下文件过多的问题
现在所有的用户都上传文件,如果网站访问量比较大,如果都上传到同一个目录下,在同一个目录下存放的文件太多了,也会对程序有影响〈其实打开该目录的时候,都会很卡,更别说读写操作)。
目录分离 :
按时间分离 : 按月、周、天、小时
按用户分离 : 按张三、李四。
按个数分离 : 一个目录下存放3000个文件。
按目录分离算法 : 按照某种特定算法进行分离。
分离算法举例:
-----》例如:上传一个文件,得到一个唯一的文件名。唯一文件名获取其hashcode值。
-----》int类型的值(32位)。让hashCode的值& 0xf;
-----》得出的这个值作为一级目录。让hashcode右移4位& 0xf;
-----》得出的这个值作为二级目录。
以此类推。
//获得唯一文件名
String uuidFilename = UploadUtils.getUuidFilename(filename);
InputStream is = fileItem.getInputStream();
//获得文件上传写入路径,绝对路径
String realPath = getServletContext().getRealPath("/upload");
//进行目录分离
String path = UploadUtils.getRealPath(uuidFilename);
String newPath = realPath + path;
File file = new File(newPath);
if(!file.exists()) {
file.mkdirs();
}
//创建输出流,写入到设置的路径中
OutputStream os = new FileOutputStream(newPath+"/"+uuidFilename);
二、文件下载
文件下载:将服务器上的一个文件,通过流写入到客户端上。
①使用超链接的方式实现文件的下载。
<a href=“文件的路径”>超链接</a>
注意:超链接的方式,如果浏览器不能识别这种格式的文件,提示下载。如果支持该格式的文件,直接打开。
②通过手动编写代码的方式实现文件的下载
设置两个头和一个流
content-Type : 文件的MIME的类型
Content-Disposition : 浏览器支持该格式的文件,提示下载
设置代表该文件的输入流 , 渝出流是固定response.getOutputStream()
1.手动编码方式下载
代码演示:下载资源放在day03的文件夹中
downloadServlet
package com.download.demo;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class DownloadServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//1.接受参数
String filename = request.getParameter("filename");
//2.下载:设置两个头和一个流
//设置Content-Type
String type = getServletContext().getMimeType(filename);
response.setContentType(type);
//设置Content-Disposition
response.setHeader("Content-Disposition", "attachment;filename="+filename);
//设置代表文件的输入流
String path = getServletContext().getRealPath("/day03");
InputStream is = new FileInputStream(path+"/"+filename);
OutputStream os = response.getOutputStream();
byte[] b = new byte[1024];
int len = 0;
while ((len=is.read(b))!=-1) {
os.write(b,0,len);
}
is.close();
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
download.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>
<h1>文件下载:手动编码方式</h1>
<h3><a href="${ pageContext.request.contextPath }/DownloadServlet?filename=02.jpg">02.jpg</a></h3>
<h3><a href="${ pageContext.request.contextPath }/DownloadServlet?filename=a.zip">a.zip</a></h3>
</body>
</html>
运行后网站打开可运行。
中文文件下载
DownloadServlet
package com.download.demo;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URLEncoder;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class DownloadServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//1.接受参数
String filename = new String(request.getParameter("filename").getBytes("ISO-8859-1"),"UTF-8");
//2.下载:设置两个头和一个流
//设置Content-Type
String type = getServletContext().getMimeType(filename);
response.setContentType(type);
//修改如下:
//定义一个代表文件的路径
String path = getServletContext().getRealPath("/day03");
File file = new File(path+"/"+filename);
//判断浏览器类型
String agent = request.getHeader("User-Agent");
if (agent.contains("Firefox")) {
//火狐浏览器
filename = DownloadUtils.base64EncodeFileName(filename);
}else {
//IE或者其他浏览器
filename = URLEncoder.encode(filename,"UTF-8");
}
//设置Content-Disposition
response.setHeader("Content-Disposition", "attachment;filename="+filename);
//设置代表文件的输入流
InputStream is = new FileInputStream(file);
OutputStream os = response.getOutputStream();
byte[] b = new byte[1024];
int len = 0;
while ((len=is.read(b))!=-1) {
os.write(b,0,len);
}
is.close();
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
DownloadUtils
package com.download.demo;
import java.io.UnsupportedEncodingException;
import org.apache.commons.codec.binary.Base64;
public class DownloadUtils {
public static String base64EncodeFileName(String fileName) {
try {
return "?UTF-8?B?"+new String(Base64.encodeBase64(fileName.getBytes("UTF-8")))
+"?=";
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
}
修改后就可以下载中文路径的文件
2.给定目录下的文件下载
给定一个目录(这个目录可以是任意盘符下的任意路径―这个路径下有多少级目录,每级目录中有多少个文件都是未知的)。将这个路径中的文件显示到页面上,在页面上给每个问题件都提供响应下载的链接,当点击这个链接的时候,对该文件进行下载。
downloadList.jsp
<%@page import="java.io.File"%>
<%@page import="java.util.Queue"%>
<%@page import="java.util.LinkedList"%>
<%@ 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>
<h1>树形遍历</h1>
<%
Queue<File> queue = new LinkedList<File>();
File root = new File("E:/resource");
queue.offer(root);
while(!queue.isEmpty()){
File file = queue.poll();
File[] files = file.listFiles();
for(File f : files){
if(f.isFile()){
%>
<h4><a href="${ pageContext.request.contextPath }/DownloadListServlet?filename=<%= f.getAbsolutePath() %>"><%= f.getName() %></a></h4>
<%
}else{
queue.offer(f);
}
}
}
%>
</body>
</html>
DownloadListServlet
package com.download.demo;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URLEncoder;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class DownloadListServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String path = request.getParameter("filename");
if (path!=null) {
path = new String(path.getBytes("ISO-8859-1"),"UTF-8");
}
File file = new File(path);
//实现文件下载:设置两个头和一个流:
//获得文件名
String filename = file.getName();
response.setContentType(getServletContext().getMimeType(filename));
//设置另一个头:
String agent = request.getHeader("User-Agent");
if (agent.contains("Firefox")) {
filename = DownloadUtils.base64EncodeFileName(filename);
}else {
filename = URLEncoder.encode(filename,"UTF-8");//如果有空格,会把他编码为+
filename = filename.replace("+", " ");//把+替换成空格就不会有小问题
}
response.setHeader("Content-Disposition", "attachment;filename="+filename);
InputStream is = new FileInputStream(file);
OutputStream os = response.getOutputStream();
int len = 0;
byte[] b = new byte[1024];
while ((len=is.read(b))!=-1) {
os.write(b,0,len);
}
is.close();
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
注意:如果有报错400,后端无效字符解析错误。则可以去修改servlet.xml配置
<Connector port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443"
URIEncoding="UTF-8"
URLEncoding="UTF-8"
useBodyEncodingForURI="true"
relaxedPathChars="|{}[],%.\"
relaxedQueryChars="|{}[],%.\" />
原因是提交的get方式的url中有tomcat解析不了的字符,极大可能是\这个字符没有解析进去。