JSON+AJAX+ThreadLocal+文件上传下载

news2025/1/19 20:18:51

文章目录

  • 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介绍

  1. JSON指的是JavaScript对象表示法(JavaScript Object Notation)
  2. JSON是轻量级的文本数据交换格式
    在这里插入图片描述
  3. 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转字符串

  1. JSON.stringify(json): 将一个json对象转换为json字符串
  2. JSON.parse(jsonString): 将一个json字符串转化为json对象
    在这里插入图片描述

1.2.2 字符串转JSON

在这里插入图片描述

1.2.3 JSON和字符串转换细节

  1. JSON.stringify(json对象)会返回json对象对应的String, 并不会影响原来的json对象
  2. JSON.parse(String)函数会返回对应的json的对象, 并不会影响原来的String
    在这里插入图片描述
  3. 在定义json对象时, 可以使用双引号表示字符串, 也可以使用单引号表示字符串. key可以不用引号.
 var personJson = {
     "name": "赵志伟",
     "age": 23
 }
 var personJson = {
     'name': '赵志伟',
     'age': 23
 }
 var personJson = {
     name: '赵志伟',
     age: 23
 }
  1. 但是在把原生字符串转成json对象时, 必须使用双引号"", 其他情况会报错

比如
var dogStr = “{‘name’:‘喵喵’, ‘age’:1}”;
➡JSON.parse(dogStr); 会报错
在这里插入图片描述
正确写法:
在这里插入图片描述

  1. JSON.stringify(json对象)返回的字符串(json对象->String), 都是双引号括起来的字符串, 所以在语法格式正确的情况下, 是可以重新转成json对象的(String->json对象)
    在这里插入图片描述

1.3 JSON在java中使用

  1. 在java中使用json, 需要引入到第三方的包gson.jar
  2. Gson是Google提供的用来在Java对象和JSON数据之间进行映射的Java类库
    在这里插入图片描述
  1. 新建
    在这里插入图片描述
  2. 引入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);
   }
}
  1. 二说TypeToken
    在这里插入图片描述
    在这里插入图片描述
  2. 三说TypeToken
    )

1.3.3 Map对象和JSON字符串转换

在这里插入图片描述

2. Ajax介绍

  1. AJAX即Asynchronous Javascript And XML(异步 JavaScript 和 XML)
  2. AJAX是一种浏览器异步发起请求(可以指定发送哪些数据), 局部更新页面的技术

2.1 Ajax应用场景

  1. 搜索引擎根据用户输入的关键字, 自动提示检索关键字
  2. 动态加载数据, 按需取得数据 [树形菜单, 联动菜单…]
  3. 改善用户体验 [输入内容前提示, 带进度条文件上传…]
  4. 电子商务应用 [购物车, 邮件订阅…]
  5. 访问第三方服务 [访问搜索服务, rss阅读器]
  6. 页面局部刷新, https://piaofang.maoyan.com/dashboard

2.2 传统的web应用-数据通信方式

在这里插入图片描述

2.3 Ajax-数据通信方式

在这里插入图片描述

2.4 Ajax文档使用

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

2.5 Ajax快速入门

2.5.1 验证用户是否存在-思路框架图

在这里插入图片描述

2.5.2 新建java项目

  1. 新建java项目
    在这里插入图片描述
  2. 引入web框架
    在这里插入图片描述
    在这里插入图片描述
  3. 新建lib目录,并引入jar包
    在这里插入图片描述
    在这里插入图片描述
  4. 配置Tomcat在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
  5. 成功启动
    在这里插入图片描述

2.5.3 实现

  1. 搭建框架
    在这里插入图片描述
  2. 发送ajax请求[http请求]
    在这里插入图片描述
  3. 后台接收并返回json格式的数据
    在这里插入图片描述
  4. 前端得到数据, 通过dom操作, 进行页面局部刷新
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
  5. 测试
    在这里插入图片描述
    在这里插入图片描述

2.6 接入数据库

引入数据库进行验证

