Serlvet笔记一(Servlet基础)
Servlet笔记二(请求和响应)
Servlet笔记三(会话及其会话技术)
Servlet笔记四(JSP技术)
Servlet笔记五(EL表达式和JSTL)
Servlet笔记六(Servlet 高级)
Servlet笔记七(JDBC)
Servlet笔记八(数据库连接池与DBUtils工具)
Servlet笔记九(JSP 开发模型)
Servlet笔记十(文件上传和下载)
很多Web应用都为用户提供了文件上传和下载的功能,例如,图片的上传与下载、邮件附件的上传与下载等。
要实现 Web 开发中的文件上传功能,通常需完成两步操作:
一 是在 Web 页面中添加上传输入项;
二 是在 Servlet 中读取上传文件的数据,并保存到本地硬盘中。
由于大多数文件的上传都是通过表单的形式提交给服务器的,因此,要想在程序中实现文件上传的功能,首先要创建一个用于提交上传文件的表单页面。在页面中,需要使用 标签在 Web 页面中添加文件上传输入项。
标签的使用需要注意以下两点。
● 必须要设置 input 输入项的 name 属性,否则浏览器将不会发送上传文件的数据。
● 必须将表单页面的 method 属性设置为 post 方式,enctype 属性设置为“multipart/form-data” 类型。
示例代码如下。
<%--指定表单数据的enctype属性以及提交方式--%>
<form enctype="multipart/ form-data" method="post">
<%--指定标记的类型和文件域的名称--%>
选择上传文件: <input type="file" name= "myfile" /><br />
form>
当浏览器通过表单提交上传文件时,由于文件数据都附带在 HTTP 请求消息体中,并且采用 MIME 类型(多用途互联网邮件扩展类型)进行描述,在后台使用 request 对象提供的 getInputStream() 方法可以读取到客户端提交过来的数据。但由于用户可能会同时上传多个文件,而在 Servlet 端直接读取上传数据,并分别解析出相应的文件数据是一项非常麻烦的工作。为了方便处理用户上传数据,Apache 组织提供了一个开源组件 Commons-FileUpload。该组件可以方便地将 “multipart/form-data” 类型请求中的各种表单域解析出来,并实现一个或多个文件的上传,同时也可以限制上传文件的大小等内容。其性能十分优异,使用极其简单。
需要注意的是,在使用 FileUpload 组件时,要导入 commons-fileupload.jar 和 commons-io.jar 两个 JAR 包,这两个 JAR 包可以去 Apache 官网 "http://commons.apache.org/” 下载(进入该网址页面后,在 Apache Commons Proper 下方表格的 Components 列中找到 FileUpload 和 IO,单击进入后即可找到下载链接)。
FileUpload 组件是通过 Servlet 来实现文件上传功能的。其工作流程如图所示。
从图中可以看出,实现文件的上传会涉及到几个陌生类,这些类都是 Apache 组件上传文件的核心类。
Fileltem 接口在 Commons-FileUpload 组件中被实现,其主要用于封装单个表单字段元素的数据,一个表单字段元素对应一个 Fileltem 对象。Commons-FileUpload 组件在处理文件上传的过程中,将每一个表单域(包括普通的文本表单域和文件域)封装在一个 Fileltem 对象中。
在此将 Fileltem 接口的实现类称为 Fileltem 类, Fileltem 类实现了 Serializable 接口,因此,支持序列化操作。在 Fileltem 类中定义了许多获取表单字段元素的方法,具体如下。
DiskFileltemFactory 类用于将请求消息实体中的每一个文件封装成单独的 Fileltem 对象。如果上传的文件比较小,将直接保存在内存中,如果上传的文件比较大,则会以临时文件的形式,保存在磁盘的临时文件夹中。默认情况下,文件保存在内存还是硬盘临时文件夹的临界值是10240,即 10KB。DiskFileltemFactory 类中包含两个构造方法,如表所示。
表列举了 DiskFileltemFactory 类的两个构造方法。其中,第 2 个构造方法需要传递两个参数,参数 sizeThreshold 代表文件保存在内存还是磁盘临时文件夹的临界值,参数 repository 表示临时文件的存储路径。
接下来,针对 DiskFileltemFactory 类的常用方法进行详细讲解,具体如下所示。
System.getProperty ("java.io.tmpdir")
ServletFileUpload 类是 Apache 组件处理文件上传的核心高级类,通过使用
parseRequest(HttpServletRequest) 方法可以将 HTML 中每个表单提交的数据封装成一个 Fileltem 对象,然后以 List 列表的形式返回。ServletFileUpload 类中包含两个构造方法,如表所示。.
表列举了 ServletFileUpload 类的两个构造方法。由于在文件上传过程中,FileltemFactory 类必须设置,因此,在使用第一个构造方法创建 ServletFileUpload 对象时,首先需要在解析请求之前调用 setFileltemFactory() 方法设置 fileltemFactory 属性。
ServletFileUpload类的常用方法如下所示。
System.getProperty ("file. encoding"));
项目结构
form.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<html>
<head>
<title>upfiletitle>
head>
<body>
<form action="/UploadServlet" method="post" enctype="multipart/form-data">
<table>
<tr>
<td>上 传 者:td>
<td>
<input type="text" name="name"/>
td>
tr>
<tr>
<td>上传文件td>
<td><input type="file" name="myfile"/>td>
tr>
<tr>
<td colspan="2"><input type="submit" value="上传">td>
tr>
table>
form>
body>
html>
2. 创建 Servlet
UploadServlet.java
package com.xxx.servlet;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
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.*;
import java.util.List;
import java.util.UUID;
@WebServlet("/UploadServlet")
public class UploadServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("text/html;charset=utf-8");
try {
// 创建 DiskFileItemFactory 工厂对象
DiskFileItemFactory factory = new DiskFileItemFactory();
// 设置文件缓存目录,如果该目录不存在则新创建一个
File f = new File("/home/sweetheart/Desktop/myjava/temp/");
if(!f.exists()){
f.mkdirs();
}
// 设置文件的缓存目录
factory.setRepository(f);
// 创建 ServletFileUpload 对象
ServletFileUpload fileUpload = new ServletFileUpload(factory);
// 设置字符编码
fileUpload.setHeaderEncoding("utf-8");
// 解析 request, 得到上传文件的 FileItem 对象
List<FileItem> fileitems = fileUpload.parseRequest(req);
// 获取字符流
PrintWriter out = resp.getWriter();
// 遍历集合
for (FileItem fileItem: fileitems){
// 判断是否为普通字段
if(fileItem.isFormField()){
// 获取字段名及值
String name = fileItem.getFieldName();
if("name".equals(name)){
if(!fileItem.getString().equals("")){
String value = fileItem.getString("utf-8");
out.println("上传者:" + value + "
");
}else{
System.out.println("上传者的信息是空的");
}
}
}else{
// 上传的是文件
// 获取文件名
String filename = fileItem.getName();
if(filename != null && !"".equals(filename)){
out.println("上传的文件名称为:" + filename + "
");
// 截取文件名,将带有客户端文件路径的前缀部分进行删除
filename = filename.substring(filename.lastIndexOf("/") + 1);
// 设置文件名唯一
filename = UUID.randomUUID().toString() + "_" + filename;
// 在服务创建同名的文件
String webPath = "/upload/";
// 将服务器中文件夹路径与文件名组合成完整的服务器路径(绝对路径)
String filepath = getServletContext().getRealPath(webPath + filename);
// 创建文件
File file = new File(filepath);
file.getParentFile().mkdirs();
file.createNewFile();
// 获取上传文件的输入流
InputStream ins = fileItem.getInputStream();
// 使用 FileOutputStream 打开服务器端上传文件
FileOutputStream fos = new FileOutputStream(file);
// 使用流写入文件中
byte[] buffer = new byte[1024]; // 1k 字节
int len;
// 循环写入
while ((len = ins.read(buffer)) > 0){
fos.write(buffer, 0, len);
}
// 关闭流
fos.close();
ins.close();
// 删除临时文件
fileItem.delete();
out.println("上传文件成功!
");
}else{
System.out.println("没有上传");
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
3. 运行结果
实现文件下载功能比较简单,通常情况下,不需要使用第三方组件实现,而是直接使用 Servlet 类和输入/输出流实现。与访问服务器文件不同的是,要实现文件的下载,不仅需要指定文件的路径,还需要在 HTTP 协议中设置两个响应消息头:
// 设定接收程序处理数据的方式
Content-Disposition: attachment;filename=
// 设定实体内容的 MIME 类型
Content-Type: application/x-msdownload
浏览器通常会直接处理响应的实体内容,需要在 HTTP 响应消息中设置两个响应消息头字段,用来指定接收程序处理数据内容的方式为下载方式。当单击 [下载] 超链接时,系统将请求提交到对应的 Servlet。在该 Servlet 中,首先获取下载文件的地址,并根据该地址创建文件字节输入流,然后通过该流读取下载的文件内容,最后将读取的内容通过输出流写到目标文件中。
项目结构
1. 创建下载页面
download.jsp
<%@ page import="java.net.URLEncoder" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>title>
head>
<body>
<a href="./DownloadServlet?filename=<%=URLEncoder.encode("图片.jpg", "utf-8")%>">文件下载a>
body>
html>
2. 创建 Servlet
DownloadServlet.java
package com.xxx.servlet;
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.io.InputStream;
import java.io.OutputStream;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
@WebServlet("/DownloadServlet")
public class DownloadServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 设置编码
resp.setContentType("text/html;charset=utf-8");
// 获取下载文件名
String filename = req.getParameter("filename");
// 解决中文编码问题
filename = new String(filename.getBytes(StandardCharsets.UTF_8));
// 下载文件所在文件夹
String folder = "/download/";
// 通知浏览器以下载方式打开; encode编码器 解决中文编码
resp.addHeader("Content-Type", "application/octet-stream");
resp.addHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(filename, "utf-8"));
// 获取输入流
InputStream ins = getServletContext().getResourceAsStream(folder+filename);
// 获取 response 对象的输出流
OutputStream out = resp.getOutputStream();
// 1k字节
byte[] buffer = new byte[1024];
int len;
// 循环输出
while ((len=ins.read(buffer)) != -1){
out.write(buffer, 0, len);
}
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
EncodingFilter.java
package com.xxx.filter;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;
@WebFilter("/DownloadServlet")
public class EncodingFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("init...");
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
request.setCharacterEncoding("utf-8");
response.setCharacterEncoding("utf-8");
chain.doFilter(request, response);
}
@Override
public void destroy() {
System.out.println("destroy...");
}
}
3. 创建下载目录及文件