文章目录
- JSON和AJAX文档介绍
- 1. JSON介绍
- 1.1 JSON快速入门
- 1.2 JSON和字符串转换
- 1.2.1 JSON转字符串
- 1.2.2 字符串转JSON
- 1.2.3 JSON和字符串转换细节
- 1.3 JSON在java中使用
- 1.3.1 Java对象和JSON字符串转换
- 1.3.2 List对象和JSON字符串转换
- 1.3.3 Map对象和JSON字符串转换
- 2. Ajax介绍
- 2.1 Ajax应用场景
- 2.2 传统的web应用-数据通信方式
- 2.3 Ajax-数据通信方式
- 2.4 Ajax文档使用
- 2.5 Ajax快速入门
- 2.5.1 验证用户是否存在-思路框架图
- 2.5.2 新建java项目
- 2.5.3 实现
- 2.6 接入数据库
- 3. jQuery操作Ajax
- 3.1 jQuery操作Ajax文档
- 3.2 jQuery.ajax()函数
- 3.3 $.get 和 $.post常用参数
- 3.4 jQuery.ajax()快速入门
- 3.4 jQuery.get()快速入门
- 3.5 jQuery.post()快速入门
- 3.6 jQuery.getJSON快速入门
- 3.7 接入数据库
- 4. ThreadLocal
- 4.1 什么是ThreadLocal?
- 4.2 ThreadLocal环境搭建
- 4.3 ThreadLocal快速入门
- 4.4 ThreadLocal源码阅读
- threadLocal.set()源码
- threadLocal.get()源码
- 5. 文件上传基本介绍
- 5.1 文件上传-原理示意图
- 5.2 文件上传页面
- 5.3 走通Servlet
- 5.4 表单项区别处理
- 5.5 创建目录-保存文件
- 5.6 中文编码问题
- 5.7 文件上传注意事项和细节
- 5.7.1 按照年月日目录存放
- 5.7.2 文件覆盖问题
- 5.7.3 封装一下
- 5.8 文件上传其他注意事项
- 5.8.1 upload文件夹为何要在out目录下直接创建
- 6. 文件下载基本介绍
- 6.1 原理示意图
- 6.2 走通Servlet
- 6.3 设置下载响应头
- 6.4 文件下载注意事项
JSON和AJAX文档介绍
在线文档
离线文档
1. JSON介绍
- JSON指的是JavaScript对象表示法(JavaScript Object Notation)
- JSON是轻量级的文本数据交换格式
- JSON独立于语言[即java, php, asp.net, go等都可以使用JSON]
1.1 JSON快速入门
JSON的定义格式
var 变量名 = {
"k1" : value, //Number类型
"k2" : "value", //字符串类型
"k3" : [], // 数组类型
"k4" : {}, //json对象类型
"k5" : [{},{}] //json数组
};
var myJson = {
"key1":"赵志伟", //字符串
"key2":23, //Number
"key3":[1,"hello",3.2], //数组
"key4":{"age":23, "name":"赵志伟"}, //json对象
"key5":[{"亚丝娜":"我的老婆", "桐谷和人":"我"},{"k1":23, "k2":"zzw"}]
};
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>json 快速入门案例</title>
<script type="text/javascript">
/*
1.myJson就是一个json对象
2.演示如何获取到json对象的属性/key
*/
var myJson = {
"key1": "赵志伟",//字符串
"key2": 123,//Number
"key3": [1, "hello", 2.3],//数组
"key4": {"age": 12, "name": "jack"},//json对象
"key5": [//json数组
{"k1": 10, "k2": "apple"},
{"k3": 30, "k4": "john"}
]
};
//1.取出key1
console.log("key1 = " + myJson.key1);
//2.取出key3
console.log("key3 = " + myJson.key3)
// 可以对key3取出的值(Array)进行遍历
for (var i = 0; i < myJson.key3.length; i++) {
console.log("第%i个元素的值 = ", i, myJson.key3[i]);
}
//3.取出key4
console.log("key4 = ", myJson.key4, "key4.name = " + myJson.key4.name);
//4.取出key5
console.log("key5 = ", myJson.key5, "k4 = " + myJson.key5[1].k4)
</script>
</head>
<body>
</body>
</html>
1.2 JSON和字符串转换
1.2.1 JSON转字符串
- JSON.stringify(json): 将一个json对象转换为json字符串
- JSON.parse(jsonString): 将一个json字符串转化为json对象
1.2.2 字符串转JSON
1.2.3 JSON和字符串转换细节
- JSON.stringify(json对象)会返回json对象对应的String, 并不会影响原来的json对象
- JSON.parse(String)函数会返回对应的json的对象, 并不会影响原来的String
- 在定义json对象时, 可以使用双引号表示字符串, 也可以使用单引号表示字符串. key可以不用引号.
var personJson = {
"name": "赵志伟",
"age": 23
}
var personJson = {
'name': '赵志伟',
'age': 23
}
var personJson = {
name: '赵志伟',
age: 23
}
- 但是在把原生字符串转成json对象时, 必须使用双引号"", 其他情况会报错
比如
var dogStr = “{‘name’:‘喵喵’, ‘age’:1}”;
➡JSON.parse(dogStr); 会报错
正确写法:
- JSON.stringify(json对象)返回的字符串(json对象->String), 都是双引号括起来的字符串, 所以在语法格式正确的情况下, 是可以重新转成json对象的(String->json对象)
1.3 JSON在java中使用
- 在java中使用json, 需要引入到第三方的包gson.jar
- Gson是Google提供的用来在Java对象和JSON数据之间进行映射的Java类库
- 新建
- 引入gson.jar包
1.3.1 Java对象和JSON字符串转换
1.3.2 List对象和JSON字符串转换
public class JavaBean {
public static void main(String[] args) {
//创建一个gson对象, 作为一个工具对象使用
Gson gson = new Gson();
//3.演示把 List对象 -> json字符串
List<Book> bookList = new ArrayList<>();
bookList.add(new Book(2, "林北星"));
bookList.add(new Book(3, "张万森"));
//解读: 因为把一个 对象/集合 转成字符串, 相对比较简单
//底层只需要遍历, 按照json格式拼接返回即可
String bookListStr = gson.toJson(bookList);
System.out.println("bookListStr= " + bookListStr);
//4.演示把 json字符串 -> List对象
//解读
// (1)如果需要把json字符串 转成 集合这样复杂的类型, 需要使用gson提供的一个类
// (2)TypeToken, 是一个自定义泛型类, 然后通过TypeToken来指定我们需要转换成的类型
/*
package com.google.gson.reflect;
public class TypeToken<T> {
final Class<? super T> rawType;
final Type type;
final int hashCode;
protected TypeToken() {
this.type = getSuperclassTypeParameter(this.getClass());
this.rawType = Types.getRawType(this.type);
this.hashCode = this.type.hashCode();
}
*/
//(1)返回的是类型的完整路径: java.util.List<com.zzw.json.Book>
//(2)gson的设计者, 需要得到类型的完整路径, 然后在底层进行反射
//(3)所以json设计者就提供了TypeToken来搞定
//二说TypeToken [为什么要添加{} 涉及到内部类..]
Type type = new TypeToken<List<Book>>() {}.getType();
List<Book> bookList2 = gson.fromJson(bookListStr, type);
System.out.println("bookList2 = " + bookList2);
}
}
- 二说TypeToken
- 三说TypeToken
1.3.3 Map对象和JSON字符串转换
2. Ajax介绍
- AJAX即Asynchronous Javascript And XML(异步 JavaScript 和 XML)
- AJAX是一种浏览器异步发起请求(可以指定发送哪些数据), 局部更新页面的技术
2.1 Ajax应用场景
- 搜索引擎根据用户输入的关键字, 自动提示检索关键字
- 动态加载数据, 按需取得数据 [树形菜单, 联动菜单…]
- 改善用户体验 [输入内容前提示, 带进度条文件上传…]
- 电子商务应用 [购物车, 邮件订阅…]
- 访问第三方服务 [访问搜索服务, rss阅读器]
- 页面局部刷新, https://piaofang.maoyan.com/dashboard
2.2 传统的web应用-数据通信方式
2.3 Ajax-数据通信方式
2.4 Ajax文档使用
2.5 Ajax快速入门
2.5.1 验证用户是否存在-思路框架图
2.5.2 新建java项目
- 新建java项目
- 引入web框架
- 新建lib目录,并引入jar包
- 配置Tomcat
- 成功启动
2.5.3 实现
- 搭建框架
- 发送ajax请求[http请求]
- 后台接收并返回json格式的数据
- 前端得到数据, 通过dom操作, 进行页面局部刷新
- 测试
2.6 接入数据库
引入数据库进行验证
项目结构 粘贴自满汉楼项目
- 思维框架图
- User实体类-无参构造器
- UserDAO
- UserService
- CheckUserServlet
- 发现错误
解决方案
src是se方式下的路径, javaweb要用到类加载器
- 测试
3. jQuery操作Ajax
- 编写原生的Ajax要写很多的代码, 要考虑浏览器兼容的问题
- 在实际工作中, 一般使用JavaScript的库(比如Jquery)发送Ajax请求
3.1 jQuery操作Ajax文档
- 在线文档
- 离线文档
3.2 jQuery.ajax()函数
- $.ajax常用函数
$.ajax常用函数的位置
- url: 请求的地址
- type: 请求的方式get或post
- data: 发送到服务器的数据, 将自动转换为请求字符串格式
- success: 成功的回调函数
- error: 失败的回调函数
- dataType: 返回的数据类型 常用json和text
3.3 $.get 和 $.post常用参数
- url: 请求的URL地址
- data: 请求发送到服务器的数据
- success: 成功时回调函数
- type: 返回内容格式, xml.html.script.json.text
说明: $.get和$.post底层还是使用$.ajax()方法来实现异步请求
3.4 jQuery.ajax()快速入门
- 在web目录下引入jquery后, Rebuild Project一下
- 发送ajax请求[http请求]
- 后台接收并返回json格式的数据
- 测试
解决方案
- 后台接收并返回数据
- 前端获取
- 前端判断
测试
- 在div中显示内容
测试
3.4 jQuery.get()快速入门
- $.get()默认是get请求, 不需要再指定请求方式
- 不需要指定参数名
- 填写的实参, 是顺序的: url—data—success回调函数—dataType
- 没有提供error接口, 可以在success回调函数里根据status判断
3.5 jQuery.post()快速入门
操作: 只需把$.get改成$.post
$.post和$.get的方式一样, 只是这时, 是按照post方法发送ajax请求[本质是http请求]
- $.post()默认是post请求, 不需要再指定请求方式
- 不需要指定参数名
- 填写的实参, 是顺序的: url—data—success回调函数—dataType
- 没有提供error接口, 可以在success回调函数里根据status判断
3.6 jQuery.getJSON快速入门
$.getJSON常用参数
- url:请求发送的URL
- data: 请求发送到服务器的数据
- success: 请求成功时的回调函数
如果你通过jQuery发送的ajax请求[本质是http请求]是get请求, 并且返回的数据格式是JSON, 可以直接使用$.getJSON(). 简洁.
3.7 接入数据库
- 接入数据库
- 前端测试
4. ThreadLocal
4.1 什么是ThreadLocal?
- ThreadLocal的作用: 可以实现在同一个线程数据共享, 从而解决多线程数据安全问题.
- ThreadLocal可以给当前线程关联一个数据(普通变量, 对象, 数据) - set方法
- ThreadLocal可以像Map一样存取数据, key为当前线程 - get方法
- 每一个ThreadLocal对象, 只能为当前线程关联一个数据. 如果要为当前线程关联多个数据, 就需要使用多个ThreadLocal对象实例
- 每个ThreadLocal对象实例定义的时候, 一般为static类型
- ThreadLocal中保存的数据. 在线程销毁时. 会自动释放
4.2 ThreadLocal环境搭建
现象->分析原理->看源码进一步理解
- 创建java项目
- ThreadLocal类图
- 开启线程
4.3 ThreadLocal快速入门
4.4 ThreadLocal源码阅读
threadLocal.set()源码
解读set源码
public void set (T value){ //1.获取当前线程,关联到当前线程 Thread t = Thread.currentThread(); //2.通过线程对象, 获取到ThreadLocalMap, 是ThreadLocal的静态内部类 // ThreadLocalMap的类型是: ThreadLocal.ThreadLocalMap ThreadLocalMap map = getMap(t); //3.如果map不为空, 将数据(dog,pig...)放入map中, key:threadLocal, value:存放的数据 // 从这个源码我们已然看出一个threadLocal只能关联一个数据, 如果再次set(), 就会替换 //4. 如果map为空, 就创建一个和当前线程关联的ThreadLocalMap, 并且将该数据放入 if (map != null) { map.set(this, value); } else { createMap(t, value); } }
debug1 存储
debug2 替换
debug3
threadLocal.get()源码
解读get源码
public T get() { //1.得到当前的线程对象 Thread t = Thread.currentThread(); //2.得到线程的threadLocals属性, 即ThreadLocalMap ThreadLocalMap map = getMap(t); if (map != null) { //3.如果线程的threadLocals不为空, 根据当前的threadLocal对象, 得到对应的Entry ThreadLocalMap.Entry e = map.getEntry(this); //如果Entry不为空 if (e != null) { @SuppressWarnings("unchecked") //返回当前的threadLocal关联的数据 T result = (T)e.value; return result; } } return setInitialValue(); }
5. 文件上传基本介绍
- 文件的上传和下载, 是常见的功能
- 后面项目就使用了文件上传下载
- 如果是传输大文件, 一般用专门的工具或插件
- 文件上传下载需要使用两个包, 需要导入
commons-fileupload-1.2.1.jar
commons-io-1.4.jar
5.1 文件上传-原理示意图
文件上传的解读
- 还是使用表单来提交
- action依然是按照以前的规定来确认
- method需指定为post
- enctype: 全称encodetype, 即编码类型. 默认是 application/x-www-form-urlencoded, 即url编码, 这种编码不适合于二进制文件的提交.
enctype要指定为 multipart/form-data, 即表示表单提交的数据有多个部分组成, 也就是说即可以提交二进制数据, 也可以提交文本数据
服务端要完成的工作 FileUploadServlet.java
- 判断是不是一个文件表单
- 判断表单提交的各个表单项是什么类型
- 如果是一个普通的表单项, 就按照文本的方式来处理
- 如果是一个文件表单项(二进制数据), 使用IO技术来处理
- 把表单提交的文件数据, 保存到你指定的服务端的某个目录
5.2 文件上传页面
新建项目, 导入web框架, 引入jar包, 配置Tomcat, 配置FileUploadServlet👉参考
<%--
Created by IntelliJ IDEA.
User: 赵志伟
Version: 1.0
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<!-- 指定了base标签 -->
<base href="<%=request.getContextPath()+"/"%>>">
<style type="text/css">
input[type="submit"] {
outline: none;
border-radius: 5px;
cursor: pointer;
background-color: #31B0D5;
border: none;
width: 70px;
height: 35px;
font-size: 20px;
}
img {
border-radius: 50%;
}
form {
position: relative;
width: 200px;
height: 200px;
}
input[type="file"] {
position: absolute;
left: 0;
top: 0;
height: 200px;
opacity: 0;
cursor: pointer;
}
</style>
<script type="text/javascript">
function prev(event) {
//获取展示图片的区域
var img = document.getElementById("preView");
//获取文件对象
var file = event.files[0];
//获取文件阅读器: Js的一个类, 直接使用即可
var reader = new FileReader();
read.readAsDataURL(file);
reader.onload = function () {
//给img的src设置图片url
img.setAttribute("src", this.result)
}
}
</script>
</head>
<body>
<%--表单的enctype属性要设置为multipart/form-data
enctype="multipart/form-data" 表示提交的数据是多个部分构成的. 有文件和文本
--%>
<form action="fileUploadServlet" method="post" enctype="multipart/form-data">
家居图: <img src="2.jpg" alt="" width="200" height="200" id="preView"><%--img是单标签, 后面不能加斜杠--%>
<input type="file" name="pic" id="" value="" onchange="prev(this)"/>
家居名: <input type="text" name="name"><br/>
<input type="submit" value="上传"/>
</form>
</body>
</html>
5.3 走通Servlet
public class FileUploadServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request, response);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("FileUpLoadServlet 被调用...");
//1.判断是不是文件表单(enctype="multipart/form-data")
if (ServletFileUpload.isMultipartContent(request)) {
System.out.println("OK");
//2.创建 DiskFileItemFactory 对象, 用于构建一个解析上传数据的工具对象
DiskFileItemFactory diskFileItemFactory = new DiskFileItemFactory();
//3.创建一个解析上传数据的工具对象
/**
* <input type="file" name="pic" id="" value="" οnchange="prev(this)"/>
* 家居名: <input type="text" name="name"><br/>
* <input type="submit" value="上传"/>
*/
ServletFileUpload servletFileUpload =
new ServletFileUpload(diskFileItemFactory);
//4.关键代码, servletFileUpload 对象可以把表单提交的数据text/文件.
// 将其封装到 FileItem 文件项中
try {
/**输出
* list==>
* [name=winner-autumn-2022 - 鍓湰.png,
* StoreLocation=D:\Program Files\apache-tomcat-8.5.82\temp\\upload_62628cc5_1881341bf4c__7f85_00000000.tmp,
* size=598521bytes, isFormField=false, FieldName=pic, name=null,
* StoreLocation=D:\Program Files\apache-tomcat-8.5.82\temp\\upload_62628cc5_1881341bf4c__7f85_00000001.tmp,
* size=6bytes, isFormField=true, FieldName=name]
*/
List<FileItem> list = servletFileUpload.parseRequest(request);
System.out.println("list==>" + list);
} catch (FileUploadException e) {
throw new RuntimeException(e);
}
} else {
System.out.println("不是文件表单...");
}
}
}
5.4 表单项区别处理
//4.关键代码, servletFileUpload 对象可以把表单提交的数据text/文件.
// 将其封装到 FileItem 文件项中
try {
/**输出
* list==>
* [name=winner-autumn-2022 - 鍓湰.png,StoreLocation=D:\Program Files\apache-tomcat-8.5.82\temp\\upload_62628cc5_1881341bf4c__7f85_00000000.tmp,size=598521bytes, isFormField=false, FieldName=pic,
* name=null,StoreLocation=D:\Program Files\apache-tomcat-8.5.82\temp\\upload_62628cc5_1881341bf4c__7f85_00000001.tmp, size=6bytes, isFormField=true, FieldName=name]
*/
List<FileItem> list = servletFileUpload.parseRequest(request);
//System.out.println("list==>" + list);
//遍历, 并分别处理
for (FileItem fileItem : list) {
//不知道是什么, 就输出看一下
//System.out.println(fileItem);
//判断是不是一个文件==>OOP程序员
if (fileItem.isFormField()) {//如果为真,就是文本 input type="text"
String name = fileItem.getString("utf-8");
System.out.println("家居名= " + name);
} else {//是一个文件
//获取上传的文件的名字
String name = fileItem.getName();
System.out.println("上传的文件名= " + name);
}
}
} catch (FileUploadException e) {
throw new RuntimeException(e);
}
} else {
System.out.println("不是文件表单...");
}
5.5 创建目录-保存文件
if (fileItem.isFormField()) {//如果为真,就是文本 input type="text"
String name = fileItem.getString("utf-8");
System.out.println("家居名= " + name);
} else {//是一个文件
//获取上传的文件的名字
String name = fileItem.getName();
System.out.println("上传的文件名= " + name);
//把上传到服务器 temp目录 下的文件保存到你指定的目录👉upload
// 1.指定一个目录, 我们网站的工作目录下
String filePath = "/upload/";
// 2.获取完整的目录[io/servlet基础]
// 这个目录是合你的web项目运行环境绑定的, 是动态的
// fileRealPath= D:\idea_project\zzw_javaweb\fileupdown\out\artifacts\fileupdown_war_exploded\\upload\
String fileRealPath =
request.getServletContext().getRealPath(filePath);
System.out.println("fileRealPath= " + fileRealPath);
//3.创建这个上传的目录=>
File fileRealPathDirectory = new File(fileRealPath);
if (!fileRealPathDirectory.exists()) {//如果目录不存在, 就创建
fileRealPathDirectory.mkdirs();//创建
}
//4.将文件拷贝到fileRealPathDirectory目录下
// 构建了一个上传的文件的完整路径[目录+文件名], 这个路径由 目录+该文件的文件名 组成
String fileFullPath = fileRealPathDirectory + "\\" + name;//这里必须加斜杠
System.out.println("fileFullPath= " + fileFullPath);
fileItem.write(new File(fileFullPath));
//5.提示信息
response.setContentType("text/html;charset=utf-8");
PrintWriter writer = response.getWriter();
writer.print("<h3>文件上传成功</h3>");
}
5.6 中文编码问题
5.7 文件上传注意事项和细节
5.7.1 按照年月日目录存放
- 如果将文件都上传到一个目录下, 当上传文件很多时, 会造成访问文件的速度变慢, 因此可以将文件上传到多个目录下.
比如: 一天上传的文件, 统一放到一个文件夹, 按照年月日格式, 比如👉20230513文件夹
手动更改时间 再次测试
工具类WebUtilspublic class WebUtils { public static void main(String[] args) { //测试代码 System.out.println(getYearMonthDay()); } public static String getYearMonthDay() { //第二代日期类Calendar Calendar calendar = Calendar.getInstance(); int year = calendar.get(calendar.YEAR); int month = calendar.get(calendar.MONTH) + 1;//月份从0开始计算 int day = calendar.get(calendar.DAY_OF_MONTH); String format = year + "/" + month + "/" + day + "/";// 2023/5/13/ //第三代日期类LocalDateTime LocalDate now = LocalDate.now(); year = now.getYear(); month = now.getMonthValue(); day = now.getDayOfMonth(); format = year + "/" + month + "/" + day + "/";// 2023/5/13/ return format; } }
5.7.2 文件覆盖问题
5.7.3 封装一下
把这部分代码摘出来封装进工具类里
5.8 文件上传其他注意事项
- 一个完美的文件上传, 要考虑的因素很多, 比如断点续传, 控制图片大小, 尺寸, 分片上传, 防止恶意上传等. 在项目中, 可以考虑使用WebUploader组件(百度开发👉https://fex.baidu.com/webuploader/doc/index.html).
- 文件上传功能, 在项目中建议有限制的使用, 一般用在头像, 证明, 合同, 产品展示等, 如果不加限制, 就会造成服务器空间被大量占用[比如微信发1次朋友圈最多9张图等].
5.8.1 upload文件夹为何要在out目录下直接创建
文件上传, 创建web/upload的文件夹. 在tomcat启动时, 没有在out目录下创建对应的upload文件夹. 其原因是tomcat对应空目录是不会在out下创建相应目录的. 所以, 只需在upload目录下放一个文件即可
- 这是web路径一个空文件夹, Tomcat启动后, 是不会在out/artifacts下创建相应目录的
- 如图, upload100文件夹下是有文件的, 那么在Tomcat启动后, out/artifacts下就会创建upload100目录
6. 文件下载基本介绍
6.1 原理示意图
响应头
- Content-Disposition: 表示下载的数据的展示方式. 比如内联形式(网页形式或者网页一部分), 或者是文件下载方式 attachment
- Content-type: 指定返回数据的类型MIME
响应体
- 在网络传输时是图片的原生数据(按照浏览器下载的编码)
6.2 走通Servlet
download.jsp->FileDownloadServlet
如果重启Tomcat后, 在out目录下你没有看到你创建的download文件夹, rebuild project -> 再次重启Tomcat(不能是重新发布)
公共资源为什么不直接放在工作路径下?👉因为Tomcat重启之后out目录就会清空
6.3 设置下载响应头
public class FileDownloadServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request, response);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("fileDownloadServlet 被调用...");
//1.准备要下载的文件, 放在web路径下的download目录, 然后重启Tomcat, 让程序把download文件夹加载到项目工作路径下
// 如果实在不行, rebuild project -> 重启Tomcat(不是重新发布, 是重启)
//2.获取要下载的文件的名字
request.setCharacterEncoding("utf-8");
String downloadFileName = request.getParameter("name");
System.out.println("downloadFileName= " + downloadFileName);
//3.给http响应,设置响应头Content-Type, 就是文件的MIME类型
// 通过servletContext来获取
ServletContext servletContext = request.getServletContext();
String downloadPath = "/download/";//下载目录 从web工程根目录计算
String downloadFileFullPath = downloadPath + downloadFileName;//拼接后->/download/1.jpg
String mimeType = servletContext.getMimeType(downloadFileFullPath);
System.out.println("mimeType= " + mimeType);
response.setContentType(mimeType);
//4.给http响应,设置Content-Disposition
// 这里考虑的细节比较多, 比如不同的浏览器写法不一样, 要考虑编码
// ff: 文件名中文需要base64, 而ie/chrome是URL编码
//(1)如果是Firefox 中文编码需要base64
//(2)Content-Disposition 指定下载的数据的展示形式(如果是attachment, 则使用文件下载方式;如果没有指定, 一般是以网页形式展示)
//(3)如果是其它(主流ie/chrome), 中文编码使用URL编码
if (request.getHeader("User-Agent").contains("Firefox")) {
// 火狐 Base64编码
response.setHeader("Content-Disposition", "attachment; filename==?UTF-8?B?" +
new BASE64Encoder().encode(downloadFileName.getBytes("UTF-8")) + "?=");
} else {
// 其他(主流ie/chrome)使用URL编码操作
response.setHeader("Content-Disposition", "attachment; filename=" +
URLEncoder.encode(downloadFileName, "UTF-8"));
}
//5.读取下载的文件数据, 返回给客户端/浏览器
//(1)创建一个和要下载的文件 关联的输入流
InputStream resourceAsStream = servletContext.getResourceAsStream(downloadFileFullPath);
//(2)得到返回数据的输出流[因为返回的文件大多数是二进制(不管是文本还是二进制都可以按字节处理),IO]
ServletOutputStream outputStream = response.getOutputStream();
//(3)使用工具类, 将输入流关联的文件, 对拷到输出流, 并返回给IOUtils
IOUtils.copy(resourceAsStream, outputStream);
}
}
6.4 文件下载注意事项
- 文件下载, 比较麻烦的就是文件名中文处理
- 对于网站的文件, 很多文件使用另存为即可下载, 对于大文件(文档, 视频), 会使用专业的下载工具(迅雷, 华为网盘, 腾讯, 百度等).
- 对于不同的浏览器, 在把文件下载完毕后, 处理的方式不一样, 有的是直接打开文件, 有的是将文件下载到本地的下载目录