第4章 处理多种内容类型

4.1 内容类型Content-Type

4.1.1 什么是Content-Type

        Content-Type是HTTP协议头中的一个字段,它用于描述HTTP请求或响应中所传输的实体数据的媒体类型(MIME类型)。Content-Type告诉客户端和服务器端所传输的数据的实际内容类型,使得客户端和服务器端可以正确地解析和处理数据。

        Content-Type一般由两部分组成:媒体类型和字符集。其中,媒体类型指的是数据的格式类型,常见的媒体类型有text、image、audio、video等,每个媒体类型都有一个唯一的标识符,例如text/plain表示纯文本格式,image/jpeg表示JPEG格式的图片。字符集则是指所传输的数据所采用的字符编码格式,常见的字符集有UTF-8、GBK等。

        Content-Type在Web开发中非常重要,它能够指示浏览器和服务器如何解析和处理数据。如果Content-Type指定的格式不正确,浏览器或服务器可能无法正确地解析和处理数据,从而导致各种问题。因此,在Web开发中,需要正确设置Content-Type来保证应用程序的正常运行。

4.1.2 错误的Content-Type示例

        举个例子,各位试试错误的Content-Type会出现什么问题?比如将HttpServletResponse中的Content-Type设置为如下:

Content-Type: text/hmtl; charset=utf-8

        使用浏览器访问:http://loclahost:8088/index.html,显示了如下结果:

        显然结果并不理想,浏览器没有将HTML解析为网页内容显示Hello World,而是将网页的HTML脚本内容直接显示了出来。这就是错误的设置Content-Type出现的问题,浏览器无法正确解析和渲染这些内容,从而导致显示异常。如果Content-Type指定的字符集与实际使用的字符集不一致,也可能会导致乱码等问题。

        只有正确设置HTTP响应Content-Type值,Content-Type头才能告诉浏览器内容的格式和字符编码,浏览器需要根据这个头来解析和渲染内容。如果Content-Type头设置正确,浏览器就可以正确地处理内容;否则,可能会导致内容无法正常显示或处理,从而影响用户体验。因此,在开发Web应用程序时,需要正确设置Content-Type头,以确保数据能够被正确地解析和处理。

4.1.3 媒体类型

        Content-Type一般由两部分组成:媒体类型和字符集。

        媒体类型(Media Type),也称为MIME类型(Multipurpose Internet Mail Extensions),是一种用于标识HTTP请求和响应中传输的数据类型的机制。它是由MIME标准定义的一种格式,用于描述一个文件的类型和格式,常见的媒体类型有text、image、audio、video等。

        媒体类型以一种“type/subtype”的形式来定义,其中type表示媒体类型,subtype表示具体的子类型。例如,text/plain表示纯文本格式,image/jpeg表示JPEG格式的图片。除此之外,媒体类型还可以指定一些可选的参数,比如charset参数用于指定所传输的数据所采用的字符编码格式。

        在HTTP请求和响应中,媒体类型通过Content-Type头部字段来指定。在HTTP请求中,Content-Type头部字段告诉服务器所发送的实体数据的格式类型和编码方式;在HTTP响应中,Content-Type头部字段则告诉客户端所返回的实体数据的格式类型和编码方式。由于媒体类型的正确设置对于HTTP协议的正常交互非常重要,因此,正确设置Content-Type头部字段是Web开发中必须要注意的一个方面。

