1 前语

最近在开发中遇到文件上传采用Base64的方法上传,记住以前刚开始学http上传文件的时候,都是经过content-type为multipart/form-data方法直接上传二进制文件,咱们知道都经过网络传输终究只能传输二进制流,所以毫无疑问他们本质上都是相同的,那么为什么还要先转成Base64呢?这两种方法有什么区别?带着这样的疑问咱们一起来剖析下。

2 multipart/form-data上传

先来看看multipart/form-data的方法,我在本地经过一个简略的比如来检查http multipart/form-data方法的文件上传,html代码如下

<!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>

页面展示也比较简略

再学http-为什么文件上传要转成Base64?

挑选文件点击上传后,经过edge浏览器f12进入调试形式检查到的恳求信息。
恳求头如下

再学http-为什么文件上传要转成Base64?
在恳求头里Content-Type 为 multipart/form-data; boundary=—-WebKitFormBoundary4TaNXEII3UbH8VKo,刚开始看必定有点懵,不过其实也不杂乱,能够简略理解为在恳求体里要传递的参数被分为多部份,每一部分经过分化符boundary分割,就比方在这个比如,表单里有file和remark两个字段,则在恳求体里就被分为两部分,每一部分经过boundary=—-WebKitFormBoundary4TaNXEII3UbH8VKo来分隔(实际上还要加上CRLF回车换行符,回车表明将光标移动到当前行的开头,换行表明一行文本的结束,也便是新文本行的开始)。需求注意下当最后一部分结尾时需求加多两个”-“结尾。
咱们持续来看恳求体

再学http-为什么文件上传要转成Base64?
榜首部分是file字段部分,它的Content-Type为image/png,第二部分为remark字段部分,它没有声明Content-Type,则默以为text/plain纯文本类型,也便是在比如中输入的“测验”,到这儿大家必定会有个疑问,上传的图片是放在哪里的,这儿怎样没看到呢?别急,我猜想是浏览器做了特别处理,恳求体里不显现二进制流,咱们经过Filder抓包东西来验证下。

再学http-为什么文件上传要转成Base64?
能够看到在榜首部分有一串乱码显现,这是由于图片是二进制文件,显现成文本格局自然就乱码了,这也证实了二进制文件也是放在恳求体里。后端运用框架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个字节。

再学http-为什么文件上传要转成Base64?

表3.1

转成二级制后每6位一组对应不同颜色,每6位前面弥补两个0组成一个字节,终究Base64编码字符是QUIKQw==,Base64编码表大家能够自行网上查找检查。

再学http-为什么文件上传要转成Base64?
咱们经过运转程序来验证下

再学http-为什么文件上传要转成Base64?
终究得出的成果与咱们上面推理的相同。

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编码的状况也就比较多。