项目结构 粘贴自满汉楼项目
在这里插入图片描述

  1. 思维框架图
    在这里插入图片描述
    在这里插入图片描述
  2. User实体类-无参构造器
    在这里插入图片描述
  3. UserDAO
    在这里插入图片描述
  4. UserService
    在这里插入图片描述
  5. CheckUserServlet
    在这里插入图片描述
    在这里插入图片描述
  6. 发现错误
    在这里插入图片描述
    解决方案
    src是se方式下的路径, javaweb要用到类加载器
    在这里插入图片描述
  7. 测试
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

3. jQuery操作Ajax

  1. 编写原生的Ajax要写很多的代码, 要考虑浏览器兼容的问题
  2. 在实际工作中, 一般使用JavaScript的库(比如Jquery)发送Ajax请求

3.1 jQuery操作Ajax文档

  1. 在线文档
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
  2. 离线文档在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

3.2 jQuery.ajax()函数

  1. $.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()快速入门

  1. 在web目录下引入jquery后, Rebuild Project一下
    在这里插入图片描述
  2. 发送ajax请求[http请求]
    在这里插入图片描述
  3. 后台接收并返回json格式的数据
    在这里插入图片描述
  4. 测试
    在这里插入图片描述
    解决方案
    在这里插入图片描述
    在这里插入图片描述
  1. 后台接收并返回数据
    在这里插入图片描述
  2. 前端获取
    在这里插入图片描述
    在这里插入图片描述
  3. 前端判断
    在这里插入图片描述
    测试
    在这里插入图片描述
    在这里插入图片描述
  4. 在div中显示内容
    在这里插入图片描述
    测试
    在这里插入图片描述
    在这里插入图片描述

3.4 jQuery.get()快速入门

  1. $.get()默认是get请求, 不需要再指定请求方式
  2. 不需要指定参数名
  3. 填写的实参, 是顺序的: url—data—success回调函数—dataType
  4. 没有提供error接口, 可以在success回调函数里根据status判断
    在这里插入图片描述
    在这里插入图片描述在这里插入图片描述

3.5 jQuery.post()快速入门

操作: 只需把$.get改成$.post

$.post和$.get的方式一样, 只是这时, 是按照post方法发送ajax请求[本质是http请求]

  1. $.post()默认是post请求, 不需要再指定请求方式
  2. 不需要指定参数名
  3. 填写的实参, 是顺序的: url—data—success回调函数—dataType
  4. 没有提供error接口, 可以在success回调函数里根据status判断
    在这里插入图片描述
    在这里插入图片描述

3.6 jQuery.getJSON快速入门

$.getJSON常用参数

  • url:请求发送的URL
  • data: 请求发送到服务器的数据
  • success: 请求成功时的回调函数

如果你通过jQuery发送的ajax请求[本质是http请求]是get请求, 并且返回的数据格式是JSON, 可以直接使用$.getJSON(). 简洁.
在这里插入图片描述
在这里插入图片描述

3.7 接入数据库

  1. 接入数据库在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
  2. 前端测试 在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

4. ThreadLocal

4.1 什么是ThreadLocal?

  1. ThreadLocal的作用: 可以实现在同一个线程数据共享, 从而解决多线程数据安全问题.
  2. ThreadLocal可以给当前线程关联一个数据(普通变量, 对象, 数据) - set方法
  3. ThreadLocal可以像Map一样存取数据, key为当前线程 - get方法
  4. 每一个ThreadLocal对象, 只能为当前线程关联一个数据. 如果要为当前线程关联多个数据, 就需要使用多个ThreadLocal对象实例
  5. 每个ThreadLocal对象实例定义的时候, 一般为static类型
  6. ThreadLocal中保存的数据. 在线程销毁时. 会自动释放

4.2 ThreadLocal环境搭建