4.1.4 错误的媒体类型示例

        目前的原生Web框架Content-Type设置为固定的"text/html",所以只能处理html类型的文件,不能处理其他多种媒体类型的文件。如果把一个图片放到static文件夹,请求后会出现意想不到的结果:

        需要将Content-Type设置为正确的媒体类型,我们必须了解有哪些媒体类型,如下是常用的媒体类型:

  • text/plain:纯文本格式,不包含任何格式化信息,只包含基本的文本字符
  • text/html:HTML格式的文本,可以用于在Web浏览器中显示Web页面
  • application/json:JSON格式的数据,常用于Web应用程序之间的数据传输
  • application/xml:XML格式的数据,常用于Web服务之间的数据传输
  • image/jpeg:JPEG格式的图像,常用于Web页面中的图像显示
  • image/png:PNG格式的图像,常用于Web页面中的图像显示
  • audio/mpeg:MPEG格式的音频,常用于在线音乐播放等应用场景
  • video/mp4:MP4格式的视频,常用于在线视频播放等应用场景

        当然,这里只是一些常见的媒体类型,实际上还有很多其他的媒体类型。

        要确定文件的媒体类型,可以使用文件的扩展名,文件的扩展名是文件名的最后一部分,通常由点号(.)和几个字符组成,例如,.txt或.jpg等。扩展名是根据文件的类型或格式而分配的。通过查看文件的扩展名,可以大致了解文件的类型。例如,.txt表示文本文件,.jpg表示图像文件,.mp3表示音频文件等等。

        这样就可以重构代码,更新HttpServletResponse根据文件的扩展名找到对应的媒体类型作为Content-Type的值:

if (contentFile.getName().endsWith(".html")){println("Content-Type: text/html; charset=utf-8");
}else if (contentFile.getName().endsWith(".png")){println("Content-Type: image/png");
}

        这样就可以正确显示图片了,这里使用if语句处理媒体类型的方式显然不可取,首先无法处理很多的文件扩展名转化为媒体类型,再有也不方便处理扩展名的大小写问题。Java提供了处理多种媒体类型的API。

4.1.5 Java中识别文件MIME的方法

        在Java中识别文件的MIME类型有以下几种方法:

        1. 使用 java.nio.file.Files.probeContentType(Path path):

        如上述示例代码所示,使用Files.probeContentType(Path path)方法可以根据文件内容探测文件的MIME类型。

        2. 使用 Apache Tika:

        Apache Tika是一个开源的Java库,可以帮助识别文件的MIME类型以及提取文本和元数据。您可以通过添加Apache Tika依赖来使用它。

        3. 使用 Java 7 的 URLConnection:

        Java 7引入了URLConnection类的guessContentTypeFromStream()方法,可以通过输入流来猜测文件的MIME类型。

        4. 使用 Apache Commons IO:

        Apache Commons IO是Apache的一个通用IO类库,它提供了一个FileUtils类,其中包含了一个probeContentType(File file)方法,可以用于获取文件的MIME类型。

4.1.6 使用 Apache Tika识别文件MIME

        在这个案例中,我们将使用 Apache Tika 库来识别文件的 MIME 类型。首先,确保您已将 Apache Tika 添加到您的项目依赖中。

<!-- 导入Tika 解决文件类型识别问题 -->
<dependency><groupId>org.apache.tika</groupId><artifactId>tika-core</artifactId><version>2.8.0</version>
</dependency>

        以下是使用 Apache Tika 的示例代码,演示如何识别文件的 MIME 类型:

