通用文件上传
通用文件上传就是在Servlet 3.0之前我们要实现文件上传,必须借助apache的commons-fileupload、commons-io包。
1. 添加lib包到WEB-INF/lib目录:
commons-fileupload-1.4.jar
commons-io-2.2.jar
2.客户端网页
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>通用文件上传</title>
</head>
<body>
<!-- 如果说表单需要上传文件,必须将enctype设置为multipart/form-data,method必须设置成POST -->
<form action="upload" method="post" enctype="multipart/form-data" >
<p>姓名:<input type="text" name="username"></p>
<p>选择头像:<input type="file" name="myFile"></p>
<input type="submit" value="上传">
</form>
</body>
</html>
3.后台Servlet
package com.lanou3g;
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
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.FileUploadException;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
/**
* 通用文件上传:
*
* 适用于:任何版本的Servlet
*
* 需要借助apache的commons-io、commons-fileupload包
*
*/
@WebServlet("/upload")
public class UploadServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
// 设置超过2M的文件就要写到临时目录(单位:byte)(这个只是决定要不要写临时文件,不能决定是否可以上传)
public static final int MAX_THRESHOLD_SIZE = 2 * 1024 * 1024;
// 设置能上传多大的文件(单位:byte)(上传的文件如果超过此大小,直接上传失败)
public static final int MAX_FILE_SIZE = 10 * 1024 * 1024;
// 设置请求中所有数据的大小上限(包括普通参数和文件)(单位:byte)(请求数据如果超过此大小,请求失败)
public static final int MAX_REQUEST_SIZE = 20 * 1024 * 1024;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("text/html;charset=UTF-8");
resp.getWriter().println("<script>alert('文件上传不支持GET请求!');location.href='upload3.jsp';</script>");
}
@Override
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 设置编码方式
request.setCharacterEncoding("UTF-8");
response.setContentType("text/html;charset=UTF-8");
PrintWriter out = response.getWriter();
out.println("<h1>文件上传后台</h1><br />");
out.println("<ul>");
// 只是为了配合示例(非文件上传必要步骤)
List<String> uploadFileNames = new ArrayList<String>();
// form表单如果设置成multipart/form-data格式,使用普通的获取参数方法时无法拿到数据的
// String userName = request.getParameter("username");
try {
// 判断当前请求是否是multipart/form-data请求
if (!ServletFileUpload.isMultipartContent(request)) {
// 普通(非文件上传)后台处理逻辑
// String userName = request.getParameter("username");
return;
}
通用文件上传逻辑开始
// 1. 创建文件上传工厂对象
DiskFileItemFactory factory = new DiskFileItemFactory();
// 设置文件上传工厂参数(可选)
// 设置文件超过多大就需要写入临时文件
factory.setSizeThreshold(MAX_THRESHOLD_SIZE);
// 设置临时文件存储的目录
factory.setRepository(new File("E:/11/upload_temp"));
// 2. 创建文件上传对象
ServletFileUpload upload = new ServletFileUpload(factory);
// 设置文件上传参数(可选)
// upload.setHeaderEncoding("");
// 设置单个文件最大能上传的大小
upload.setFileSizeMax(MAX_FILE_SIZE);
// 设置整个请求中最大能传输的大小
upload.setSizeMax(MAX_REQUEST_SIZE);
// upload.setProgressListener(pListener);
// 3. 解析请求:将请求中表单提交上来的域提取出来
// FileItem接口代表multipart/form-data表单中提交上来的每个域(包括普通文本域和文件域)
List<FileItem> fileItems = upload.parseRequest(request);
// 4. 循环处理每个表单域
for (FileItem fileItem : fileItems) {
// 判断是否是文本表单域
if (fileItem.isFormField()) {
// 获取表单input的name属性值
String inputName = fileItem.getFieldName();
// 获取对应的value
// 不建议使用无参的getString,因为表单中如果填写了中文数据,会乱码
// String value = fileItem.getString();
String value = fileItem.getString("UTF-8");
out.println("<li>表单中提交的普通参数:" + inputName + " ->" + value + "</li>");
continue;
}
// 4.1 获取上传文件相关参数
// 文件上传表单域(<input type="file" name="xx" />)
String inputName = fileItem.getFieldName(); // 获取表单input的name属性值
String fileName = fileItem.getName(); // 获取上传的文件名称
if(fileName == null || fileName.trim().equals("")) {
continue;
}
// fileItem.getInputStream(); //获取上传文件对应的输入流
String contentType = fileItem.getContentType(); // 获取上传文件的类型
long fileSize = fileItem.getSize(); // 获取上传文件的大小
// fileItem.getOutputStream(); // 获取上传文件对应的输出流
out.println("<li>表单中上传文件域的name值:" + inputName + "</li>");
out.println("<li>表单中上传的文件名称:" + fileName + "</li>");
out.println("<li>表单中上传的文件类型:" + contentType + "</li>");
out.println("<li>表单中上传的文件大小(byte):" + fileSize + "</li>");
uploadFileNames.add(fileName);
// 4.2 保存文件
// File saveFile = new File("E:/11/upload/"+fileName);
File saveFile = new File(getRealPath("/upload", fileName, request));
fileItem.write(saveFile);
}
out.println("</ul>");
// 上传到相对于项目路径的目录下
// myFile.write(getRealPath("/upload", fileName, request));
out.println("<h2>文件上传成功!</h2>");
for(String fileName : uploadFileNames) {
out.println("<a href='upload/"+fileName+"' download>"+fileName+"</a><br />");
}
out.println("<a href='upload.jsp'>返回继续上传</a>");
} catch (FileUploadException e) {
e.printStackTrace();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
/**
* 根据传入的相对于项目根路径的路径,返回该路径在物理硬盘上的绝对路径
*
* @param relativePath 要保存到硬盘上的目录相对于项目的路径
* @param fileName 要保存到硬盘上的文件名称
* @param req 请求对象
* @return 要保存的文件在硬盘上的绝对完整路径
*/
private String getRealPath(String relativePath, String fileName, HttpServletRequest req) {
System.out.println("相对路径:" + relativePath);
// 1. 获取ServletContext上下文对象
ServletContext ctx = req.getServletContext();
// 2. 获取相对路径对应在硬盘上的绝对路径
String realPath = ctx.getRealPath(relativePath);
System.out.println("绝对路径:" + realPath);
// 3. 用绝对路径拼上文件名称,最终得到该文件对应的完整绝对路径
String finalPath = realPath + "/" + fileName;
System.out.println("最终文件路径:" + realPath);
return finalPath;
}
}
清理临时文件
fileupload包给我们提供了一个可以直接使用的监听器用于清理上传后的临时文件。我们直接在web.xml中配置一下就可以了
<listener>
<listener-class>
org.apache.commons.fileupload.servlet.FileCleanerCleanup
</listener-class>
</listener
上传超大文件
Tomcat默认最大请求大小2Mb,如果需要上传超大的文件,我们需要修改%TOMCAT_HOME%/conf/server.xml
<Connector URIEncoding="UTF-8" connectionTimeout="20000"
port="8080" protocol="HTTP/1.1" redirectPort="8443"
maxSwallowSize="-1" />
maxSwallowSize: 指定一次请求最大吞吐量。 设为-1,表示没有上限。单位是字节。
文件下载
文件下载我们无需借助任何第三方包,也无需写太多实现代码。其原理其实就是设置响应头, 我们的浏览器其实不管你响应的是什么内容,只是根据响应头来做响应操作。比如我们响应的Content-Type设置为text/html浏览器就会渲染内容,如果我们响应的Content-Type设置为Content-Type:application/vnd.ms-excel,那浏览器就会调用本地excel程序或者直接用读取excel的方式来处理服务端返回的数据
下面是一些常用的请求头示例:
// 请求该页面就出现下载保存窗口。
header("Content-Type:application/force-download");
// 二进制流,不知道具体下载文件类型。
header("Content-Type:application/octet-stream");
// 表示要下载的文件类型是 .xls
header("Content-Type:application/vnd.ms-excel");
// 提示用户将当前文件保存到本地。
header("Content-Type:application/download");
// 作为附件下载,文件名是 测试.xlsx
header("Content-Disposition:attachment;filename=测试.xlsx");