这一次,弄明白JS中的文件相关(二):HTTP请求头和响应头

(一)前置知识

开始前,我们先来复习一下HTTP的基础知识。

HTTP请求分为:请求行、请求头、空行、请求体(也叫正文、请求实体、请求主体)。

HTTP响应分为:状态行(也叫响应行)、响应头、空行、响应体(也叫正文、响应实体、响应主体)。

HTTP请求中,最常见的GET请求是没有请求体的(GET的查询字符串不属于请求体),只有POSTPUT请求有请求体。

HTTP响应中,大部分时候都会有响应体,什么情况会没有呢?

状态码为1xx204 No Content301302重定向、304 Not Modified的响应。还有一个,就是HEAD方法的响应,HEADGET的区别,是只返回头部信息,不返回实体数据。

我们今天要讨论的HTTP中文件相关的字段,就是指的请求体和响应体(统称为实体)相关的字段。

 

(二)请求体和响应体相关的字段

1. 数据类型:实体的MIME类型。

Accept:请求头字段,标记客户端可理解的MIME类型,写法如:Accept: text/html,application/xml,image/webp

Content-Type:通用字段,请求头和响应头都可以用,只要有实体数据,就应该设置正确的Content-Type,没有的话就不需要。Content-Type的数据格式是:数据类型+字符集,比如:application/json;charset=UTF-8。作为请求头时,一般不需要后面的字符集。作为响应头时,当响应体为文本类型时,应该指定字符集。

2. 编码类型:实体的压缩格式,比如gzip, deflate, br。服务器可以通过对数据进行压缩,来减小响应数据的大小。

Accept-Encoding:请求头字段,标记客户端支持的压缩格式,写法如:Accept-Encoding: gzip, deflate, br

Conent-Encoding:响应头字段,响应体的实际压缩格式,写法如:Content-Encoding: gzip

3. 语言类型,指的是实体的自然语言,比如汉语、英语等。

Accept-Language:请求头字段,标记客户端可理解的自然语言,写法如:Accept-Language: zh-CN, zh, en

Content-Language:响应头字段,响应体的实际自然语言,写法如:Content-Language: zh-CN

4. 字符集,指的是实体的字符集。

Accept-Charset:请求头字段,写法如:Accept-Charset: gbk, utf-8

这儿要注意,没有对应的响应头字段,响应体的数据类型和字符集,都在Content-Type响应头中。

5. 资源处理方式

Content-Disposition:响应头字段,指示客户端应以何种方式处理响应体数据,是显示(inline)还是下载附件(attachment),写法如:Content-Disposition: attachment; filename="example.pdf"

6. 数据长度

Content-Length:通用字段。不管对于客户端还是服务端,知道明确的Content-Length,都是有好处的。响应头中的Content-Length更为常见,因为它可以帮助客户端优化加载速度、判断响应是否完整以及何时可以安全关闭连接。

7. 分块传输和范围请求

Transfer-EncodingRangeAccept-RangesContent-Range这些暂不讨论。

有的字段,我们在平时的开发中几乎没用过,感知不到它们的存在,原因很可能是:浏览器和现代框架,会帮助我们自动处理。

 

 

我们来重点说一下:Content-Type

(三)Content-Type请求头:

Content-Type请求头最常用的三个值是:application/x-www-form-urlencodedmultipart/form-dataapplication/json。还有一些其他值,比如text/plaintext/xmlapplication/octet-stream等,一般是在发送特定数据格式时使用,用的不多。

application/x-www-form-urlencodedmultipart/form-data这两位起源于HTML表单提交,后来在ajax请求中继续沿用。application/json则是从ajax时代开始流行。所以早期的ajax库如jquery,默认的post数据格式是application/x-www-form-urlencoded,而现代ajax库如axios,默认的数据格式就变成了application/json

1.application/x-www-form-urlencoded

这是form表单的默认post提交方式,将字段名和值进行URL编码,得到这种格式:a=1&b=2&c=3。你一定感觉很熟悉,没错,和url的查询字符串长一个样。这种提交方式适用于简单的文本数据,同样也是jquery.ajax的默认post请求方式。

2.multipart/form-data

当需要上传文件时,不管是通过表单提交,还是各种ajax框架,都需要使用这种提交方式。因为这种格式能够携带二进制数据,比如文件。

3.application/json

现代ajax框架,普遍默认采用这种post方式,比如axios,它post请求的默认数据格式就是这个。

 

(四)Content-Type响应头:

相比请求头主要是3个值,Content-Type响应头的值就丰富多了,浏览器根据响应体的数据类型,来做出不同的处理。

比如最常见的网页是text/htmlapi接口数据是application/json,图片是image/jpeg, image/png, image/gifcss文件是text/css等。

这里面比较特别的就是application/octet-stream,意思是不明类型的二进制数据,浏览器对它的处理方式一般是直接下载。

nodejs中,如果使用原生的http模块,你需要手动设置Content-Type。但如果使用web框架,比如Expresskoa2,如果你不设置,它们会根据内容自动推断并设置一个合适的Content-Type。在nginxHTTP服务器中,也是会自动添加静态文件的Content-Type,它们内部通常有一张mime类型和文件扩展名的映射表。

 

(五)文件下载

我们来专门说一下文件下载:

我们工作中大多数HTTP请求,浏览器对于文件的处理方式,都是打开或直接使用,而不是下载。前端和后端,分别可以通过各自的方式来让浏览器下载文件。

1. 前端使用<a href="文件URL" download="自定义文件名">下载文件</a>,只要给a标签添加download属性,就无需管响应头的Conent-Type是什么,都可以点击下载文件,还可以指定下载的文件名。

2. 后端把Content-Type响应头设为:application/octet-stream。这种方式,实际上是把文件标记为不明类型的二进制数据,浏览器通常采用下载的方式处理这种数据。

3. 后端设置正确的Content-Type响应头,然后设置Content-Disposition: attachment; filename="example.pdf"

Content-Disposition是专门指示浏览器如何处理响应体的内容,被浏览器显示(inline)还是作为附件下载(attachment)。可以看出,Content-Disposition是专门处理文件下载的,这才是服务端指定文件下载的最佳实践。

 

(六)文件上传

再来说一下文件上传,在web开发中,文件上传经历了从传统表单提交到使用AJAX技术的演变。

表单时代,我们通过<form method="post" enctype="multipart/form-data">来上传文件。

在前端,出于安全考虑,JS没有文件系统的权限,所以我们无法通过编程的方式创建文件,<input type="file" />是唯一的文件获取方式。

我们选中的文件,在JS中就是File对象。文件上传,就是把File对象通过表单提交或ajax发送给后端。

那么ajax时代,我们已经很少使用表单了,上传文件应该怎么写呢?

先考一下你,File对象可以作为一个普通对象的属性值吗?

答案是:不可以。因为对象的属性和值都必须是可序列化的数据,比如简单类型或对象,而FileBlob不是简单可序列化的数据。也就是说,JSON和键值对都不支持二进制数据,只有multipart/form-data这种数据格式支持。

我曾经以为,可以通过这种把File挂在普通对象的方式,作为post的请求的参数,来上传文件。由于以上的原因,这样是行不通的。

现在我们一般很少用表单了,如何存放二进制数据呢?此时我们需要一个新的数据结构:FormData

FormDataHTML5引入的一个API,它允许我们将表单数据以键值对的形式组织起来,并且可以方便地添加文件内容,它可以模拟表单提交。具体这么写:

// XHR上传文件
// 获取文件输入元素
var fileInput = document.getElementById('fileInput');
var file = fileInput.files[0];

// 创建XMLHttpRequest实例
var xhr = new XMLHttpRequest();
xhr.open('POST', '/api/upload', true);
xhr.onload = function () {
  if (xhr.status === 200) {
    console.log('文件上传成功:', xhr.responseText);
  } else {
    console.error('文件上传失败:', xhr.statusText);
  }
};

// 设置请求头,对于文件上传,通常不需要手动设置Content-Type
// 因为在使用FormData时,浏览器会自动设置
// xhr.setRequestHeader('Content-Type', 'multipart/form-data');

// 创建FormData对象
var formData = new FormData();
formData.append('file', file);

// 发送请求
xhr.send(formData);
// axios上传文件
// 获取文件输入元素
var fileInput = document.getElementById('fileInput');
var file = fileInput.files[0];

// 创建FormData对象
var formData = new FormData();
formData.append('file', file);

// 使用axios上传文件
axios.post('/api/upload', formData, {
  // axios会自动处理FormData的Content-Type
  // 无需手动设置
})
.then(response => {
  console.log('文件上传成功:', response.data);
})
.catch(error => {
  console.error('文件上传失败:', error);
});

 

以上是对HTTP请求头和响应头中实体字段的总结,重点说明了Content-Type以及文件下载和上传。

posted @ 2024-01-23 18:03  路泽宇  阅读(815)  评论(0编辑  收藏  举报