文件的上传
- 1.我们先测试打通服务器
- upload.jsp负责文件的上传
<%--
Created by IntelliJ IDEA.
User: SWT
Date: 2023/9/22
Time: 18:39
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
<form action="http://localhost:8080//jsp//uploadServlet" method="post" enctype="multipart/form-data">
姓名:<input type="text" name="username"><br>
头像:<input type="file" name="photo"><br>
<input type="submit" value="上传">
</form>
</head>
<body>
</body>
</html>
- 我们使用UploadServlet程序进行接收:
package com.servlet;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class UploadServlet extends HttpServlet {
//用来出来上传的文件
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("文件上传过来了");
}
}
- doPost方法被访问,说明数据可以发送到servlet程序
文件上传的Http协议介绍
- 在请求时
- 请求头:
- 请求体
- 为什么用Post请求
因为get请求是有长度限制的,而上传文件一般都会超出get的长度限制
我们的客户端是以流的形式发送数据的,所以我们服务器必须以流的形式来接收数据 - 以流的形式来接收数据:
- 新的servlet程序
package com.servlet;
import javax.servlet.ServletException;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class UploadServlet extends HttpServlet {
//用来出来上传的文件
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// System.out.println("文件上传过来了");
final ServletInputStream inputStream = req.getInputStream();
byte[]buffer = new byte[1024000];
final int read = inputStream.read(buffer);
System.out.println(new String(buffer,0,read));
}
}
- 输出
上传用到的类和方法的介绍
-
根据上面我们已经可以接收到文件的数据了,现在我们需要的是将数据解析得到我们需要的内容
-
对于文件上传这些常见的功能,有很多第三方机构已经写好了,并且封装成了jar包,我们只需要区使用就可以了
-
1.我们首先需要导入我们需要的2个jar包(在资料里面有)
我们上传的表单项的类型不同,对应的我们的处理也不同(如普通表单项和文件)
对于具体的类和方法的介绍,详见官方笔记
使用fileupload解析上传数据
- 注意:
- jsp文件中的表单项
<form action="http://localhost:8080//jsp//uploadServlet" method="post" enctype="multipart/form-data">
姓名:<input type="text" name="username"><br>
头像:<input type="file" name="photo"><br>
<input type="submit" value="上传">
</form>
- servlet程序
package com.servlet;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileItemFactory;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import javax.servlet.ServletException;
import javax.servlet.ServletInputStream;
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.util.List;
public class UploadServlet extends HttpServlet {
//用来出来上传的文件
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//1.判断上传的数据是否是多段数据(只有是多段数据,才是文件上传的)
if(ServletFileUpload.isMultipartContent((req))){
//创建FileItemFactory工厂实现类
FileItemFactory factory = new DiskFileItemFactory();
//创建用于解析上传数据的工具类ServletFileUpload类的对象
ServletFileUpload servletFileUpload = new ServletFileUpload(factory);
try {
//解析上传的数据,得到每一个表单项FileItem
final List <FileItem>list = servletFileUpload.parseRequest(req);
//循环判断,每一个表单项:是普通类型还是上传的文件
for (FileItem fileItem : list) {
if(fileItem.isFormField()){
//普通表单项
System.out.println("表单项的name属性值:"+fileItem.getFieldName());
//参数:UTF-8解决乱码问题
System.out.println("表单项的value属性值:"+fileItem.getString("UTF-8"));
}else {
//上传的文件
System.out.println("表单项的name属性值:"+fileItem.getFieldName());
System.out.println("上传的文件名:"+fileItem.getName());
//将上传的文件写入到d盘的一个位置
fileItem.write(new File("d://"+fileItem.getName()));
}
}
} catch (FileUploadException e) {
e.printStackTrace();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
}
- 输出
文件下载
文件下载的实现
-
关于文件下载过程:
-
从上面我们可以看出,文件下载需要首先客户端发送请求,然后服务器进行处理:服务器会在服务器的存储系统中找到相应的文件。并以二进制的形式将文件返回给客户端。然后由客户端进行下载
-
我们省略客户端,直接以访问servlet的形式进行演示
-
步骤分析:
- servlet程序
package com.servlet;
import org.apache.commons.io.IOUtils;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletInputStream;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
//处理下载图片
public class DownLoadServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//1.获取要下载的文件名
String downloadFileName = "小姐姐.jpg";
//2.读取要下载的文件内容(通过ServletContext对象可以读取)
/*
*斜杠被服务器解析为http://localhost:8080/工程名/
* 映射到web目录
*/
final ServletContext servletContext = getServletContext();
final InputStream resourceAsStream = servletContext.getResourceAsStream("/file/" + downloadFileName);
//4.在回传前,通过响应头告诉客户端返回的数据类型
//获取要下载的文件类型
final String mimeType = servletContext.getMimeType("/file/" + downloadFileName);
System.out.println("下载的文件类型"+mimeType);
//告诉客户端返回的数据类型
resp.setContentType(mimeType);
//5.告诉客户端收到的数据用于下载使用(还是使用响应头)
//Content-Disposition响应头 ,表示收到数据怎样处理
//attachment,表示附件,表示下载使用
//filename表示指定下载的文件名
resp.setHeader("Content-Disposition", "attachment;filename=" + downloadFileName);
//获取输出流
final ServletOutputStream servletOutputStream = resp.getOutputStream();
//3.把下载的文件内容回传给客户端
//读取输出流中的全部数据,复制给输出流,输出给客户端
IOUtils.copy(resourceAsStream, servletOutputStream);
}
}
我们的步骤4 5 需要在把下载的文件内容回传给客户端之前完成。
- 我的文件名小姐姐.jpg是中文名。好像在下载的文件中中文名并没有显示。似乎是还存在中文乱码的问题
使用URLEncoder解决谷歌和IE浏览器中文下载名乱码的问题
- 下载的文件名是由由设置的响应头决定的,可以和原来的文件名相同,也可以不同
当我们 下载的文件名是中文的话,我们的下载的文件的名字,是不能展现的 - 我们看看http协议里面的内容
因为http协议是老美设计的,在最初的时候仅仅考虑了英文的情况,没有考虑到中文的情况
使用URLEncoder类将中文进行URL编码。将中文转化为%xx%xx(16进制)的格式。已解决中文乱码的问题
- 此时我们再次查看协议内容
此时我们下载的文件的中文文件名也可以显示了 - 但是好像通过上面的编码解决中文的问题,但是我们的火狐浏览器还是不支持URL编码
Base64编码操作
现在一般推荐使用Base64类直接进行编码和解码,而不去使用sun.misc.BASE64Encoder类
package com.test;
import java.util.Base64;
import java.util.Scanner;
public class Test2 {
//演示Base64的编码和解码
public static void main(String[] args) {
//编码操作
String content = "使用Base64进行编码和解码";
final Base64.Encoder encoder = Base64.getEncoder();
final byte[] encode = encoder.encode(content.getBytes());
System.out.println(encode.toString());
//解码操作
final Base64.Decoder decoder = Base64.getDecoder();
final byte[] decode = decoder.decode(encode);
System.out.println(new String(decode));
}
}
使用Base64编码解决解决火狐浏览器下载(附件)中文乱码问题
目前我的IDEA上面提供Base64编码的类是Base64类,但是我们按照上面的写法无法达到相同的需求
使用 User-Agent请求头判断:动态切换不同的方案解决所有浏览器附件乱码的问题
- 基本思路:如果是火狐浏览器使用Base64编码,如果是IE,谷歌使用URL编码
- 在请求头中:有个User-Agent表示浏览器的信息
总结
1.在文件的下载阶段使用了URL和Base64两种方式来应对不同的浏览器来解决中文附件名乱码的问题。但是IDEA已经没有提供Base64EnCoder类来编码了。而是提供了Base64类,使用了Base64类但是采用Base64Encoder类的回应头的写法,好像不能生效
2.但是好像目前的火狐浏览器也可以兼容URL的编码方式了,所以我认为没有必要进行浏览器判断来选择不同的编码方式了,而是可以直接都使用URL的编码方式就可以了