利用 Servlet 实现文件的上传的下载
一、文件上传
1.前端代码:
(1).基于表单的上传如果在表单中使用表单元素浏览器在解析表单时,会自动生成一个输入框和一个按钮,输入框可供用户填写本地文件的文件名和路径名,按钮可以让浏览器打开一个文件选择框供用户选择文件,此时表单要加上 enctype 属性
(2).JS 动态生成文件框:点击 “添加文件” 按钮,可以添加一个文件框而且可以删除。
jsp代码:
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
<script type="text/javascript" src = "../js/jquery.min.js"></script>
<script type="text/javascript">
$(function(){
var i = 2;
$("#addFile").click(function(){
$("#tr").before("<tr class = 'file'><td>File"+i+":</td>"+
"<td><input type = 'file' name = 'file"+i+"'/></td></tr>" +
"<tr class = 'desc'><td>Desc"+i+":</td>"+
"<td><input type = 'text' name = 'desc"+i+"'/></td>" +
"<td><button id = 'delete"+ i +"' type = 'button'>删除</button></td></tr>");
i++;
$("#delete" + (i-1)).click(function(){
var tr = $(this).parent().parent();//获取 delete 按钮所在的 tr 对象
tr.prev("tr").remove(); //获取 delete 按钮所在的 tr 对象的前一个 tr 对象并移除
tr.remove();
//对 i 重新排序
$(".file").each(function(index){//index 从 0 开始,遍历 file 集合修改对应属性值
var n = index + 1;
$(this).find("td:first").text("File" + n + ":");
$(this).find("td:nth-child(2) input").attr("name","file"+ n +"");
});
$(".desc").each(function(index){
var n = index + 1;
$(this).find("td:first").text("Desc" + n + ":");
$(this).find("td:nth-child(2) input").attr("name","desc" + n);
//对删除按钮也要进行重新编号
$(this).find("td:last button").attr("id","delete" + n);
});
i--;
});
});
})
</script>
</head>
<body>
<font color="red">${message }</font>
<form action="FileUploadServlet" method="post" enctype="multipart/form-data">
<table>
<tr class = "file">
<td>File1:</td>
<td><input type = "file" name = "file1"/></td>
</tr>
<tr class = "desc">
<td>Desc1:</td>
<td><input type = "text" name = "desc1"/></td>
</tr>
<tr id = "tr">
<td><input type = "submit" value = "上传"/></td>
<td><button id = "addFile" type = "button">添加文件</button></td>
</tr>
</table>
</form>
</body>
</html>
2.后台代码:
(1).上传文件的属性文件:upload.properties
文件能够上传的扩展名以及不能超过的大小(字节)。
<!-- 以下文件能够上传 --!>
exts=pptx,docx,doc,xls,xlsx
<!-- 单个文件不能超过 1M --!>
file.max.size=1048576
<!-- 文件总大小不能超过 5M --!>
total.file.max.size=5242880
(2).上属性文件 Bean 类:FileUploadProperties.java
单例模式,从上面的 upload.properties 文件中提取出信息,以供后面使用。
import java.util.HashMap;
import java.util.Map;
/**
*
* 单例模式,只需获取一次就可以了。
*/
public class FileUploadProperties {
private Map<String, String> properties = new HashMap<>();
private FileUploadProperties(){}
private static FileUploadProperties instance = new FileUploadProperties();
public static FileUploadProperties getInstance(){
return instance;
}
public void addProperty(String propertyName,String propertyValue){
properties.put(propertyName, propertyValue);
}
public String getPropertty(String propertyName){
return properties.get(propertyName);
}
}
(3).监听器:FileUploadListener.java
当项目运行起来,立即从属性文件中获取信息加载到 FileUploadProperties 类中,这部分工作可由 Listener 完成。
import java.io.IOException;
import java.io.InputStream;
import java.util.Map;
import java.util.Properties;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.annotation.WebListener;
@WebListener
public class FileUploadListener implements ServletContextListener {
public FileUploadListener() {
}
public void contextDestroyed(ServletContextEvent arg0) {
// TODO Auto-generated method stub
}
/**
* 当服务器启动的时候运行该方法,加载信息。
*/
public void contextInitialized(ServletContextEvent arg0) {
InputStream in = getClass().getClassLoader().getResourceAsStream("/upload.properties");
Properties properties = new Properties();
try {
properties.load(in);
for (Map.Entry<Object, Object> pro: properties.entrySet()) {
String propertyName = (String) pro.getKey();
String propertyValue = (String) pro.getValue();
FileUploadProperties.getInstance().addProperty(propertyName, propertyValue);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
(4).文件实体类:FileUploadBean.java
将上传的文件进行封装,方便后面使用。
import java.io.InputStream;
public class FileUploadBean {
private Integer id;
private String fileName; //文件名
private String filePath; //文件路径
private String fileDesc; //文件描述
private long fileSize; //文件大小
private InputStream in; //文件的输入流
public FileUploadBean() {
super();
// TODO Auto-generated constructor stub
}
public FileUploadBean(String fileName, String filePath, String fileDesc, long fileSize, InputStream in) {
super();
this.fileName = fileName;
this.filePath = filePath;
this.fileDesc = fileDesc;
this.fileSize = fileSize;
this.in = in;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getFileName() {
return fileName;
}
public void setFileName(String fileName) {
this.fileName = fileName;
}
public String getFilePath() {
return filePath;
}
public void setFilePath(String filePath) {
this.filePath = filePath;
}
public String getFileDesc() {
return fileDesc;
}
public void setFileDesc(String fileDesc) {
this.fileDesc = fileDesc;
}
public long getFileSize() {
return fileSize;
}
public void setFileSize(long fileSize) {
this.fileSize = fileSize;
}
public InputStream getIn() {
return in;
}
public void setIn(InputStream in) {
this.in = in;
}
}
(5).Servlet 类:FileUploadServlet.java
当表单设置 enctype=”multipart/form-data” 时表示表单以二进制传输数据,此时不能在通过request.getParameter(“file”) 获取文件,需要通过 commons-fileupload-1.3.1.jar 来获取文件。
- FileItemFactory 文件上传工厂类(把每一个请求表单项封装为一个个FileItem对象)
ServletFileUpload 文件上传核心类,可以获取所有的FileItem对象
- Boolean isMultipartContent(request);判断表单类型,文件上传表单但会true
- upload.setFileSizeMax(fileSizeMax);设置单个上传文件的最大值
- upload.setSizeMax(sizeMax);设置总上传文件大总的最大值
- upload.setHeaderEcoding(“UTF-8”);设置上传文件名的编码
FileItem 封装了普通表单项的值以及文件上传表单元素的值
- item.getFiledName();获取上传表单元素的名称
- item.getString();获取上传数据;
- item.getString(“UTF-8”);获取上传数据,处理中文
- item.getContentType();获取上传文件类型[文件上传项]
- item.getInputStream();获取文件流[文件上传项]
- item.getName();获取文件名[文件上传项]
- item.write(file);把数据写到file文件
- item.delete();删除临时文件
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Random;
import java.util.UUID;
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.FileUpload;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.apache.commons.io.FileUtils;
import org.junit.Test;
import cn.edu.pzhu.cg.bean.Student;
import cn.edu.pzhu.cg.db.JDBCTools;
@WebServlet("/app2/FileUploadServlet")
public class FileUploadServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
private static final String FILE_PATH = "d:\\Test\\files";
public FileUploadServlet() {
super();
// TODO Auto-generated constructor stub
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//要请求的页面
String path = null;
try {
ServletFileUpload upload = getServletFileUpload();
//解析请求获取 FileItem 集合
List<FileItem> list = upload.parseRequest(request);
//解析 FileItem 获取每个文件的相关信息,并将文件保存
List<FileUploadBean> fileBeans = getFileBeans(list);
//校验文件大小
validateFileSize(fileBeans);
//校验文件扩展名
validateExtName(fileBeans);
//上传文件
uploadFile(fileBeans);
//将文件信息写入数据库中
saveFileUploadBeans(fileBeans);
path = "success.jsp";
} catch (Exception e) {
e.printStackTrace();
//如果发生异常,重新回到上传页面
path = "upload.jsp";
//保存异常信息
request.setAttribute("message", e.getMessage());
}
//请求 path 页面
request.getRequestDispatcher(path).forward(request, response);
}
/**
*
* @Title: validateFileSize
* @Description: TODO(校验文件大小,先校验每个文件大小是否超过规定大小,再校验总文件大小,将信息以异常的形式抛出)
* @param @param fileBeans 参数
* @return void 返回类型
* @throws
*/
private void validateFileSize(List<FileUploadBean> fileBeans) {
String fileMaxSize = FileUploadProperties.getInstance().getPropertty("file.max.size");
String totalFileMaxSize = FileUploadProperties.getInstance().getPropertty("total.file.max.size");
int tempFileSize = Integer.parseInt(fileMaxSize)/1024/1024;
int tempTotalFileSize = Integer.parseInt(totalFileMaxSize)/1024/1024;
long total = 0;
for (FileUploadBean file : fileBeans) {
if(file.getFileSize() > Integer.parseInt(fileMaxSize)){
throw new cn.edu.pzhu.cg.app2.FileUploadException(file.getFileName() + " 文件大小超过 " + tempFileSize + " M");
}
total += Integer.parseInt(fileMaxSize);
}
if(total > Integer.parseInt(totalFileMaxSize)){
throw new cn.edu.pzhu.cg.app2.FileUploadException("总文件文件大小超过 " + tempTotalFileSize + " M");
}
}
/**
* 文件上传成功后,将文件信息写入到数据库中,由于多条信息需要写入数据库中,这里要开启事务操作。
* @Title: saveFileUploadBeans
* @Description: TODO(这里用一句话描述这个方法的作用)
* @param @param fileBeans 参数
* @return void 返回类型
* @throws
*/
private void saveFileUploadBeans(List<FileUploadBean> fileBeans) {
Connection conn = null;
FileUploadDAO fileUploadDAO= new FileUploadDAO();
try {
conn = JDBCTools.getConnection();
//开启事务,设置不自动提交
conn.setAutoCommit(false);
for (FileUploadBean file : fileBeans) {
fileUploadDAO.insert(conn, file.getFileName(),FILE_PATH,file.getFileDesc());
}
conn.commit();
} catch (Exception e) {
try {
conn.rollback();
} catch (SQLException e1) {
e1.printStackTrace();
}
e.printStackTrace();
} finally{
JDBCTools.release(conn, null, null);
}
}
/**
* 将文件集合中的文件上传到磁盘中,
* @Title: uploadFile
* @Description: TODO(这里用一句话描述这个方法的作用)
* @param @param fileBeans 参数
* @return void 返回类型
* @throws
*/
private void uploadFile(List<FileUploadBean> fileBeans) {
String fileName = null;
File file = null;
try {
for(FileUploadBean fileUploadBean:fileBeans){
fileName = getFileName(fileUploadBean.getFileName());
fileUploadBean.setFileName(fileName);
file = new File(FILE_PATH,fileName);
FileUtils.copyInputStreamToFile(fileUploadBean.getIn(), file);
}
} catch (Exception e1) {
e1.printStackTrace();
}
}
/**
* 验证用户上传的文件与配置的后缀文件是否一致
* @Title: validateExtName
* @Description: TODO(这里用一句话描述这个方法的作用)
* @param @param fileBeans
* @param @return 参数
* @return boolean 返回类型
* @throws
*/
private void validateExtName(List<FileUploadBean> fileBeans) {
//配置文件中能够上传的文件扩展名
String [] propertiesExtNames = FileUploadProperties.getInstance().getPropertty("exts").split(",");
List<String> propertiesExtName = Arrays.asList(propertiesExtNames);
for(FileUploadBean file:fileBeans){
String fileName = file.getFileName();
//判断文件扩展名是否在扩展名集合中,如果不在,抛出异常信息。
if(!propertiesExtName.contains(fileName.substring(fileName.lastIndexOf(".") + 1))){
throw new cn.edu.pzhu.cg.app2.FileUploadException(fileName + " 文件扩展名不合法");
}
}
}
/**
* 通过 FileItem 将文件以及相应信息提取出来并封装成 FileUploadBean 对象,将所有的 FileUploadBean 存放在集合中
* @Title: getFileBeans
* @Description: TODO(这里用一句话描述这个方法的作用)
* @param @param list
* @param @return 参数
* @return List<FileUploadBean> 返回类型
* @throws
*/
private List<FileUploadBean> getFileBeans(List<FileItem> list) {
List<FileUploadBean> fileBeans = new ArrayList<>();
List<FileItem> files = new ArrayList<>();
List<FileItem> descs = new ArrayList<>();
String path = "d:\\Test\\files";
for(FileItem item:list){
if(item.isFormField()){
descs.add(item);
}else{
files.add(item);
}
}
for(int i = 0;i < files.size();i++){
String fileName = files.get(i).getName();
String fileDesc = null;
InputStream in = null;
long fileSize = 0;
try {
fileDesc = descs.get(i).getString("UTF-8");
in = files.get(i).getInputStream();
fileSize = files.get(i).getSize();
} catch (Exception e) {
e.printStackTrace();
}
FileUploadBean fileUploadBean = new FileUploadBean(fileName, path, fileDesc,fileSize,in);
fileBeans.add(fileUploadBean);
}
return fileBeans;
}
/**
* 防止上传文件时发生文件名重复问题,为每个文件重生生成一个文件名,由系统时间 + 随机数 + 原文件名
* @Title: getFileName
* @Description: TODO(这里用一句话描述这个方法的作用)
* @param @param name
* @param @return 参数
* @return String 返回类型
* @throws
*/
private String getFileName(String name) {
String fileName;
Random random = new Random();
fileName = System.currentTimeMillis() + random.nextInt(1000000) + name;
return fileName;
}
/**
* 返回 ServletFileUpload 对象
* @Title: getServletFileUpload
* @Description: TODO(这里用一句话描述这个方法的作用)
* @param @return 参数
* @return ServletFileUpload 返回类型
* @throws
*/
private ServletFileUpload getServletFileUpload() throws Exception{
DiskFileItemFactory factory = new DiskFileItemFactory();
ServletFileUpload upload = new ServletFileUpload(factory);
// upload.setFileSizeMax(Integer.parseInt(fileMaxSize));
// upload.setSizeMax(Integer.parseInt(totalFileMaxSize));
return upload;
}
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
二、文件下载
文件下载需要设置响应流,告诉浏览器是个下载文件,交由用户处理。
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@WebServlet("/app2/DownLoadFileServlet")
public class DownLoadFileServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
public DownLoadFileServlet() {
super();
// TODO Auto-generated constructor stub
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setCharacterEncoding("UTF-8");
request.setCharacterEncoding("UTF-8");
//通知浏览器这是个下载的文件
response.addHeader("Content-type", "appllication/octet-stream");
String path = request.getParameter("path");
String fileName = request.getParameter("name");
//通知浏览器不再由浏览器来自行处理(或打开)要下载的文件,而交由用户处理
response.addHeader("Content-Disposition", "attachment;filename="+fileName);
//获得文件输入流
InputStream in = new FileInputStream(path);
OutputStream out = response.getOutputStream();
byte[] buffer = new byte[1024];
int len = 0;
while((len = in.read()) != -1){
out.write(buffer, 0, len);
}
//因为输出流要向客户端输出文件,所以这里不关闭,输出流。
in.close();
}
}