目录
一、文件上传简介
1、文件上传的步骤
2、一个简单的上传 Demo
3、上传的 http 协议内容
4、commons-fileupload.jar 常用 API 介绍
二、文件下载简介
1、文件下载的步骤
2、一个简单的下载 Demo
3、文件名包含中文的解决方法
4、不同浏览器的编码方式
三、示例
1、uploadFile.jsp:
2、downloadFile.jsp:
3、uploadFileServlet 类:
4、downloadFileServlet 类:
5、index.jsp:
6、说明
(1)要有一个 form 表单,请求方式为 post 请求(因为上传的文件一般都超出长度限制)。
(2)form 标签的 encType 属性值必须为 multipart/form-data。
表示提交的数据,以多段的形式进行拼接,然后以二进制流的形式发送给服务器。
多段:一个表单项代表一个数据段
(3)在 form 标签中使用 input-type = file 添加上传的文件。
(4)编写服务器代码(Servlet 程序),接收并处理上传的数据。
(1)uploadFile.jsp:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
Title
(2)uploadFileServlet 类:
package com.servlet;
import javax.servlet.ServletException;
import javax.servlet.http.*;
import java.io.IOException;
public class uploadFileServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("文件上传成功");
}
}
(1)请求体【负载】-【查看源代码】
(2)分析协议内容
(3)服务器如何接收
因为客户端是以流的形式发送的数据,所以服务器也以流的形式接收
package com.servlet;
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.IOException;
public class uploadFileServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("文件上传成功");
// 获取流
ServletInputStream servletIS = req.getInputStream();
// 缓冲区
byte[] buffer = new byte[1024000];
int read = servletIS.read(buffer);
// 以字符串形式输出
System.out.println(new String(buffer, 0, read));
}
}
在使用之前需要导入两个 jar 包(版本自定):
(1)常用的类和方法:
(2)示例:
package com.servlet;
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;
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.File;
import java.io.IOException;
import java.util.*;
public class uploadFileServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("文件上传成功");
// 先判断上传的数据是否为多段,只有多段的数据才是文件上传的
if (ServletFileUpload.isMultipartContent(req)) {
// 创建 FileItemFactory 实现类
FileItemFactory fileItemFactory = new DiskFileItemFactory();
// 创建用于解析上传数据的工具类 ServletFileUpload 类
ServletFileUpload servletFileUpload = new ServletFileUpload(fileItemFactory);
try {
// 解析上传的数据,获得表单项列表 List
List fileItemList = servletFileUpload.parseRequest(req);
// 遍历表单项,判断是否为文件
for (FileItem fileItem : fileItemList) {
if (fileItem.isFormField()) { // 普通
System.out.println("表单项的name属性值:" + fileItem.getFieldName());
System.out.println("表单项的value属性值:" + fileItem.getString("utf-8"));
} else { // 文件
System.out.println("表单项的name属性值:" + fileItem.getFieldName());
System.out.println("上传的文件名:" + fileItem.getName());
// 绝对路径保存到本地(服务器)
fileItem.write(new File("D:/Programming Project/JavaProject/WebDemoProject/Demo/file/web/src/upload/" + fileItem.getName()));
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
(1)客户端请求下载
(2)服务器获取要下载的文件名,在服务器硬盘读取要下载的文件内容
(3)获取文件数据类型,通过响应头告诉客户端,服务器即将返回此数据类型
(4)通过响应头告诉客户端,返回的文件用于下载,而非显示到浏览器页面上
(5)将资源作为输入流,返回给(输出到)客户端
注意:相对路径不要写错了,在浏览器上是不会有报错的。
package com.servlet;
import org.apache.commons.io.IOUtils;
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.*;
public class downloadFileServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 1.获取要下载的文件名
String downloadFileName = "R-C.jpg";
// 2.读取要下载的文件内容(使用ServletContext对象可以读取)
ServletContext servletContext = getServletContext();
// 3.获取文件数据类型,并通过响应头告诉客户端,服务器即将返回此数据类型
String mime = servletContext.getMimeType("./src/upload/" + downloadFileName); // 暂时将 upload 的文件作为可下载的文件
resp.setContentType(mime);
// 4.通过响应头告诉客户端,返回的文件用于下载,而非显示到浏览器页面上
/*
content-Disposition:表示如何处理数据
attachment:表示附件
filename:表示下载的文件名
*/
resp.setHeader("content-Disposition", "attachment;filename=" + downloadFileName);
// 5.将资源作为输入流,返回给(输出到)客户端
InputStream inputStream = servletContext.getResourceAsStream("./src/upload/" + downloadFileName);
OutputStream outputStream = resp.getOutputStream();
IOUtils.copy(inputStream, outputStream);
}
}
这是个非常常见的问题,可以通过 URL 编码以及 Base64 编码这些操作解决。
这两种 encode 都没有进行 decode,解码工作交给了浏览器完成(可以发现下面的示例中,都没有 decode 相关的操作)。
(1)URL 编码
(1-1)使用 URLEncoder 类的 encode 函数,传入文件名和编码格式即可:
String filename = URLEncoder.encode(downloadFileName, "UTF-8");
resp.setHeader("content-Disposition", "attachment;filename=" + filename);
URL 编码会将文件名转化为 %xx%xx 的格式,其中 xx 是十六进制。
(2)Base64 编码
(注意 Base64 有一个老版本是 Base64Encoder,jdk1.9 之后就不再使用了,需要将其更换为 Base64)
(2-1)另外需要注意的是,使用 Base64 编码,对请求头也有一定的编码格式要求:
content-Disposition: attachment; filename == ?charset?B?fileName?=
(2-2)示例:
Base64.Encoder base64Encoder = Base64.getEncoder();
String encode = base64Encoder.encodeToString(downloadFileName.getBytes("UTF-8"));
// 注意 filename==?,== 两端不能有空格
resp.setHeader("content-Disposition", "attachment; filename==?UTF-8?B?" + encode + "?=");
上面介绍了两种解决中文编码的方法,但并不是所有浏览器都能兼容某一种方法。因此需要额外判断浏览器是哪一种浏览器。
具体方法就是:用域对象获取请求头中的 User-Agent 的值,然后查看是否包含某种浏览器的关键字。
if (req.getHeader("User-Agent").contains("Chrome")) {
String filename = URLEncoder.encode(downloadFileName, "UTF-8");
resp.setHeader("content-Disposition", "attachment; filename=" + filename);
} else {
// 其他编码方式
}
编写简单的上传和下载页面,并作相应操作。
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
Title
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
Title
package com.servlet;
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;
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.File;
import java.io.IOException;
import java.util.*;
public class uploadFileServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("文件上传成功");
// 先判断上传的数据是否为多段,只有多段的数据才是文件上传的
if (ServletFileUpload.isMultipartContent(req)) {
// 创建 FileItemFactory 实现类
FileItemFactory fileItemFactory = new DiskFileItemFactory();
// 创建用于解析上传数据的工具类 ServletFileUpload 类
ServletFileUpload servletFileUpload = new ServletFileUpload(fileItemFactory);
try {
// 解析上传的数据,获得表单项列表 List
List fileItemList = servletFileUpload.parseRequest(req);
// 遍历表单项,判断是否为文件
for (FileItem fileItem : fileItemList) {
if (fileItem.isFormField()) { // 普通
System.out.println("表单项的name属性值:" + fileItem.getFieldName());
System.out.println("表单项的value属性值:" + fileItem.getString("utf-8"));
} else { // 文件
System.out.println("表单项的name属性值:" + fileItem.getFieldName());
System.out.println("上传的文件名:" + fileItem.getName());
// 绝对路径保存到本地(服务器),不是服务器去解析地址,因此不能用 / 映射至工程
fileItem.write(new File("D:/Programming Project/JavaProject/WebDemoProject/Demo/file/web/src/upload/" + fileItem.getName()));
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
package com.servlet;
import org.apache.commons.io.IOUtils;
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.*;
import java.net.URLEncoder;
public class downloadFileServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 1.获取要下载的文件名
String downloadFileName = req.getParameter("filename");
// 2.读取要下载的文件内容(使用ServletContext对象可以读取)
ServletContext servletContext = getServletContext();
// 3.获取文件数据类型,并通过响应头告诉客户端,服务器即将返回此数据类型
String mime = servletContext.getMimeType("./src/upload/" + downloadFileName); // 暂时将 upload 的文件作为可下载的文件
resp.setContentType(mime);
// 4.通过响应头告诉客户端,返回的文件用于下载,而非显示到浏览器页面上
/*
content-Disposition:表示如何处理数据
attachment:表示附件
filename:表示下载的文件名
*/
if (req.getHeader("User-Agent").contains("Chrome")) {
String filename = URLEncoder.encode(downloadFileName, "UTF-8");
resp.setHeader("content-Disposition", "attachment; filename=" + filename);
} else {
// 其他编码方式
}
// 5.将资源作为输入流,返回给(输出到)客户端
InputStream inputStream = servletContext.getResourceAsStream("./src/upload/" + downloadFileName);
OutputStream outputStream = resp.getOutputStream();
IOUtils.copy(inputStream, outputStream);
}
}
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
$Title$
上传
下载
(1)上传/下载容量较小的文件,使用 GET/POST 都可以(POST 请求更安全)
(2)下载时 GET/POST 的区别
(3)第(2)点很重要
(4)第(3)点很重要