现象->分析原理->看源码进一步理解

  1. 创建java项目
    在这里插入图片描述
  2. ThreadLocal类图
    在这里插入图片描述
  3. 开启线程
    在这里插入图片描述
    在这里插入图片描述

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. 文件上传基本介绍

  1. 文件的上传和下载, 是常见的功能
  2. 后面项目就使用了文件上传下载
  3. 如果是传输大文件, 一般用专门的工具或插件
  4. 文件上传下载需要使用两个包, 需要导入
    commons-fileupload-1.2.1.jar
    commons-io-1.4.jar
    在这里插入图片描述
    在这里插入图片描述

5.1 文件上传-原理示意图

文件上传的解读

  1. 还是使用表单来提交
  2. action依然是按照以前的规定来确认
  3. method需指定为post
  4. enctype: 全称encodetype, 即编码类型. 默认是 application/x-www-form-urlencoded, 即url编码, 这种编码不适合于二进制文件的提交.
    enctype要指定为 multipart/form-data, 即表示表单提交的数据有多个部分组成, 也就是说即可以提交二进制数据, 也可以提交文本数据

服务端要完成的工作 FileUploadServlet.java

  1. 判断是不是一个文件表单
  2. 判断表单提交的各个表单项是什么类型
  3. 如果是一个普通的表单项, 就按照文本的方式来处理
  4. 如果是一个文件表单项(二进制数据), 使用IO技术来处理
  5. 把表单提交的文件数据, 保存到你指定的服务端的某个目录

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 按照年月日目录存放

  1. 如果将文件都上传到一个目录下, 当上传文件很多时, 会造成访问文件的速度变慢, 因此可以将文件上传到多个目录下.
    比如: 一天上传的文件, 统一放到一个文件夹, 按照年月日格式, 比如👉20230513文件夹
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    手动更改时间 再次测试
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    工具类WebUtils
public 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 文件上传其他注意事项

  1. 一个完美的文件上传, 要考虑的因素很多, 比如断点续传, 控制图片大小, 尺寸, 分片上传, 防止恶意上传等. 在项目中, 可以考虑使用WebUploader组件(百度开发👉https://fex.baidu.com/webuploader/doc/index.html).
  2. 文件上传功能, 在项目中建议有限制的使用, 一般用在头像, 证明, 合同, 产品展示等, 如果不加限制, 就会造成服务器空间被大量占用[比如微信发1次朋友圈最多9张图等].

5.8.1 upload文件夹为何要在out目录下直接创建

文件上传, 创建web/upload的文件夹. 在tomcat启动时, 没有在out目录下创建对应的upload文件夹. 其原因是tomcat对应空目录是不会在out下创建相应目录的. 所以, 只需在upload目录下放一个文件即可

  1. 这是web路径一个空文件夹, Tomcat启动后, 是不会在out/artifacts下创建相应目录的
    在这里插入图片描述
    在这里插入图片描述
  2. 如图, upload100文件夹下是有文件的, 那么在Tomcat启动后, out/artifacts下就会创建upload100目录
    在这里插入图片描述
    在这里插入图片描述

6. 文件下载基本介绍

6.1 原理示意图

响应头

  1. Content-Disposition: 表示下载的数据的展示方式. 比如内联形式(网页形式或者网页一部分), 或者是文件下载方式 attachment
  2. Content-type: 指定返回数据的类型MIME

响应体

  1. 在网络传输时是图片的原生数据(按照浏览器下载的编码)

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 文件下载注意事项

  1. 文件下载, 比较麻烦的就是文件名中文处理
    在这里插入图片描述
  2. 对于网站的文件, 很多文件使用另存为即可下载, 对于大文件(文档, 视频), 会使用专业的下载工具(迅雷, 华为网盘, 腾讯, 百度等).
  3. 对于不同的浏览器, 在把文件下载完毕后, 处理的方式不一样, 有的是直接打开文件, 有的是将文件下载到本地的下载目录

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/527840.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

DAY 58 数据库的存储引擎

存储引擎的概念 什么是存储引擎 MySQL中的数据用各种不下同的技术存储在文件中&#xff0c;每一种技术都使用不同的存储机制、索引技巧、锁定水平并最终提供不同的功能和能力&#xff0c;这些不同的技术以及配套的功能在MySQL中称为存储引擎。 存储引擎是MySQL将数据存储在文…

JUC之volatile

作用&#xff1a;volatile是Java提供的一种轻量级的同步机制 保证内存可见性 不保证原子性 防止指令重排序 public class VolatileDemo {private static int num0;public static void main(String[] args) {new Thread(()->{while (num0){}},"A").start();try {…

Spark大数据处理讲课笔记4.6 Spark SQL数据源 - JDBC

文章目录 零、本讲学习目标一、Spark SQL读取关系数据库二、Spark SQL JDBC连接属性三、创建数据库与表&#xff08;一&#xff09;创建数据库&#xff08;二&#xff09;创建学生表&#xff08;二&#xff09;创建成绩表 四、读取和写入数据库表&#xff08;一&#xff09;利用…

力扣第 104 场双周赛 2681. 英雄的力量

原题链接力扣 题目大意&#xff1a;我开始看成连续子段了&#xff0c;写了个递归程序....... 一个数组任选一个子序列&#xff0c;子序列的力量值最大值平方*最小值。求所有子序列的力量和。 分析过程&#xff1a;如序列长度为n&#xff0c;子序列总数为2的n次幂&#xff0c…

SpringCloud------zookeeper代替Eureka,zookeeper版本冲突解决(七)

SpringCloud------zookeeper代替Eureka&#xff08;七&#xff09; SpringCloud整合zookeeper代替Eureka 注册中心zookeeper zookeeper是一个分布式协调工具&#xff0c;可以实现注册中心功能 关闭Linux服务器防火墙后&#xff0c;启动zookeeper服务器 zookeeper服务器取代Eur…

mac桌面文件删除怎么恢复?别急,有办法!

大家是不是习惯于将临时要用的文件都存放在桌面上。虽然文件放在桌面上&#xff0c;可以方便我们随时调取&#xff0c;但是也容易出现误删除的情况&#xff0c;给我们带来麻烦。mac桌面文件删除怎么恢复&#xff1f;希望通过本篇教程&#xff0c;你能找回误删除的桌面文件。 案…

script标签type值application/json,importmap和module

type&#xff08;默认text/javascript&#xff09; 该属性定义 script 元素包含或src引用的脚本语言。属性的值为 MIME 类型&#xff08;媒体类型&#xff09;&#xff1b; 如果没有定义这个属性&#xff0c;脚本会被视作 JavaScript。 如果 MIME 类型不是 JavaScript 类型&a…

GPT4结对编程实战,鹅厂一线研发真实使用感受

&#x1f449;腾小云导读 ChatGPT4相比ChatGPT3.5在逻辑推理能力上有很大的进步&#xff0c;其代码生成能力颇为优越。因此作者尝试在工作中某些不涉密的基础工作上&#xff0c;应用ChatGPT4来提升研发效率&#xff0c;简单尝试之后发现其在不少场景是有效的。本文将向大家展示…

元宇宙又“死”了!Epic老板:你当6亿用户是摆设?

“扎克伯格花了数年时间试图让Metaverse成为现实&#xff0c;但现在它已被AI取代&#xff0c;并走向科技创意的坟墓。”一篇表达“元宇宙已死”的文章近期在推特上引发热议&#xff0c;而游戏制作公司Epic Games CEO Tim Sweeney的还击更是让这个话题热上加热。 “搞一次在线守…

【SSL证书】使用mkcert创建局域网或单机可信任Windows格式证书

初学者对SSL证书的理解可能非常模糊。所谓SSL证书&#xff0c;其实它包含两个方面&#xff0c;一是根证书&#xff0c;二是HTTPS的证书&#xff0c;HTTPS证书合法性由其根证书来进行认定。几大证书供应商的根证书一般都预置在系统中了&#xff0c;所以给人的错觉就是只知HTTPS证…

深入解析 Facebook 分析工具,洞察用户行为和优化策略

作为一名 Facebook 运营者&#xff0c;了解用户行为和优化策略是至关重要的。在本文中&#xff0c;我们将深入解析Facebook 分析工具&#xff0c;帮助你更好地洞察用户行为和优化策略。 1.Facebook 像素 Facebook 像素是一个重要的工具&#xff0c;可以帮助运营者了解用户在网…

SNMP简介

背景 简单网络管理协议SNMP&#xff08;Simple Network Management Protocol&#xff09;用于网络设备的管理。网络设备种类多种多样&#xff0c;不同设备厂商提供的管理接口&#xff08;如命令行接口&#xff09;各不相同&#xff0c;这使得网络管理变得愈发复杂。为解决这一…

新车推迟、裁员降本,沃尔沃被现实狠狠“扇了一个耳光”

汽车行业的魅力&#xff0c;或许就在于不断的给自己打气&#xff0c;然后被打脸。 今年&#xff0c;上海车展开幕前&#xff0c;沃尔沃汽车大中华区销售公司总裁钦培吉在新车发布会上直言&#xff1a;“新势力会的&#xff0c;我们三年就学会了&#xff1b;我们会的&#xff0c…

无影云桌面,搭建一个属于自己的云上主机

无影云桌面&#xff0c;搭建一个属于自己的云上主机 1.无影云桌面介绍2.无影云桌面试用3.无影云桌面尝鲜4.测试云桌面的连通性5.体验无影云的娱乐办公场景6.将无影云桌面作为服务器使用7.无影云桌面使用总结 1.无影云桌面介绍 无影云桌面是一种云计算技术&#xff0c;可以将用…

《LKD3粗读笔记》(12)内存管理

1、页 内核把物理页作为内存管理的基本单元内存管理单元&#xff08;MMU&#xff09;以页为单位来管理系统中的页表从虚拟内存的角度看&#xff0c;页就是最小单位。体系结构不同&#xff0c;支持页的大小也不尽相同。大多数32位体系结构支持4KB的页&#xff0c;而64位体系结构…

排序算法之堆排序的实现

一、堆的相关概念 堆一般指的是二叉堆&#xff0c;顾名思义&#xff0c;二叉堆是完全二叉树或者近似完全二叉树 1. 堆的性质 ① 是一棵完全二叉树 ② 每个节点的值都大于或等于其子节点的值&#xff0c;为最大堆&#xff1b;反之为最小堆。 2. 堆的存储 一般用数组来表示堆&…

网站域名查询地址-域名所有者查询

域名查询注册信息查询 147SEO域名查询是一款全能的域名查询注册信息查询软件。它不仅提供了单个域名的实时查询功能&#xff0c;还支持批量域名查询功能&#xff0c;可以快速查询多个域名的注册和到期信息。以下是147SEO域名查询的主要特点&#xff1a; 批量域名查询&#xff…

基于【EasyDL】【图像分类】实现农作物病害识别小程序

内容、数据集来源:基于飞桨的农作物病害智能识别系统 - 飞桨AI Studio 项目背景 联合国粮食及农业组织的一份报告表明&#xff0c;每年农业生产的自然损失中有三分之一以上是由农业病虫害造成的&#xff0c;使这些成为当前影响农业生产和农业生产的最重要因素。需要考虑的农业…

浅谈Vue响应式

什么是响应式 不管是自己面试还是八股文告诉你的是&#xff0c;响应式描述的是视图与数据变化之间的一种关系。但这不够准确。 要要了解什么是响应式&#xff0c;我们必须了解不管是React,还是Vue其实本质就是一个函数。 那么我们可以形象的说&#xff1a;响应式描述的就是函数…

编译C++ makefile ZXing-cpp生成条型码 二维码及识别 再试验证成功vs2022

下载ZXing-cpp源码及vs2022 cmake --help看看支持的vs 为什么没有win32呢 进到目录&#xff0c;//新建编译目录//生成vs工程 mkdir build & cd build cmake .. 中间提示opencv和qt等缺东西不要紧&#xff0c;我只用到生成条型码 release也正常。生成成功。 默认是MD 先…