import org.apache.tika.Tika;public class FileMimeTypeExample {public static void main(String[] args) {String filePath = "path/to/your/file"; // 替换为实际文件路径Tika tika = new Tika();try {// 获取文件的 MIME 类型String mimeType = tika.detect(filePath);System.out.println("File MIME Type: " + mimeType);} catch (Exception e) {e.printStackTrace();}}
}

        在上述示例代码中,我们首先创建了一个 Tika 实例,并使用 tika.detect(filePath) 方法来获取文件的 MIME 类型。将 "path/to/your/file" 替换为实际文件的路径。

        请确保您的项目中已经包含了 Apache Tika 的相关依赖,否则该代码将无法编译和执行。

        运行示例代码后,您将得到文件的 MIME 类型,例如:"text/plain"、"image/jpeg"、"application/pdf" 等。

        Apache Tika 是一个功能强大的工具,可以处理多种文件类型的内容提取和元数据解析。它适用于需要处理多种文件格式的应用程序。

4.1.7 发送多个响应头

        我们可以使用Tika识别文件的类型,发送文件的Content-Type。然而HTTP的响应头在实际应用中通常会包含很多信息,不仅限于Content-Type和Content-Length。处理多个响应头是Web服务框架中的重要功能,它允许服务器与浏览器之间进行更加灵活和复杂的交互,以满足各种不同的需求。为了处理多个响应头信息,我们可以对HttpServletResponse类进行进一步扩展,使其能够处理更多的响应头。

        我们可以为HttpServletResponse添加了一个HashMap类型的headers字段来存储响应头信息,并添加了setHeader()方法来设置响应头。在send()方法中,我们先发送状态行,然后遍历headers字段,将所有响应头发送到客户端。在发送完所有响应头后,再发送响应正文。

        这样,通过对HttpServletResponse类的扩展,我们能够处理多个响应头,使得Web服务框架能够更好地与客户端进行通信,提供更加丰富和定制化的功能。这有助于实现更高效、灵活和符合实际需求的Web应用程序。

4.1.8 重构HttpServletResponse发送多个响应头

        1. 添加一个headers用于缓存多个响应头信息

        在重构时候,HttpServletResponse中添加一个HashMap类型变量 headers用于缓存任意多的响应头信息,并且在发送响应头时候遍历headers,将全部响应头发送到浏览器。代码示意如下:

    //响应头相关信息, key是响应头名称,value是响应头的值private HashMap<String, String> headers = new HashMap<>();/*** 添加自定义的响应头* @param name 响应头名称* @param value 响应头的值*/public void addHeader(String name, String value){headers.put(name, value);}/*** 发送响应头* @throws IOException 网络故障*/private void sendHeaders() throws IOException{//遍历Header集合,发送每个响应头for (Map.Entry<String, String> entry: headers.entrySet()){String name = entry.getKey();String value = entry.getValue();println(name + ": " + value);System.out.println("发响应头: " + name + ": " + value);}//发送空行println("");}

        2. 添加响应头

        在HttpServletResponse中定义 Tika类型的tika变量,用于查询文件的媒体类型。并且在添加响应文件时候,查询文件的媒体类型,添加响应头信息:

    private Tika tika = new Tika();public void setContentFile(File contentFile) throws IOException {this.contentFile = contentFile;//添加响应头// 使用 Tika 来获取 Content-Type 类型addHeader("Content-Type", tika.detect(contentFile));addHeader("Content-Length", Long.toString(contentFile.length()));addHeader("File-Name", contentFile.getName());}

        3. 定义发送状态行和响应正文的方法

        为了代码更加整洁增加可读性,将发送状态行和发送响应正文代码抽取为方法:

    /*** 发送状态行* @throws IOException 网络故障*/private void sendStatusLine() throws IOException {String statusLine = "HTTP/1.1 " + statusCode + " " + statusReason;//发送状态行println(statusLine);System.out.println("发送状态行: "+statusLine);}/*** 发送响应正文(响应体)* @throws IOException 网络故障*/private void sendContent() throws IOException{//将文件内容发送到浏览器FileInputStream in = new FileInputStream(contentFile);OutputStream out = socket.getOutputStream();byte[] buf = new byte[8*1024];int n;while ((n=in.read(buf))!=-1){out.write(buf, 0, n);}out.flush();System.out.println("发响应正文");}

        4. 重构发送响应的方法

        最终,发送方法重构为:

    /*** 发送HTTP响应消息,发送3个部分内容,状态行、响应头和响应正文* @throws IOException 网络故障*/public void send() throws IOException {//发送状态行sendStatusLine();//发送响应头sendHeaders();//发送响应正文sendContent();}

        HttpServletResponse重构完成后,使其能够处理多种媒体类型,代码更加的清晰:

        其中:

  • Tika类型的tika变量,用于查询文件的媒体类型
  • Socket类型的socket变量,表示客户端与服务端之间的网络连接
  • int类型的statusCode变量和String类型的statusReason变量,用于表示HTTP响应的状态行信息
  • HashMap<String, String>类型的headers变量,用于表示HTTP响应的响应头信息
  • File类型的contentFile变量,用于表示HTTP响应的正文内容

        除此之外,该类还包括一些方法:

  • sendStatusLine():发送HTTP响应的状态行信息
  • sendHeaders():发送HTTP响应的响应头信息
  • sendContent():发送HTTP响应的正文内容
  • send():将上述三个方法组合起来,一次性发送完整的HTTP响应
  • setContentFile(File contentFile):设置HTTP响应的正文内容,并同时设置Content-Length和Content-Type两个响应头信息
  • setStatusCode(int statusCode):设置HTTP响应的状态码
  • setStatusReason(String statusReason):设置HTTP响应的状态描述
  • println(String line):发送一行字符串到网络流中
  • addHeader(String name, String value):添加自定义的响应头信息

        在该类中,通过OutputStream的write()方法将HTTP响应的状态行、响应头和正文内容发送到Socket所表示的网络连接中,从而将HTTP响应返回给客户端。

        代码参考:

/*** 封装HTTP响应逻辑*/
public class HttpServletResponse {// MimetypesFileTypeMap 用于查询文件的媒体类型private MimetypesFileTypeMap fileTypeMap = new MimetypesFileTypeMap();private Socket socket;//状态行相关信息private int statusCode = 200;                   //状态代码private String statusReason = "OK";             //状态描述//响应头相关信息, key是响应头名称,value是响应头的值private HashMap<String, String> headers = new HashMap<>();//响应正文相关信息private File contentFile;                       //响应正文对应的实体文件public HttpServletResponse(Socket socket){this.socket = socket;}/*** 发送状态行* @throws IOException 网络故障*/private void sendStatusLine() throws IOException {String statusLine = "HTTP/1.1 " + statusCode + " " + statusReason;//发送状态行println(statusLine);System.out.println("发送状态行: "+statusLine);}/*** 发送响应头* @throws IOException 网络故障*/private void sendHeaders() throws IOException{//遍历Header集合,发送每个响应头for (Map.Entry<String, String> entry: headers.entrySet()){String name = entry.getKey();String value = entry.getValue();println(name + ": " + value);System.out.println("发响应头: " + name + ": " + value);}//发送空行println("");}/*** 发送响应正文(响应体)* @throws IOException 网络故障*/private void sendContent() throws IOException{//将文件内容发送到浏览器FileInputStream in = new FileInputStream(contentFile);OutputStream out = socket.getOutputStream();byte[] buf = new byte[8*1024];int n;while ((n=in.read(buf))!=-1){out.write(buf, 0, n);}out.flush();System.out.println("发响应正文");}/*** 发送HTTP响应消息,发送3个部分内容,状态行、响应头和响应正文* @throws IOException 网络故障*/public void send() throws IOException {//发送状态行sendStatusLine();//发送响应头sendHeaders();//发送响应正文sendContent();}public void setContentFile(File contentFile){this.contentFile = contentFile;//设置响应头:addHeader("Content-Lentgh", Long.toString(contentFile.length()));addHeader("Content-Type", fileTypeMap.getContentType(contentFile));}/*** 添加自定义的响应头* @param name 响应头名称* @param value 响应头的值*/public void addHeader(String name, String value){headers.put(name, value);}public void setStatusCode(int statusCode) {this.statusCode = statusCode;}public void setStatusReason(String statusReason){this.statusReason = statusReason;}/*** 发送一行到网络流* @param line 一行* @throws IOException 网络故障*/private void println(String line) throws IOException {OutputStream out = socket.getOutputStream();byte[] data = line.getBytes(StandardCharsets.ISO_8859_1);out.write(data);out.write('\r');//发送回车符out.write('\n');//发送换行符}
}

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

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

相关文章

数据库(MySQL)基础:约束

一、概述 1.概念&#xff1a;约束是作用于表中字段上的规则&#xff0c;用于限制存储在表中的数据。 2.目的&#xff1a;保证数据库中数据的正确、有效性和完整性。 3.分类 约束描述关键字非空约束限制该字段的数据不能为nullnot null唯一约束保证该字段的所有数据都是唯一…

Python深度学习基于Tensorflow(2)Tensorflow基础

文章目录 基本操作数据转换和数据生成操作形状数据提取和保存变量Numpy和Tensorflow的比较 计算图静态图动态图自动图 自动微分使用Tensorflow 实现回归 首先是Tensorflow的安装&#xff0c;由于可能会出现版本冲突&#xff0c;最好在conda环境安装&#xff0c;同时&#xff0c…

什么是IT服务台?

IT服务台是组织中的单一联系点&#xff0c;负责解决所有与 IT 相关的问题、查询和请求。IT服务台也称为技术支持、支持中心、信息中心、IT 解决方案中心或技术支持。 IT 服务台的多用途角色可实现多个目标&#xff0c;例如快速解决问题、培养用户满意度、提高组织流程效率以及提…

华为ensp中BFD和OSPF联动(原理及配置命令)

作者主页&#xff1a;点击&#xff01; ENSP专栏&#xff1a;点击&#xff01; 创作时间&#xff1a;2024年5月6日20点26分 BFD通常指的是双向转发检测。BFD是一个旨在快速检测通信链路故障的网络协议&#xff0c;提供了低开销、短延迟的链路故障检测机制。它主要用于监测两个…

【复试分数线】C9历年分数线汇总(第二弹)

今天我将分析C9中主要考信号的5所院校&#xff1a;复旦大学、上海交通大学、南京大学、哈尔滨工业大学、西安交通大学。 这次会为大家整理四电四邮的整理了近三年各院校的复试分数线作为参考&#xff0c;大家可以参考&#xff01; 大多数院校采取的是1.2:1差额的形式复试。举…

egg数据统计之mysql数据库创建视图并可当表使用并查询

打开视图---->新建视图-----> 运行sql ----> 保存 统计地区的愿望数量 sql语句 select user.id AS id,count(userplant.userid) AS amount,user.locationid AS locationid,user->location.name AS locationname from ((userplants userplant left join users us…

macOS12安装 php7.1和apache

1. 安装php 7.1 macOS12不再自带php brew tap shivammathur/php 查看可安装版本 brew search php 安装指定版本&#xff08;禅道适用PHP运行环境(7.0/7.1/7.2版本)&#xff09; brew install php7.1 环境配置 vim ~/.zshrc export PATH"/usr/local/opt/php7.1/bin:…

Rust 使用egui创建一个简单的下载器demo

仓库连接: https://github.com/GaN601/egui-demo-download-util 这是我第一个rust gui demo, 学习rust有挺长时间了, 但是一直没有落实到实践中, 本着对桌面应用的兴趣, 考察了slint、egui两种框架, 最后还是选择了egui. 这篇博客同时包含我当前的一些理解, 但是自身技术有限,…

Java 7大排序

&#x1f435;本篇文章将对数据结构中7大排序的知识进行讲解 一、插入排序 有一组待排序的数据array&#xff0c;以升序为例&#xff0c;从第二个数据开始&#xff08;用tmp表示&#xff09;依次遍历整组数据&#xff0c;每遍历到一个数据都再从tmp的前一个数据开始&#xff0…

LeetCode-2960. 统计已测试设备【数组 模拟】

LeetCode-2960. 统计已测试设备【数组 模拟】 题目描述&#xff1a;解题思路一&#xff1a;模拟解题思路二&#xff1a; 一次遍历&#xff0c;简洁写法解题思路三&#xff1a;0 题目描述&#xff1a; 给你一个长度为 n 、下标从 0 开始的整数数组 batteryPercentages &#xf…

自动驾驶纵向控制算法

本文来源——b站忠厚老实的老王&#xff0c;链接&#xff1a;忠厚老实的老王投稿视频-忠厚老实的老王视频分享-哔哩哔哩视频 (bilibili.com)&#xff0c;侵删。 功率和转速之间的关系就是&#xff1a;功率P等于转矩M乘以转速ω。并不是油门越大加速度就越大。 发动机和电机的转…

AngularJS基本概念

版本&#xff1a; AngularJs 1.x&#xff1a;https://angularjs.org/ AngularJs 2&#xff1a;https://angular.io/ 或 https://angular.cn/ 实现语言&#xff1a; Angular 1.x&#xff1a;使用ES(avaScript)编写&#xff0c;可直接在浏览器中运行。 Angular 2&#xff1a…

【机器学习】AI时代的核心驱动力

机器学习&#xff1a;AI时代的核心驱动力 一、引言二、机器学习的基本原理与应用三、机器学习算法概览四、代码实例&#xff1a;线性回归的Python实现 一、引言 在数字化浪潮席卷全球的今天&#xff0c;人工智能&#xff08;AI&#xff09;已经不再是科幻小说中的遥远概念&…

[muduo网络库]——muduo库三大核心组件之Channel类(剖析muduo网络库核心部分、设计思想)

接着上文[muduo网络库]——muduo库的Reactor模型&#xff08;剖析muduo网络库核心部分、设计思想&#xff09;&#xff0c;接下来详细介绍一下这三大核心组件中的Channel类。 先回顾一下三大核心组件之间的关系。 接着我们进入正题。 Channel Channel类封装了一个 fd 、fd感兴…

【STM32 |程序实测】LED灯闪烁、LED灯流水线、蜂鸣器

LED闪烁&LED流水灯&蜂鸣器的面包板接线图&#xff0c;及对应程序示例 LED闪烁 面包板接线图如下 开启APB2时钟&#xff0c;并且在GPIOA上进行配置&#xff0c;推挽输出&#xff0c;引脚A0&#xff0c;50HZ速度 #include "stm32f10x.h" /…

[Linux][网络][网络层][IP协议]详细讲解

目录 0.基本概念1.IP协议头格式2.IP分片与组装1.为什么要分片&#xff1f;2.分片后谁来组装&#xff1f;3.这个分片操作传输层知道吗&#xff1f;4.如何识别报文和报文的不同&#xff1f;5.接收端&#xff0c;如何得知报文是独立的还是一个分片&#xff1f;6.如何区别哪些分片是…

【论文泛读|附源码】如何进行动力学重构? 神经网络自动编码器结合SINDy发现数据背后蕴含的方程

这一篇文章叫做 数据驱动的坐标发现与方程发现算法。 想回答的问题很简单&#xff0c;“如何根据数据写方程”。 想想牛顿的处境&#xff0c;如何根据各种不同物体下落的数据&#xff0c;写出万有引力的数学公式的。这篇文章就是来做这件事的。当然&#xff0c;这篇论文并没有…

数据结构--图。

在前面&#xff0c;我们学习了线性表和树&#xff0c;而接下来我们要学习的图相较于他们就更加复杂。 目录 一.图的有关概念 一.图的有关概念 1.定义 图(graph)G由两个集合V和E组成&#xff0c;记为G&#xff08;VE)。V是顶点的有穷非空集合;E是边的集合,边是V中顶点的无序对…

【Linux】传输文件,补充:VMware中Linux系统无法连接网络的解决方法

Linux系统可以和其他系统之间进行传输文件&#xff0c;只要通过ssh连接成功以后&#xff0c;就能进行文件传输。 Linux系统也可以通过URL规则和网页之间进行传输文件&#xff08;即上传/下载&#xff09;。 1、Linux系统之间传输文件&#xff1a;scp centos7自带ssh服务&…

FPGA+炬力ARM实现VR视频播放器方案

FPGA炬力ARM方案&#xff0c;单个视频源信号&#xff0c;同时驱动两个LCD屏显示&#xff0c;实现3D 沉浸式播放 客户应用&#xff1a;VR视频播放器 主要功能&#xff1a; 1.支持多种格式视频文件播放 2.支持2D/3D 效果实时切换播放 3.支持TF卡/U盘文件播放 4.支持定制化配置…