16. 文件上传下载
16.1 介绍
实现web开发中的文件上传功能,需完成如下二步操作:
1、在web页面中添加上传输入项
2、在servlet中读取上传文件的数据,并保存到服务器中。
如何在web页面中添加上传输入项?
<input type="file">
表单用于在web页面中添加文件上传输入项,设置文件上传输入项时须注意:
1、必须要设置input输入项的name属性,否则浏览器将不会发送上传文件的数据。
2、必须把form表单的enctype属值设为multipart/form-data.
3、必须把form表单的method属性设置为post方式。设置该值后,浏览器在上传文件时,将把文件数据附带在http请求消息体中,并使用MIME协议对上传的文件进行描述,以方便接收方对上传数据进行解析和处理。
如何在Servlet中读取文件上传数据,并保存到服务器中?
Request对象提供了一个getInputStream方法,通过这个方法可以读取到客户端提交过来的数据。 但由于用户可能会同时上传多个文件,在servlet端编程直接读取上传数据,并分别解析出相应的文件数据是一项非常麻烦的工作。
在Servlet3.0出现之前,处理文件上传需要借助第三方组件,例如Apache的commons-fileupload组件等。而Servlet3.0出现以后就摆脱了这一问题。使用Servlet3.0可以十分方便的实现文件的上传。
16.2 commons-fileupload
由Apache提供的上传组件: 官网
首先需要创建web项目
添加依赖
<dependencies>
<!--单元测试-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<!--Servlet-API 编译时需要-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.0.1</version>
<scope>provided</scope>
</dependency>
<!--JSP-API 编译时需要-->
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>2.1</version>
<scope>provided</scope>
</dependency>
<!-- lombok 帮助快速生成 setter、getter toString equals、hashcode 构造器 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.12</version>
<scope>provided</scope>
</dependency>
<!--jstl Apache 的标准标签库-->
<dependency>
<groupId>jstl</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<!-- commons-fileupload 从request中解析 文件流 -->
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.4</version>
</dependency>
</dependencies>
16.2.1 文件上传
上传页面: upload.jsp
<%--
Created by IntelliJ IDEA.
User: root
Date: 2020/12/18
Time: 9:11
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h3>上传文件</h3>
<form method="post" enctype="multipart/form-data" action="${pageContext.request.contextPath}/up">
<input type="file" name="file" />
<input type="text" name="name" value=""/>
<input type="text" name="password" value=""/>
<input type="submit">
</form>
</body>
</html>
配置文件用于配置 上传文件目录: config.properties
upload.dir=d:/upload
上传文件的Servlet
package com.neuedu.updown;
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;
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 java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.Date;
import java.util.List;
import java.util.Properties;
/**
* 项目: javawebother
* 类名: ${NAME}
* 创建时间: 2020/12/18 9:29
* 描述 : ${dc}
* 作者 : 张金山
* QQ : 314649444
* Site: https://jshand.gitee.io
*/
@WebServlet(name = "FileUploadServlet", urlPatterns = "/up")
public class FileUploadServlet extends HttpServlet {
private static String baseDir;
static {
InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream("config.properties");
Properties properties = new Properties();
try {
properties.load(is);
baseDir = (String) properties.getProperty("upload.dir");
} catch (IOException e) {
e.printStackTrace();
}
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
boolean isMultipart = ServletFileUpload.isMultipartContent(request);
if (isMultipart) {
// Create a factory for disk-based file items
DiskFileItemFactory factory = new DiskFileItemFactory();
// Configure a repository (to ensure a secure temp location is used)
ServletContext servletContext = this.getServletConfig().getServletContext();
File repository = (File) servletContext.getAttribute("javax.servlet.context.tempdir");
factory.setRepository(repository);
// Create a new file upload handler
ServletFileUpload upload = new ServletFileUpload(factory);
// Parse the request
try {
//表单域, input【text、file】
List<FileItem> items = upload.parseRequest(request);
for (FileItem item : items) {
if(item.isFormField()) { // text password checkbox ...
String name = item.getFieldName() ;// 控件的名字 input[name= ??? ]
String value = item.getString(); // 控件的值 input[value= ??? ]
System.out.println(name+"\t"+value);
}else{ // type = file
// String fileName = item.getFieldName(); //原始的文件名
String fileName = item.getName(); //原始的文件名
File dist = new File(baseDir,fileName);
item.write(dist);//转储到 baseDir【D:/upload】
String now = "";//new Date();
String clientHost = request.getRemoteHost();
long size = item.getSize();
String info = String.format("host:%s\t time:%s, name:%s\t size:%s",
clientHost,
new Date().toString(),
fileName,
String.valueOf(size)
);
//上传文件的信息
System.out.println(info);
}
}
} catch (FileUploadException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
} else {//普通的表单处理
// request.getParameter()
}
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
}
16.2.2 保存上传文件信息
创建数据库
/*
SQLyog Ultimate v12.08 (64 bit)
MySQL - 5.7.18 : Database - upload
*********************************************************************
*/
/*!40101 SET NAMES utf8 */;
/*!40101 SET SQL_MODE=''*/;
/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
CREATE DATABASE /*!32312 IF NOT EXISTS*/`upload` /*!40100 DEFAULT CHARACTER SET utf8 */;
USE `upload`;
/*Table structure for table `upload_files` */
DROP TABLE IF EXISTS `upload_files`;
CREATE TABLE `upload_files` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键',
`origin_name` varchar(500) NOT NULL COMMENT '原始文件名',
`path` varchar(500) NOT NULL COMMENT '文件系统中的相对路径',
`upload_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '上传时间',
`size` int(11) DEFAULT NULL COMMENT '文件大小',
`ip` varchar(100) DEFAULT NULL COMMENT '上传ip',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
/*Data for the table `upload_files` */
/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
添加JDBCUTIL依赖
<dependency>
<groupId>com.neuedu</groupId>
<artifactId>jdbcutil</artifactId>
<version>1.0</version>
</dependency>
jdbc.properties
jdbc.url=jdbc:mysql://127.0.0.1:3306/upload?characterEncoding=utf8&useUnicode=true&useSSL=false
jdbc.driverClass=com.mysql.jdbc.Driver
jdbc.username=root
jdbc.password=root
实体类
@Data
@NoArgsConstructor
@AllArgsConstructor
public class UploadFile {
private Integer id;
private String path;
private String ip;
private Integer size;
@Column("origin_name")
private String originName;
@Column("upload_time")
private String uploadTime;
}
UploadFileDao层代码
package com.neuedu.updown.dao;
import com.neuedu.updown.entity.UploadFile;
import com.neuedu.util.JDBCUtil;
import java.util.List;
/**
* 项目: javawebother
* 类名: UploadFileDao
* 创建时间: 2020/12/18 10:08
* 描述 :
* 作者 : 张金山
* QQ : 314649444
* Site: https://jshand.gitee.io
*/
public class UploadFileDao {
/**
* 查询列表
* @return
*/
public List<UploadFile> selectList(){
String sql = " select * from upload_files ";
return JDBCUtil.executeQuery(sql,UploadFile.class);
}
/**
* 根据主键查询信息
* @param id
* @return
*/
public UploadFile getOne(Integer id){
String sql = " select * from upload_files where id = ? ";
return JDBCUtil.getOne(sql,UploadFile.class,id);
}
/**
* 保存信息
* @param uploadFile
* @return
*/
public boolean insert(UploadFile uploadFile){
String sql = "insert into upload_files( origin_name, path, size, ip ) values(?,?,?,?) ";
return JDBCUtil.executeUpdate(sql,
uploadFile.getOriginName(),
uploadFile.getPath(),
uploadFile.getSize(),
uploadFile.getIp()
);
}
}
service层代码
package com.neuedu.updown.service;
import com.neuedu.updown.dao.UploadFileDao;
import com.neuedu.updown.entity.UploadFile;
import java.util.List;
/**
* 项目: javawebother
* 类名: UploadFileService
* 创建时间: 2020/12/18 10:15
* 描述 : 业务层
* 作者 : 张金山
* QQ : 314649444
* Site: https://jshand.gitee.io
*/
public class UploadFileService {
private UploadFileDao uploadFileDao = new UploadFileDao();
/**
* 查集合
* @return
*/
public List<UploadFile> list(){
return uploadFileDao.selectList();
}
/**
* 查询一个
* @param id
* @return
*/
public UploadFile getById(Integer id){
return uploadFileDao.getOne(id);
}
/**
* 保存信息
* @param uploadFile
* @return
*/
public boolean save(UploadFile uploadFile){
return uploadFileDao.insert(uploadFile);
}
}
在controller中上传的时将信息保存到数据库中
package com.neuedu.updown;
import com.neuedu.updown.entity.UploadFile;
import com.neuedu.updown.service.UploadFileService;
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;
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 java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.Date;
import java.util.List;
import java.util.Properties;
import java.util.UUID;
/**
* 项目: javawebother
* 类名: ${NAME}
* 创建时间: 2020/12/18 9:29
* 描述 : ${dc}
* 作者 : 张金山
* QQ : 314649444
* Site: https://jshand.gitee.io
*/
@WebServlet(name = "FileUploadServlet", urlPatterns = "/up")
public class FileUploadServlet extends HttpServlet {
private UploadFileService uploadFileService = new UploadFileService();
private static String baseDir;
static {
InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream("config.properties");
Properties properties = new Properties();
try {
properties.load(is);
baseDir = (String) properties.getProperty("upload.dir");
} catch (IOException e) {
e.printStackTrace();
}
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
boolean isMultipart = ServletFileUpload.isMultipartContent(request);
if (isMultipart) {
// Create a factory for disk-based file items
DiskFileItemFactory factory = new DiskFileItemFactory();
// Configure a repository (to ensure a secure temp location is used)
ServletContext servletContext = this.getServletConfig().getServletContext();
File repository = (File) servletContext.getAttribute("javax.servlet.context.tempdir");
factory.setRepository(repository);
// Create a new file upload handler
ServletFileUpload upload = new ServletFileUpload(factory);
// Parse the request
try {
//表单域, input【text、file】
List<FileItem> items = upload.parseRequest(request);
for (FileItem item : items) {
if(item.isFormField()) { // text password checkbox ...
String name = item.getFieldName() ;// 控件的名字 input[name= ??? ]
String value = item.getString(); // 控件的值 input[value= ??? ]
System.out.println(name+"\t"+value);
}else{ // type = file
// String fileName = item.getFieldName(); //原始的文件名
String fileName = item.getName(); //原始的文件名
String ext = fileName.substring(fileName.lastIndexOf("."));
String newFileName = UUID.randomUUID().toString().replace("-","")+ext;
File dist = new File(baseDir,newFileName); //使用uuid 生成不重复的 文件名
item.write(dist);//转储到 baseDir【D:/upload】
String now = "";//new Date();
String clientHost = request.getRemoteHost();
long size = item.getSize();
String info = String.format("host:%s\t time:%s, name:%s\t size:%s",
clientHost,
new Date().toString(),
fileName,
String.valueOf(size)
);
//上传文件的信息
System.out.println(info);
UploadFile uploadFile = new UploadFile();
uploadFile.setIp(clientHost);
uploadFile.setOriginName(fileName);
uploadFile.setPath(newFileName);
uploadFile.setSize((int) size);
uploadFileService.save(uploadFile);
}
}
} catch (FileUploadException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
} else {//普通的表单处理
// request.getParameter()
}
//查询
response.sendRedirect(request.getContextPath()+"/filelist");
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
}
展示文件列表Controller
package com.neuedu.updown;
import com.neuedu.updown.entity.UploadFile;
import com.neuedu.updown.service.UploadFileService;
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 java.io.IOException;
import java.util.List;
/**
* 项目: javawebother
* 类名: ${NAME}
* 创建时间: 2020/12/18 11:04
* 描述 : ${dc}
* 作者 : 张金山
* QQ : 314649444
* Site: https://jshand.gitee.io
* http://127.0.0.1:8080/web/filelist
*/
@WebServlet(name = "FileListServlet",urlPatterns = "/filelist")
public class FileListServlet extends HttpServlet {
UploadFileService uploadFileService = new UploadFileService();
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//跳转到 列表页面
List<UploadFile> list = uploadFileService.list();
request.setAttribute("list",list);
request.getRequestDispatcher("file_list.jsp").forward(request,response);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
}
展示列表的jsp
<%@ page import="com.neuedu.updown.entity.UploadFile" %>
<%@ page import="java.util.List" %><%--
Created by IntelliJ IDEA.
User: root
Date: 2020/12/18
Time: 10:59
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<%
List<UploadFile> list = (List<UploadFile>) request.getAttribute("list");
%>
<a href="${pageContext.request.contextPath}/upload.jsp">上传文件</a>
<table cellspacing="0" cellpadding="0" border="1" width="80%">
<tr>
<td>序号</td>
<td>原始文件名</td>
<td>上传时间</td>
<td>文件大小</td>
<td>上传ip</td>
<td>下载</td>
</tr>
<%
for (int i = 0; i < list.size(); i++) {
UploadFile upload = list.get(i);
%>
<tr>
<td><%=(i + 1)%>
</td>
<td><%=upload.getOriginName()%>
</td>
<td><%=upload.getUploadTime()%>
</td>
<td><%=upload.getSize()%>
</td>
<td><%=upload.getIp()%>
</td>
<td><a href="${pageContext.request.contextPath}/download?id=<%=upload.getId()%>">下载</a></td>
</tr>
<%
}
%>
</table>
</body>
</html>
16.2.3 文件下载
通过Serlvet将用户选择下载的文件,通过response对象输出流,输出到客户端浏览器,提供下载。
Content-Disposition 消息头指示回复的内容该以何种形式展示,是以内联的形式(即网页或者页面的一部分),还是以附件的形式下载并保存到本地。
response.addHeader("Content-Disposition","attachment;filename="+ URLEncoder.encode(uploadFile.getOriginName(),"utf-8") +"");
下载的Controller
package com.neuedu.updown;
import com.neuedu.updown.entity.UploadFile;
import com.neuedu.updown.service.UploadFileService;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URLEncoder;
import java.util.Properties;
/**
* 项目: javawebother
* 类名: ${NAME}
* 创建时间: 2020/12/18 11:16
* 描述 : ${dc}
* 作者 : 张金山
* QQ : 314649444
* Site: https://jshand.gitee.io
*
* http://127.0.0.1:8080/web/download?id=3
*
*
*
*/
@WebServlet(name = "FileDownLoadServlet",urlPatterns = "/download")
public class FileDownLoadServlet extends HttpServlet {
UploadFileService uploadFileService = new UploadFileService();
private static String baseDir;
static {
InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream("config.properties");
Properties properties = new Properties();
try {
properties.load(is);
baseDir = (String) properties.getProperty("upload.dir");
} catch (IOException e) {
e.printStackTrace();
}
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//设置header信息 ,通知浏览器,输出文件的类型
String id = request.getParameter("id");
UploadFile uploadFile = uploadFileService.getById(Integer.parseInt(id));
response.addHeader("Content-Disposition","attachment;filename="+ URLEncoder.encode(uploadFile.getOriginName(),"utf-8") +"");
ServletOutputStream out = response.getOutputStream();
String fileName = uploadFile.getPath();
File downloadFile = new File(baseDir,fileName);
FileInputStream fis = new FileInputStream(downloadFile);
byte[] buffer = new byte[1024];
int len = -1;
while ( (len = fis.read(buffer)) != -1){
out.write(buffer,0,len);
out.flush();
}
out.close();
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request,response);
}
}
上传文件的列表
点击【下载】后Chrome浏览器的效果
16.3 Servlet3.0 上述环境相同
修改FileUploadServlet
package com.neuedu.updown;
import com.neuedu.updown.entity.UploadFile;
import com.neuedu.updown.service.UploadFileService;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.annotation.MultipartConfig;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.Part;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.Date;
import java.util.List;
import java.util.Properties;
import java.util.UUID;
/**
* 项目: javawebother
* 类名: ${NAME}
* 创建时间: 2020/12/18 9:29
* 描述 : ${dc}
* 作者 : 张金山
* QQ : 314649444
* Site: https://jshand.gitee.io
*/
@WebServlet(name = "FileUploadServlet", urlPatterns = "/up")
@MultipartConfig
public class FileUploadServlet extends HttpServlet {
private UploadFileService uploadFileService = new UploadFileService();
private static String baseDir;
static {
InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream("config.properties");
Properties properties = new Properties();
try {
properties.load(is);
baseDir = (String) properties.getProperty("upload.dir");
} catch (IOException e) {
e.printStackTrace();
}
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
Part part = request.getPart("file");
// request.getParts()
String contentDisposition = part.getHeader("content-disposition"); // form-data; name="file"; filename="xxxx"
//原始的文件名
String fileName = contentDisposition.split("filename")[1] ;// ="xxxx"
fileName = fileName.substring(2,fileName.length()-1);
System.out.println("fileName:"+fileName);
String ext = fileName.substring(fileName.lastIndexOf("."));
String newFileName = UUID.randomUUID().toString().replace("-", "") + ext;
File dist = new File(baseDir, newFileName); //使用uuid 生成不重复的 文件名
part.write(dist.getPath());//转储到 baseDir【D:/upload】
String now = "";//new Date();
String clientHost = request.getRemoteHost();
long size = part.getSize();
String info = String.format("host:%s\t time:%s, name:%s\t size:%s",
clientHost,
new Date().toString(),
fileName,
String.valueOf(size)
);
//上传文件的信息
System.out.println(info);
UploadFile uploadFile = new UploadFile();
uploadFile.setIp(clientHost);
uploadFile.setOriginName(fileName);
uploadFile.setPath(newFileName);
uploadFile.setSize((int) size);
uploadFileService.save(uploadFile);
//查询
response.sendRedirect(request.getContextPath()+"/filelist");
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
}
