目录
一、文件上传和下载概述
1.1、web页面实现上传文件的注意事项
1.1.1、概述
1.1.2、总结
1.2、在Servlet中读取文件上传数据,并保存到本地硬盘中
1.2.1、概述
1.2.2、Commons-fileupload 文件上传组件
1.3、手动实现文件上传示例
1.3.1、web页面
1.3.2、servlet
1.3.3、效果
二、Apache提供的文件上传组件fileupload
2.1、所需jar包
2.2、fileupload工作流程
2.3、DiskFileItemFactory
2.3.1、常用方法
2.4、ServletFileUpload
2.4.1、常用方法
三、上传文件案例
3.1、实现步骤
3.2、多个文件上传的javascript编码
3.3、上传文件处理细节
四、文件下载
4.1、web应用中实现文件
4.2、示例
4.2.1、相关组件
4.2.2、FileServlet
4.2.3、Downlist.jsp
4.2.4、index.jsp
4.2.5、upload.jsp
一、文件上传和下载概述
1.1、web页面实现上传文件的注意事项
1.1.1、概述
1、在web页面中通过<input type=“file”>标签实现文件的上传,此标签必须设置name属性,name属性值随意起但尽量要有意义。
2、form表单的enctype属值设为multipart/form-data.method 属性设置为post方式。设置该值后,浏览器在上传文件时,将把文件数据附带在http请求消息体中,并使用MIME协议对上传的文件进行描述,以方便接收方对上传数据进行解析和处理。
1.1.2、总结
提交方式:post
web页面中添加文件上传输入项:<input type=”file” name="随意命名"/>
指定表单类型:
默认类型:enctype="application/x-www-form-urlencoded"
文件上传类型:multipart/form-data
示例:
<form name="file_name" action="${pageContext.request.contextPath }/upload" method="post" enctype="multipart/form-data">
</form>
1.2、在Servlet中读取文件上传数据,并保存到本地硬盘中
1.2.1、概述
Request对象提供的getInputStream方法以读取客户端提交过来的数据,当用户上传多个文件在servlet端解析文件数据十分麻烦。为方便处理用户上传的文件数据,
Apache推出Commons-fileupload 组件(开源组件),
性能优异,并且其API使用极其简单。
1.2.2、Commons-fileupload 文件上传组件
1.3、手动实现文件上传示例
1.3.1、web页面
<%@ page language="java" import="java.util.*" pageEncoding="utf-8"%>
<%
String path = request.getContextPath();
String basePath = request.getScheme() + "://"
+ request.getServerName() + ":" + request.getServerPort()
+ path + "/";
%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<base href="<%=basePath%>">
<title>Upload</title>
<meta http-equiv="pragma" content="no-cache">
<meta http-equiv="cache-control" content="no-cache">
<meta http-equiv="expires" content="0">
<meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
<meta http-equiv="description" content="This is my page">
</head>
<body>
<form name="upload_name"
action="${pageContext.request.contextPath}/upload" method="post"
enctype="multipart/form-data">
用户名:<input type="text" name="username" /> <br />
文 件: <input type="file" name="file_img"> <br />
<input type="submit" value="注册" />
</form>
</body>
</html>
1.3.2、servlet
package cn.itcase.upload;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class Upload extends HttpServlet {
/**
*
*/
private static final long serialVersionUID = 1L;
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
/*
request.getParameter(""); // GET/POST
request.getQueryString(); // 获取GET提交的数据
request.getInputStream(); // 获取post提交的数据
编码格式设置
request.setCharacterEncoding("UTF-8")
response.setCharacterEncoding("UTF-8")
response.setContentType("text/html; charset=UTF-8");
response.setHeader("Content-Type", "text/html; charset=UTF-8");
流
字符流 高级流
Reader读取
Writer写出
字节流 低级流
InputStream 输入流 转换为 XXXReader
OutputStream 输出留 转换为 XxxWriter
*/
// setContentType设置浏览器的编码格式
request.setCharacterEncoding("UTF-8");
//response.setCharacterEncoding("UTF-8");
// 1.获取表单流数据 字节流 获取数据
InputStream in = request.getInputStream();
// 2.转换流 字符流 便于操作
InputStreamReader inStream = new InputStreamReader(in);
// 3.缓冲流
BufferedReader reader = new BufferedReader(inStream);
// 4.输出数据
String str = null;
while((str = reader.readLine()) != null){
System.out.println(str);
}
// 5.关闭流
reader.close();
inStream.close();
in.close();
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
this.doGet(request,response);
}
}
1.3.3、效果
------WebKitFormBoundaryZAZepyvkbF8hZAVY
Content-Disposition: form-data; name="username"
hello
------WebKitFormBoundaryZAZepyvkbF8hZAVY
Content-Disposition: form-data; name="file_img"; filename="htllo.txt"
Content-Type: text/plain
Hello
hello world
------WebKitFormBoundaryZAZepyvkbF8hZAVY--
需要把它解析到web页面上,使用传统的方式来实现比较繁琐且开发效率低
所以需要用工具来实现文件的上传
二、Apache提供的文件上传组件fileupload
2.1、所需jar包
Commons-fileuploadcommons-io
2.2、fileupload工作流程
2.3、DiskFileItemFactory
DiskFileItemFactory 是创建 FileItem 对象的工厂
2.3.1、常用方法
1、构造器/构造方法
public DiskFileItemFactory(int sizeThreshold, java.io.File repository)
2、设置内存缓冲区的大小,默认值为10K。当上传文件大于缓冲区大小时, fileupload组件将使用临时文件缓存上传文件
public void setSizeThreshold(int sizeThreshold)
3、指定临时文件目录,默认值为System.getProperty("java.io.tmpdir")
public void setRepository(java.io.File repository)
2.4、ServletFileUpload
负责处理上传的文件数据,并将表单中每个输入项封装成一个 FileItem 对象中
2.4.1、常用方法
1、判断上传表单是否为multipart/form-data类型
boolean isMultipartContent(HttpServletRequest request)
2、解析request对象,并把表单中的每一个输入项包装成一个fileItem 对象,并返回一个保存了所有FileItem的list集合
List parseRequest(HttpServletRequest request)
3、设置上传文件的最大值
setFileSizeMax(long fileSizeMax)
4、设置上传文件总量的最大值
setHeaderEncoding(java.lang.String encoding)
5、设置编码格式
setProgressListener(ProgressListener pListener)
三、上传文件案例
3.1、实现步骤
1、创建DiskFileItemFactory对象,设置缓冲区大小和临时文件目录
2、使用DiskFileItemFactory 对象创建ServletFileUpload对象,并设置上传文件的大小限制。
3、调用ServletFileUpload.parseRequest方法解析request对象,得到一个保存了所有上传内容的List对象。
4、对list进行迭代,每迭代一个FileItem对象,调用其isFormField方法判断是否是上传文件
为普通表单字段,则调用getFieldName、getString方法得到字段名和字段值
为上传文件,则调用getInputStream方法得到数据输入流,从而读取上传数据。
3.2、多个文件上传的javascript编码
每次动态增加一个文件上传输入框,都把它和删除按纽放置在一个单独的div中,并对删除按纽的onclick事件进行响应,使之删除删除按纽所在的div。
如:
this.parentNode.parentNode.removeChild(this.parentNode);
3.3、上传文件处理细节
1、中文文件乱码问题
文件名中文乱码问题,可调用ServletUpLoader的setHeaderEncoding方法,或者设置request的setCharacterEncoding属性
2、临时文件的删除问题
由于文件大小超出DiskFileItemFactory.setSizeThreshold方法设置的内存缓冲区的大小时,Commons-fileupload组件将使用临时文件保存上传数据,因此在程序结束时,务必调用FileItem.delete方法删除临时文件。
Delete方法的调用必须位于流关闭之后,否则会出现文件占用,而导致删除失败的情况。
3、文件存放位置
为保证服务器安全,上传文件应保存在应用程序的WEB-INF目录下,或者不受WEB服务器管理的目录。
为防止多用户上传相同文件名的文件,而导致文件覆盖的情况发生,文件上传程序应保证上传文件具有唯一文件名。
为防止单个目录下文件过多,影响文件读写速度,处理上传文件的程序应根据可能的文件上传总量,选择合适的目录结构生成算法,将上传文件分散存储。
4、ProgressListener显示上传进度
ProgressListener progressListener = new ProgressListener() {
public void update(long pBytesRead, long pContentLength, int pItems) {
System.out.println("到现在为止, " + pBytesRead + " 字节已上传,总大小为 "
+ pContentLength);
}
};
upload.setProgressListener(progressListener);
5、以KB为单位显示上传进度
long temp = -1; //temp注意设置为类变量
long ctemp = pBytesRead /1024;
if (mBytes == ctemp)
return;
temp = mBytes;
四、文件下载
4.1、web应用中实现文件
超链接直接指向下载资源程序实现下载需设计连个响应头
设置Content-Type 的值为:application/x-msdownload。
Web 服务器需要告诉浏览器其所输出的内容的类型不是普通的文本文件或 HTML 文件,而是一个要保存到本地的下载文件。
Web 服务器希望浏览器不直接处理相应的实体内容,而是由用户选择将相应的实体内容保存到一个文件中,这需要设置 Content-Disposition 报头。
该报头指定了接收程序处理数据内容的方式,在 HTTP 应用中只有 attachment 是标准方式,attachment 表示要求用户干预。
在 attachment 后面还可以指定 filename 参数,该参数是服务器建议浏览器将实体内容保存到文件中的文件名称。
在设置 Content-Dispostion 之前一定要指定 Content-Type.
response.setContentType("application/x-msdownload");
String str = "attachment;filename="+java.net.URLEncoder.encode(fileName,"UTF-8");
response.setHeader("Content-pisposition",str);
因为要下载的文件可以是各种类型的文件并且要将文件传送给客户端,其相应内容应该被当做二进制来处理,所以应该调用response.getOutputStream();方法返回 ServeltOutputStream 对象来向客户端写入文件内容。
ServletOutputStream so = response.getOutputStream();
byte[] data = new byte[2048];
int len = 0;
while((len = is.read(data))>0){
so.write(data,o,len);
}
4.2、示例
4.2.1、相关组件
4.2.2、FileServlet
package cn.itcase.upload;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
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.FileItemFactory;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
/**
* 处理文件的上传与下载
*
* @author Administrator
*
*/
public class FileServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 获取请求参数:区分不同的操作类型
String method = request.getParameter("method"); // 根据参数名获取参数值
if ("upload".equals(method)) { // 如果method==upload则执行条件
// 上传
upload(request, response);
} else if ("downList".equals(method)) {
// 进入下载列表
downList(request, response);
} else if ("down".equals(method)) {
// 下载
down(request, response);
}
}
/**
* 处理下载
*/
private void down(HttpServletRequest request, HttpServletResponse response) {
try {
// 获取用户下载的文件名称(url地址后面追加数据,get)
String fileName = request.getParameter("fileName");
fileName = new String(fileName.getBytes("ISO8859-1"));
// 获取上传的目录路径
String basePath = getServletContext().getRealPath("/upload");
// 获取一个文件流
InputStream in = new FileInputStream(new File(basePath, fileName));
// 如果文件名是中文,需要进行url编码
fileName = URLEncoder.encode(fileName, "UTF-8");
// 设置下载的响应头
response.setHeader("content-disposition", "attachment;fileName="
+ fileName);
// 获取response字节流
OutputStream out = response.getOutputStream();
byte[] b = new byte[1024];
int len = -1;
while((len = in.read(b))!=-1){
out.write(b,0,len);
}
// 关闭
out.close();
in.close();
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
/**
* 进入下载列表
*
* @param request
* @param response
*/
private void downList(HttpServletRequest request,
HttpServletResponse response) {
try {
// 实现思路:先获取upload目录下所有文件的文件名,再保存;跳转到down.jsp列表展示
// 1. 初始化map集合Map<包含唯一标记的文件名, 简短文件名>;
Map<String, String> FileNames = new HashMap<String, String>();
// 2. 获取上传目录,及其下所有的文件的文件名
String bathPath = getServletContext().getRealPath("/upload");
// 目录
File file = new File(bathPath);
// 目录下,所有文件名
String list[] = file.list();
// 遍历,封装
if (list != null && list.length > 0) {
for (int i = 0; i < list.length; i++) {
// 全名
String fileName = list[i];
// 短名
String shortName = fileName.substring(fileName
.lastIndexOf("#") + 1);
// 封装
FileNames.put(fileName, shortName);
}
}
// 3 保存到request域
request.setAttribute("FileNames", FileNames);
// 4 转发
request.getRequestDispatcher("/downlist.jsp").forward(request,
response);
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
/**
* 文件上传
*
* @param request
* @param response
*/
private void upload(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
try {
// 1.创建工场对象
FileItemFactory factory = new DiskFileItemFactory();
// 2.文件上传核心工具
ServletFileUpload upload = new ServletFileUpload(factory);
// 设置大小限制参数
upload.setFileSizeMax(10 * 1024 * 1024);// 单个文件大小限制
upload.setSizeMax(50 * 1024 * 1024); // 总文件大小限制
upload.setHeaderEncoding("utf-8"); // 请求头编码处理 对中文文件编码处理
// 3.判断
if (upload.isMultipartContent(request)) {
// 把请求数据转换为list集合
List<FileItem> list = upload.parseRequest(request);
// 遍历
for (FileItem item : list) {
// 判断:普通文本数据
if (item.isFormField()) {
// 获取名称
String name = item.getFieldName();
// 获取值
String value = item.getString();
System.out.println(value);
}// 文件表单项
else {
/* 文件上传 */
// 1.1 获取文件名称
String name = item.getName();
/* 处理文件名重名问题 */
// 1.2 先得到唯一标记
String id = UUID.randomUUID().toString();
// 1.3 拼接文件名
name = id + "#" + name;
// 2 得到上传目录
String basePath = getServletContext().getRealPath(
"/upload");
// 3 创建要上传的文件对象
File file = new File(basePath, name);
// 4 上传
item.write(file);
item.delete(); // 删除组件运行产生的临时文件
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
this.doGet(request, response);
}
}
4.2.3、Downlist.jsp
<%@ page language="java" import="java.util.*" pageEncoding="utf-8"%>
<!-- 导入核心标签库 jstl c标签库 基础见:https://mp.csdn.net/postedit/90279303这篇博客 -->
<%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<%
String path = request.getContextPath();
String basePath = request.getScheme() + "://"
+ request.getServerName() + ":" + request.getServerPort()
+ path + "/";
%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<base href="<%=basePath%>">
<title>My JSP 'Downlist.jsp' starting page</title>
<meta http-equiv="pragma" content="no-cache">
<meta http-equiv="cache-control" content="no-cache">
<meta http-equiv="expires" content="0">
<meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
<meta http-equiv="description" content="This is my page">
</head>
<body>
<table border="1" align="center">
<tr>
<th>序号</th>
<th>文件名</th>
<th>操作</th>
</tr>
<%--
jstl标签常用属性释义:
begin="" : 从哪个元素开始遍历,从0开始.默认从0开始
end="": 到哪个元素结束。默认到最后一个元素
step="" : 步长 (每次加几) ,默认1
items="": 需要遍历的数据(集合)
var="": 每个元素的名称
varStatus="": 当前正在遍历元素的状态对象。(count属性:当前位置,从1开始)
--%>
<c:forEach var="en" items="${requestScope.fileNames}" varStatus="vs">
<tr>
<td>${vs.count }</td>
<td>${en.value }</td>
<td>
<%--<a href="${pageContext.request.contextPath }/FileServlet?method=down&..">下载</a>--%>
<!-- 构建一个地址 --> <c:url var="url" value="FileServlet">
<c:param name="method" value="down"></c:param>
<c:param name="fileName" value="${en.key}"></c:param>
</c:url> <!-- 使用上面地址 --> <a href="${url }">下载</a>
</td>
</tr>
</c:forEach>
</table>
</body>
</html>
4.2.4、index.jsp
<%@ page language="java" import="java.util.*" pageEncoding="utf-8"%>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<base href="<%=basePath%>">
<title>My JSP 'index.jsp' starting page</title>
<meta http-equiv="pragma" content="no-cache">
<meta http-equiv="cache-control" content="no-cache">
<meta http-equiv="expires" content="0">
<meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
<meta http-equiv="description" content="This is my page">
</head>
<body>
<a href="${pageContext.request.contextPath}/upload.jsp">文件上传</a><br/>
<a href="${pageContext.request.contextPath}/fileServlet?method=downList">文件下载</a><br/>
</body>
</html>
4.2.5、upload.jsp
<%@ page language="java" import="java.util.*" pageEncoding="utf-8"%>
<%
String path = request.getContextPath();
String basePath = request.getScheme() + "://"
+ request.getServerName() + ":" + request.getServerPort()
+ path + "/";
%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<base href="<%=basePath%>">
<title>My JSP 'upload.jsp' starting page</title>
<meta http-equiv="pragma" content="no-cache">
<meta http-equiv="cache-control" content="no-cache">
<meta http-equiv="expires" content="0">
<meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
<meta http-equiv="description" content="This is my page">
</head>
<body>
<form name="frm_test"
action="${pageContext.request.contextPath }/fileServlet?method=upload"
method="post" enctype="multipart/form-data">
<%--<input type="hidden" name="method" value="upload">--%>
用户名:
<input type="text" name="userName">
<br /> 文件:
<input type="file" name="file_img">
<br />
<input type="submit" value="提交">
</form>
</body>
</html>