Vuelva a aprender http: ¿por qué es necesario convertir las cargas de archivos a Base64?

1. Introducción

Recientemente, durante el desarrollo, encontré cargas de archivos usando Base 64. Recuerdo que cuando comencé a aprender cómo cargar archivos a través de http, cargué archivos binarios directamente a través de tipo de contenido como multipart/form-data. transmitido a través de la red Solo se pueden transmitir transmisiones binarias, por lo que no hay duda de que son esencialmente lo mismo, entonces, ¿por qué convertir primero a Base64? ¿Cuál es la diferencia entre estas dos formas? Con tales preguntas, analicemos juntos.

2 carga de datos de varias partes/formulario

Echemos un vistazo al método multipart/form-data primero. Utilizo un ejemplo simple localmente para ver las cargas de archivos en el método http multipart/form-data. El código html es el siguiente

<!DOCTYPE html>
<html>
<head>
    <title>上传文件示例</title>
    <meta charset="UTF-8">
<body>
<h1>上传文件示例</h1>
<form action="/upload" method="POST" enctype="multipart/form-data">
    <label for="file">选择文件:</label>
    <input type="file" id="file" name="file"><br>
    <label for="tx">说明:</label>
    <input type="text" id="tx" name="remark"><br><br>
    <input type="submit" value="上传">
</form>
</body>
</html>

La visualización de la página también es relativamente simple.

image.png

Después de seleccionar el archivo y hacer clic en cargar, ingrese al modo de depuración a través del navegador edge f12 para ver la información de la solicitud.
El encabezado de la solicitud es el siguiente. image.pngEn el encabezado de la solicitud, el tipo de contenido es multipart/form-data; border=----WebKitFormBoundary4TaNXEII3UbH8VKo. Debe ser un poco confuso al principio, pero no es complicado. Puede ser simplemente entendido como los parámetros que se pasan en el cuerpo de la solicitud Se divide en varias partes, y cada parte se divide por el límite del descomponedor. Por ejemplo, en este ejemplo, hay dos campos archivo y comentario en el formulario, y se divide en dos partes en el cuerpo de la solicitud, y cada parte se divide por border=-- --WebKitFormBoundary4TaNXEII3UbH8VKo para separar (en realidad agregue CRLF salto de línea de retorno de carro, retorno de carro significa mover el cursor al comienzo de la línea actual, significa salto de línea el final de una línea de texto, es decir, el comienzo de una nueva línea de texto). Cabe señalar que se deben agregar dos terminaciones "-" al final de la última parte.
Sigamos mirando el cuerpo de la solicitud.

image.png 第一部分是file字段部分,它的Content-Type为image/png,第二部分为remark字段部分,它没有声明Content-Type,则默认为text/plain纯文本类型,也就是在例子中输入的“测试”,到这里大家肯定会有个疑问,上传的图片是放在哪里的,这里怎么没看到呢?别急,我猜测是浏览器做了特殊处理,请求体里不显示二进制流,我们通过Filder抓包工具来验证下。

image.png 可以看到在第一部分有一串乱码显示,这是因为图片是二进制文件,显示成文本格式自然就乱码了,这也证实了二进制文件也是放在请求体里。后端使用框架springboot通过MultipartFile接受文件也是解析请求体的每一部分最终拿到二进制流。

@RestController
public class FileController {
    // @RequestParam可接收Content-Type 类型为:multipart/form-data 
    // 或 application/x-www-form-urlencoded 请求体的内容
    @PostMapping("/upload")
    public String upload(@RequestParam("file") MultipartFile file) {
        return "test";
    }
}

到此multipart/form-data方式上传文件就分析完了,关于multipart/form-data官方说明可参考 RFC 7578 - Returning Values from Forms: multipart/form-data (ietf.org)

3 Base64上传

在http的请求方式中,文件上传只能通过multipart/form-data的方式上传,这样一来就会有比较大的限制,那有没其他方式可以突破这一限制,也就是说我可以通过其他的请求方式上传,比如application/json?当然有,把文件当成一个字符串,和其他普通参数没什么两样,我们可以通过其他任意请求方式上传。如果转成了字符串,那上传文件就比较简单了,但问题是我们怎么把二进制流转成字符串,因为这里面可能会有很多“坑”,业界一般的做法是通过Base64编码把二进制流转成字符串,那为什么不直接转成字符串而要先通过Base64来转呢?我们下面来分析下。

3.1 Base64编码原理

在分析原理之前,我们先来回答什么是Base64编码?首先我们要知道Base64只是一种编码方式,并不是加解密算法,因此Base64可以编码,那也可以解码,它只是按照某种编码规则把一些不可显示字符转成可显示字符。这种规则的原理是把要编码字符的二进制数每6位分为一组,每一组二进制数可对应Base64编码的可打印字符,因为一个字符要用一个字节显示,那么每一组6位Base64编码都要在前面补充两个0,因此总长度比编码前多了(2/6) = 1/3,因为6和8最小公倍数是24,所以要编码成Base64对字节数的要求是3的倍数(24/8=3字节),对于不足字节的需要在后面补充字节数,补充多少个字节就用多少个"="表示(一个或两个),这么说有点抽象,我们通过下面的例子来说明。
我们对ASCII码字符串"AB\nC"(\n和LF都代表换行)进行Base64编码,因为一共4字节,为了满足是3的倍数需要扩展到6个字节,后面补充了2个字节。

image.png

表3.1

转成二级制后每6位一组对应不同颜色,每6位前面补充两个0组成一个字节,最终Base64编码字符是QUIKQw==,Base64编码表大家可以自行网上搜索查看。

image.png 我们通过运行程序来验证下

image.png 最终得出的结果与我们上面推理的一样。

3.2 Base64编码的作用

在聊完原理之后,我们继续来探讨文件上传为什么要先通过Base64编码转成字符串而不直接转成字符串?一些系统对特殊的字符可能存在限制或者说会被当做特殊含义来处理,直接转成普通字符串可能会失真,因此上传文件要先转成Base64编码字符,不能把二进制流直接字符串。

另外,相比较multipart/form-data Base64编码文件上传比较灵活,它不受请求类型的限制,可以是任何请求类型,因为最终就是一串字符串,相当于请求的一个参数字段,它不像二进制流只能限定multipart/form-data的请求方式,日常开发中,我们用的比较多的是通过apllication/json的格式把文件字段放到请求体,这种方式提供了比较便利的可操作性。

4 总结

本文最后再来总结对比下这两种文件上传的方式优缺点。
(1)multipart/form-data可以传输二进制流,效率较高,Base64需要编码解码,会耗费一定的性能,效率较低。
(2)Base64不受请求方式的限制,灵活度高,http文件二进制流方式传输只能通过multipart/form-data的方式,灵活度低。
因为随着机器性能的提升,小文件通过二进制流传输和字符串传输,我们对这两种方式时间延迟的感知差异并不那么明显,因此大部分情况下我们更多考虑的是灵活性,所以采用Base64编码的情况也就比较多。

Acho que você gosta

Origin juejin.im/post/7251131990438264889
Recomendado
Clasificación