第8天 二进制文件上传—原理解析
第8天 二进制文件上传
今天要让post.js插件,解析二进制文件。今天采用的解析方式和昨天完全不同,post.js插件需要重写,当然昨天的基础也是很必要的。
this首先,还是要找到突破口,研究如何让插件支持二进制,并且分析解析原理。然后着手开发一些测试代码,基本的分析工作完成后,再重写post.js插件,最后通过例子进行测试。
yellow今天的内容相对抽象,需要全神贯注和动手实验,一定要动手自己做demo和测试代码。下面就开始今天的开发,希望一切顺利!
二进制解析原理
用二进制格式代替字符串
对二进制进行解析,就不能把上传来的body信息转换为字符串形式处理,而是要用Buffer二进制类型处理。
首先,我们不能用 body_data += chunk 方式合并体数据了,因为合并后是字符串形式,需要采用下面的编码方式代替。
var body_data; var chunk_list = []; req.on("data",function(chunk){ chunk_list.push(chunk); }) req.on("end",function(){ body_data = Buffer.concat(chunk_list); })
通过这种方式,我们得到了二进制Buffer类型对象body_data,Buffer.concat
方法可以合并二进制类型数组,返回一个新的Buffer对象。
先不必弄明白所有代码,跟着往下走就好。现在只要明白 body_data 是二进制对象,我们要对它进行解析,找到它各个部分的信息块。
body_data可以理解为是一个byte字节数组,通过body_data[0]可以得到第一个byte。
为什么?
(小胖问:为什么要这样呢?和之前的合并方式有什么差异呢?)
最关键的差异,是当二进制转换为字符形式处理后,再转回二进制形式,里面的数据结构就变化了。通过下面的代码,让你明白。
var buf = new Buffer([0,1,21,0]); console.log(buf); // <Buffer 00 01 15 00> var buf2 = new Buffer(buf.toString("ascii"),"ascii"); console.log(buf2);// <Buffer 20 01 15 20>
上面的代码的意思是,把二进制对象buf通过转换为ascii编码的字符串,然后再转回来。这时候数据就不一样了。我们发现里面的 00 转换为 20 ,太深的不必解释,至少知道通过二进制转字符串,再转换回二进制格式,数据就会变化!
所以,结论就出来了,昨天的方式只适合于文本文件的上传。
那该如何是好?
(小帅哥说道:字符串不能用了,直接操作二进制?好像很复杂的样子!)
是的,相对之前的编码要复杂一些。不过,弄明白原理后,就没什么神秘之处了。下面,我们分析一下,到底该如何做,从哪里下手。
二进制的读取和分析,可以从头循环读取,每次抓取一个byte,然后根据上下文,来判断读取的是哪个信息块。比如,读取的是边界字符串部分,还是信息头,或者是主体信息。根据这个思考方向,我们可以编写一些代码:(分析要配合编码,这样将很快把分析落实和验证分析,不要只是想,要想一些就要马上做一些,这是最快的设计与开发的方法)
var position = 0; // 读取的自己位置 for(var len = body_data.length;position<len;){ // 循环读body_data字节 var b = body_data[position]; // b 就是字节的 int数据。 }
下一步,我们要配合上下文,对 var b
进行分析。所谓上下文,我们可以理解为:读取状态,也就是眼下读取的是哪部分数据。所以要定义一下编码:
// 读取状态 // 0表示怀疑是边界字符串 // 1表示正在读取头部信息字符串 // 2表示正在读取body体数据 var readState = 0;
上下文状态,也就是当前读取什么,我们分为三种状态。怀疑读取边界,这个状态的产生,是由于读取到了-
这个信息,-
二进制转为int是45
,每当读取到-
就会把readState=0
;1表示读取信息头,这里没有怀疑字样,因为如果读取边界之后,就能确定要读取头部信息了;2表示读取body体信息,这个也不需要怀疑,因为读取完头部信息,就可以确定要读取body体数据了。
回过头来再说说,怀疑读取边界信息。为什么要用怀疑字样呢?这是因为除了第一个边界信息外,我们不知道什么时候读完body体信息,如果读到-
就说明要开始读取边界信息,这个不准确,因为我们无法确定body信息体内是否包括-
。
根据以上的分析,我们可以写出如下的编码:
function handle(b){ // 字节处理器 switch(readState){ case 0: // 处理怀疑是正在读取边界字符串 break case 1: // 读取信息头的实际操作 break; case 2: // 读取信息体 break; } }
handle是个处理器,根据readState的状态处理上下文数据。
var backup = [] 备用数据 和 var body = []
bady表示上传的数据体。backup表示一个储存二进制 int 类型数据的数组,也就是当读取数据时,如果不确定是属于哪部分数据,就先存储在备用数据中。当确定之后直接把backup放入body,并清空backup。
以上就是整个的分析过程,其实看书学习,重点在于训练分析能力,而不在于背诵了多少API,API文档就是用来查看的,所以没必要所谓的精通API。当然精通更好。思路清晰了,编码也就水到渠成,分析的过程就是经验之谈,这个和API不同,每个人分析方式都不同,在这里我也只是抛砖引玉。希望通过这个过程,传授你经验,而不是几个API使用的知识。
在全书中,我也一直在强调经验的传授,分析过程大于记忆的理念。我们要提高的是创造力和逻辑分析能力,记忆力现在确实不是什么强项,因为知识从哪里都可以学到,资讯获得太容易,而分析能力和创造力却是很难量化的,也是很难学到的。
下一节,我们要大幅度修改post.js插件的代码,让它具有处理二进制信息的能力。