文件上传
实现web开发中的文件上传功能,操作步骤:
1.在web页面中添加上传输入项。
2.在servlet中读取上传文件的数据,并保存到本地硬盘中。
<input type=“file”>标签用于在web页面中添加文件上传输入项,设置文件上传输入项时
须注意:
附加知识:1.必须要设置input输入项的name属性,否则浏览器将不会发送上传文件的数据。2.必须把form的enctype属值设为“ multipart/form-data ”2.必须把form的method属性设置为post方式。
enctype属性规定在发送表单数据之前如何对其进行编码。属性可能的值:
application/x-www-form-urlencoded 在发送前编码所有字符(默认)。multipart/form-data 不对字符编码。在使用包含文件上传控件的表单时,必须使用该值。text/plain 空格转换为 "+" 加号,但不对特殊字符编码。
方式1:手动实现文件上传
案例中上传的文件a.txt
web页面:
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>手动执行文件上传</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">
<!--
<link rel="stylesheet" type="text/css" href="styles.css">
-->
</head>
<body>
<form action="${pageContext.request.contextPath }/TestServlet" method="post" enctype="multipart/form-data">
用户名:<input type="text" name="userName"/><hr/>
文件:<input type="file" name="file1"/> <input type="submit" value="提交"/>
</form>
</body>
</html>
Servlet手动获取上传文件
package com.cn.servlet;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import javax.servlet.ServletException;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* 手动获取文件上传
* @author liuzhiyong
*
*/
public class TestServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
//获取表单(POST)提交的数据流
ServletInputStream in = request.getInputStream();
//转换流
InputStreamReader inStream = new InputStreamReader(in);
//缓冲流
BufferedReader reader = new BufferedReader(inStream);
//输出数据
String str = null;
while((str=reader.readLine()) != null){
System.out.println(str);
}
//关闭
reader.close();
inStream.close();
in.close();
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
this.doGet(request, response);
}
}
效果:
由于手动上传文件,需要另外去解析,所以使用现成的工具,详见方式2.
方式2:文件上传组件(FileUpload组件,推荐)
文件上传功能开发中很常用,Apache组织也提供了文件上传组件,FileUpload组件。
FileUpload组件使用步骤:
下载组件,引入jar文件
commons-fileupload-1.2.1.jar
commons-io-1.4.jar
共2个jar包,
点击打开链接,即可使用其API了。
FileUpload组件API:
|-Interface FileItemFactory 文件上传工厂类(把每一个请求表单项封装为一个个FileItem对象)
|--Class DiskFileItemFactory
|----void setRepository(java.io.File repository) 设置临时缓存目录
|-Class ServletFileUpload 文件上传核心类对象,可以获取所有的FileItem对象
|----List parseRequest(javax.servlet.http.HttpServletRequest request) 获取所有文件上传项FileItem
|----boolean isMultipartContent(javax.servlet.http.HttpServletRequest request)
判断上传表单是否为multipart/form-data类型(即
判断当前表单是否为文件上传表单
),如果是返回true
|----void setFileSizeMax(long fileSizeMax) 设置单个文件上传最大值
|----void setSizeMax(long sizeMax) 设置总的文件最大大小
|----void setHeaderEncoding(java.lang.String encoding) 设置上传的文件名的编码,相当于request.setCharacterEncoding(encoding)
|-Interface FileItem 封装了普通表单项数据以及文件上传表单数据
|----String getFieldName() 获取上传表单元素名称
|----String getString() 获取上传元素值
|----String getString(java.lang.String encoding) 获取上传元素值,并处理格式
|----String getContentType() 获取上传文件类型【仅上传文件表单项有数据】
|----InputStream getInputStream() 获取post方式提交上来的上传文件流【仅文件上传表单项有数据】
|----String getName() 获取上传文件名
|----void write(java.io.File file) 写文件到指定文件
|----void delete() 删除临时文件
使用FileUpload组件测试:
web页面
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>手动执行文件上传</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">
<!--
<link rel="stylesheet" type="text/css" href="styles.css">
-->
</head>
<body>
<form action="${pageContext.request.contextPath }/FileUpLoadServlet" method="post" enctype="multipart/form-data">
用户名:<input type="text" name="userName"/><hr/>
文件:<input type="file" name="file1"/> <input type="submit" value="提交"/>
</form>
</body>
</html>
Servlet使用组件获取数据
package com.cn.servlet;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
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.FileItemFactory;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
/**
* 文件上传组件使用
* @author liuzhiyong
*
*/
public class FileUpLoadServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
//1.创建文件上传工厂类
DiskFileItemFactory fileItemFactory = new DiskFileItemFactory();
// fileItemFactory.setRepository(repository);//设置临时目录
//2.创建文件上传核心类对象,可以获取所有的FileItem对象
ServletFileUpload upload = new ServletFileUpload(fileItemFactory );
// upload.setFileSizeMax(fileSizeMax);//设置单个文件上传最大值
// upload.setSizeMax(sizeMax);//设置总的文件最大大小
// upload.setHeaderEncoding(encoding);//设置上传的文件名的编码,相当于request.setCharacterEncoding(encoding);
//判断当前表单是否为文件上传表单,如果是返回true
if(ServletFileUpload.isMultipartContent(request)){
//3.把请求数据转换为FileItem对象的集合
try {
List<FileItem> list = upload.parseRequest(request);//获取所有文件上传项FileItem
//遍历,得到每一个上传项
for(FileItem item : list){
//判断是普通表单项,还是文件上传表单项
if(item.isFormField()){//普通表单
String fieldName = item.getFieldName();//表单元素(这里是文本框)名称
String content = item.getString();//表单元素(这里是文本框)值
// String content = item.getString("utf-8");//表单元素(这里是文本框)值,并处理编码
}else{//文件上传表单
String fieldName = item.getFieldName();//表单元素名称
String contentType = item.getContentType();//上传文件类型
String name = item.getName();//文件名
InputStream in = item.getInputStream();//文件流
String content = item.getString();//文件内容
InputStreamReader inStream = new InputStreamReader(in);
//缓冲流
BufferedReader reader = new BufferedReader(inStream);
//输出数据
String str = null;
while((str=reader.readLine()) != null){
System.out.println(str);
}
item.write(new File("d:/aa文件.txt"));//写文件
item.delete();//删除临时文件
//关闭
reader.close();
inStream.close();
in.close();
}
}
} catch (Exception e) {
//测试
e.printStackTrace();
}
}else{
System.out.println("当前表单不是文件上传表单,不处理!");
}
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
this.doGet(request, response);
}
}
效果:
控制台下直接输出文件流内容
FileItem类write()写文件到硬盘的效果:
写出的文件内容如下:
文件的上传下载完整案例
需求:
文件上传完整案例
1.设置单个文件不能超过30M
2.设置总大小不能超过50M
3.上传目录:上传到项目资源目录下的upload目录
4.上传文件不能覆盖,解决上传文件名的同名问题
web开始页面:index.jsp
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>文件上传与下载</title>
</head>
<body>
<a href="${pageContext.request.contextPath }/upload.jsp">文件上传</a>
<hr/>
<a href="${pageContext.request.contextPath }/FileServlet?method=downList">文件下载列表</a>
</body>
</html>
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>手动执行文件上传</title>
</head>
<body>
<form action="${pageContext.request.contextPath }/FileServlet?method=upload" method="post" enctype="multipart/form-data">
用户名:<input type="text" name="userName"/><hr/>
文件:<input type="file" name="file1"/>
<input type="submit" value="提交"/>
</form>
<%-- 不提交文件上传表单,测试 --%>
<a href="${pageContext.request.contextPath }/FileServlet?method=upload">访问FileUploadServlet试试</a>
</body>
</html>
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%-- 引入jstl核心标签库 --%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<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">
<!--
<link rel="stylesheet" type="text/css" href="styles.css">
-->
</head>
<body>
<table>
<tr>
<th>序号</th>
<th>文件</th>
<th>操作</th>
</tr>
<c:forEach items="${requestScope.fileNameMap }" var="entry" varStatus="varStatus">
<tr>
<td>${varStatus.count }</td>
<td>${entry.value }</td>
<td>
<%-- 方式1 --%>
<%--<a href="${pageContext.request.contextPath}/FileServlet?method=download&fileName=${entry.key}">下载</a>--%>
<%-- 方式2:在JSP页面中构造一个URL地址 。。注意context不写,表示默认当前项目路径下 --%>
<c:url var="url" value="/FileServlet" context="${pageContext.request.contextPath}">
<c:param name="method" value="download"></c:param>
<c:param name="fileName" value="${entry.key }"></c:param>
</c:url>
<%-- 使用上面的地址 --%>
<a href="${url }">下载</a>
</td>
</tr>
</c:forEach>
</table>
</body>
</html>
Servlet处理
package com.cn.servlet;
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 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.ProgressListener;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
/**
* 处理文件上传与下载:
* 上传功能:
1.设置单个文件不能超过30M
2.设置总文件大小不能超过50M
3.上传目录:上传到项目资源目录下的upload目录
4.上传文件不能覆盖,解决上传文件名的同名问题
下载功能:
下载文件
* @author liuzhiyong
*
*/
public class FileServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
//获取请求参数,区分不同的操作类型
String method = request.getParameter("method");
if("upload".equals(method)){
upload(request, response);
}else if("downList".equals(method)){
downList(request, response);
}else if("download".equals(method)){
download(request, response);
}
}
/**
* 进入下载刘表
* 思路:
* 先获取upload目录下所有文件的文件名,再保存,跳转到downList.jsp列表展示
* @param request
* @param response
* @throws ServletException
* @throws IOException
*/
private void downList(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException{
//1.初始化map集合Map<包含唯一标记的文件名, 简单文件名>
Map<String, String> fileNameMap = new HashMap<String, String>();
//2.获取上传目录,及其下所有文件的文件名
String bathPath = this.getServletContext().getRealPath("/upload");
//上传目录
File file = new File(bathPath);
//获取上传目录下,所有文件名
String[] list = file.list();//返回目录下的文件或者目录名,包含隐藏文件。
//遍历,封装
if(list!=null && list.length>0){
for(String str : list){
//全名
String fileName = str;
//获取全名字符#后面的短名
String shortName = fileName.substring(fileName.lastIndexOf("#")+1);
//将全名和短命封装到Map集合中
fileNameMap.put(fileName, shortName);
}
}
//3.保存到request域对象中
request.setAttribute("fileNameMap", fileNameMap);
//4.转发到下载列表downList.jsp页面
request.getRequestDispatcher("/downList.jsp").forward(request, response);
}
/**
* 处理下载
* @param request
* @param response
* @throws ServletException
* @throws IOException
*/
private void download(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException{
//获取用户下载的文件名称(url地址后面追加的参数fileName的值,此参数是GET方式提交的,所以后面需要处理编码问题)
String fileName = request.getParameter("fileName");
//处理编码
fileName = new String(fileName.getBytes("iso-8859-1"), "utf-8");
//现获取文件上传的目录路径
String basePath = this.getServletContext().getRealPath("/upload");
//文件对象
File file = new File(basePath, fileName);
//获取一个文件输入字节流对象
FileInputStream in = new FileInputStream(file);
//如果文件名是中文,需要进行url编码,不然下载后中文不显示
fileName = URLEncoder.encode(fileName, "utf-8");
/**
程序实现下载需设置两个响应头:
设置Content-Type 的值为:application/x-msdownload。Web 服务器需要告诉浏览器其所输出的内容的类型不是普通的文本文件或 HTML 文件,而是一个要保存到本地的下载文件。
Web 服务器希望浏览器不直接处理相应的实体内容,而是由用户选择将相应的实体内容保存到一个文件中,这需要设置 Content-Disposition 报头。该报头指定了接收程序处理数据内容的方式,
在 HTTP 应用中只有 attachment 是标准方式,attachment 表示要求用户干预。在 attachment 后面还可以指定 filename 参数,
该参数是服务器建议浏览器将实体内容保存到文件中的文件名称。在设置 Content-Dispostion 之前一定要指定 Content-Type.
*/
//设置下载的响应头
// response.setContentType("application/x-msdownload");//但是我发现这里其实可以不加
response.setHeader("content-disposition", "attachment;fileName=" + fileName);
//获取response字节流
OutputStream out = response.getOutputStream();//因为要下载的文件可以是各种类型的文件,所以要将文件传送给客户端,其相应内容应该被当做二进制来处理,所以应该调用输出字节流来向客户端写入文件内容。
//缓冲数组
byte[] buff = new byte[1024];
int len = -1;
while((len = in.read(buff)) != -1){
out.write(buff, 0, buff.length);
}
//关闭资源
out.close();
in.close();
}
/**
* 处理上传
* @param request
* @param response
* @throws ServletException
* @throws IOException
*/
private void upload(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException{
try {
//1.创建文件上传工厂类(把每一个请求表单项封装为一个个FileItem对象)
DiskFileItemFactory factory = new DiskFileItemFactory();
//2.创建文件上传核心类对象(可以获取所有的FileItem对象)
ServletFileUpload upload = new ServletFileUpload(factory);
// //【需求1:设置单个文件不能超过30M】
// upload.setFileSizeMax(30*1024*1024);//30M
// //【需求2:设置总文件大小不超过50M】
// upload.setSizeMax(50*1024*1024);//50M
//【需求1:设置单个文件不能超过200M】
upload.setFileSizeMax(200*1024*1024);//200M
//【需求2:设置总文件大小不超过300M】
upload.setSizeMax(300*1024*1024);//300M
upload.setHeaderEncoding("utf-8");//设置上传的文件名的编码,若果没有设置编码,当上传文件名为中文时,会出现乱码。
/**
* ProgressListener显示上传进度
*/
ProgressListener progressListener = new ProgressListener(){
@Override
public void update(long pBytesRead, long pContentLength, int pItems) {
System.out.println("到现在为止, " + pBytesRead/1024 + " KB已上传,总大小为 " + pContentLength/1024 + "KB");
}
};
upload.setProgressListener(progressListener);
//判断:上传表单是否为multipart/form-data类型
if(ServletFileUpload.isMultipartContent(request)){
//3.把请求数据转换为FileItem的集合
List<FileItem> list = upload.parseRequest(request);
//遍历list
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();
/**
* 问题:文件重命名,防止上传后覆盖
* 解决:给用户添加一个唯一标记
*/
//随机生成一个唯一标记
String uuid = UUID.randomUUID().toString().replace("-", "");
name = uuid + "#" + name;
//获取上传的目录路径
String basePath = this.getServletContext().getRealPath("/upload");// /斜杠代表当前服务器项目路径下
//创建文件对象
File file = new File(basePath, name);
//写文件
InputStream in = item.getInputStream();
item.write(file);
in.close();//关闭流
item.delete();//删除临时文件
}
}
}else{
System.out.println("不是文件上传表单,不处理!");
}
} catch (Exception e) {
e.printStackTrace();
}
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
this.doGet(request, response);
}
}