nodeformidable詳解網(wǎng)頁(yè)設(shè)計(jì) -電腦資料

電腦資料 時(shí)間:2019-01-01 我要投稿
【clearvueentertainment.com - 電腦資料】

   

前言

    最近在研究nodejs如何實(shí)現(xiàn)文件上傳功能,偶然讀到《nodejs-post文件上傳原理詳解》這篇教程,感覺(jué)非常給力,

nodeformidable詳解網(wǎng)頁(yè)設(shè)計(jì)

。教程中詳細(xì)解讀了post數(shù)據(jù)格式,以及通過(guò)分析node-formidable模塊的源代碼來(lái)解讀文件上傳的原理。

    可是,在源碼分析的過(guò)程中,作者并沒(méi)有解釋得很到位,從而導(dǎo)致在看完整篇教程后某些地方還有些云里霧里,在潛心研究一番后,終于明了,然而這也催生了我寫這篇文章的想法。

    由于該文章是在上文所提教程的基礎(chǔ)之上補(bǔ)充的,因此,建議讀之前先看以上教程。

詳解post數(shù)據(jù)的格式

    使用post提交表單后,傳遞過(guò)來(lái)的數(shù)據(jù)到底是什么樣的呢?

    上篇教程中已經(jīng)講解過(guò),可是,為了使之能更好地與后面的數(shù)據(jù)解析部分的代碼相結(jié)合,我對(duì)Post數(shù)據(jù)進(jìn)行了一個(gè)詳細(xì)的標(biāo)注

HTML

<span></span><span><body></span><span></span><span><form</span><span></span><span>method</span><span>=</span><span>"post"</span><span></span><span>action</span><span>=</span><span>"test.php"</span><span></span><span>enctype</span><span>=</span><span>"multipart/form-data"</span><span>></span><span>What is your name?</span><span><input</span><span></span><span>type</span><span>=</span><span>"text"</span><span></span><span>name</span><span>=</span><span>"submit-name"</span><span></span><span>/><br</span><span></span><span>/></span><span>What files are you sending?</span><span><input</span><span></span><span>type</span><span>=</span><span>"file"</span><span></span><span>name</span><span>=</span><span>"files"</span><span></span><span>multiple</span><span>=</span><span>"multiple"</span><span></span><span>/><br</span><span></span><span>/></span><span></span><span><input</span><span></span><span>type</span><span>=</span><span>"submit"</span><span></span><span>value</span><span>=</span><span>"send"</span><span></span><span>/></span><span></span><span></form></span><span></span><span></body></span>
<span>該</span><span>html</span><span>中有一個(gè)表單,其中包含了一個(gè)</span><span>text</span><span>文本框和一個(gè)</span><span>file</span><span>多文件上傳控件。</span>

POST HEADER

   

    這是Post的header信息,請(qǐng)注意Content-Type后面的boundary數(shù)據(jù),這就是邊界字符串,用于分隔每個(gè)字段數(shù)據(jù)。

POST DATA

   

    上圖便是傳遞過(guò)來(lái)的POST數(shù)據(jù)部分,圖中對(duì)各部分已經(jīng)作了詳細(xì)的表示,每一行其實(shí)隱含著一個(gè)\r\n(CR LF)。 上面提到的教程中其實(shí)已經(jīng)總結(jié)過(guò)POST數(shù)據(jù)的規(guī)則,這里再總結(jié)一次:

每一部分?jǐn)?shù)據(jù)包括字段部分和數(shù)據(jù)部分,比如文本框數(shù)據(jù),字段部分是ContentPosition,數(shù)據(jù)部分是huli;字段部分和數(shù)據(jù)部分使用一個(gè)空行分隔; 每一行后面實(shí)際隱含這一個(gè)\r\n(CR LF)

每一部分?jǐn)?shù)據(jù)以邊界字符串分隔,假設(shè)post_header中定義的邊界為Aa03x,那么一般的邊界字符串為–Aa03x\r\n(程序中稱這樣的邊界字符串為PART_BOUNDARY,表示后面還有數(shù)據(jù)需要解析),最后面的邊界字符串為–Aa03x–(程序中稱這樣的邊界字符串為L(zhǎng)AST_BOUNDARY,表示所有的數(shù)據(jù)都解析完畢)

在每一部分?jǐn)?shù)據(jù)中,數(shù)據(jù)部分(記得前面提到過(guò)的,每一部分?jǐn)?shù)據(jù)包括字段部分和數(shù)據(jù)部分嗎?)是和接下來(lái)的邊界字符串緊挨在一起的!

    更加詳細(xì)的信息可參考W3C文檔的FORMS

POST數(shù)據(jù)解析流程

    模塊中將POST數(shù)據(jù)的解析分成多個(gè)不同的狀態(tài),接著它會(huì)遍歷buffer中每個(gè)字符的狀態(tài),進(jìn)行相應(yīng)的處理。 這些狀態(tài)分為:

    PARSER_UNINITIALIZED:系統(tǒng)尚未初始化

    TART:開始解析

    START_BOUNDARY:開始解析邊界字符串

    HEADER_FIELD_START:解析HEADER_FIELD前的預(yù)處理

    HEADER_FIELD:開始解析HEADER_FIELD

    HEADER_VALUE_START:解析HEADER_VALUE前的預(yù)處理

    HEADER_VALUE:開始解析HEADER_VALUE

    HEADER_VALUE_ALMOST_DONE:header_value部分解析完畢

    HEADERS_ALMOST_DONE:所有的header解析完畢

    PART_DATA_START:解析數(shù)據(jù)段前的預(yù)處理

    PART_DATA:開始解析數(shù)據(jù)段

    ART_END:數(shù)據(jù)段解析完畢

    END:所有的POST_DATA解析完

    結(jié)合POST_DATA中的圖示來(lái)看這些狀態(tài), 注意:狀態(tài)的流轉(zhuǎn)是從上到下的。

源碼詳解

    下面,我會(huì)按照文件上傳的原理把整個(gè)流程重新走一遍,以便大家更加理解。 其中,我會(huì)省略一些源碼中對(duì)本篇文章來(lái)說(shuō)并不太需要的部分,比如容錯(cuò)處理等等。 首要工作是進(jìn)行一些基本模塊的引入

<span>var</span><span>http</span><span>=</span><span>require</span><span>(</span><span>'http'</span><span>),</span><span>fs</span><span>=</span><span>require</span><span>(</span><span>'fs'</span><span>),</span><span>path</span><span>=</span><span>require</span><span>(</span><span>'path'</span><span>),</span><span></span><span>StringDecoder</span><span></span><span>=</span><span></span><span>require</span><span>(</span><span>'string_decoder'</span><span>).</span><span>StringDecoder</span><span>,</span><span></span><span>EventEmitter</span><span></span><span>=</span><span></span><span>require</span><span>(</span><span>'events'</span><span>).</span><span>EventEmitter</span><span>,</span><span>util</span><span>=</span><span>require</span><span>(</span><span>'util'</span><span>);</span>
<span>//首先創(chuàng)建一個(gè)服務(wù)器</span><span>http</span><span>.</span><span>createServer</span><span>(</span><span>function</span><span>(</span><span>request</span><span>,</span><span>response</span><span>){</span><span></span><span>//處理文件上傳:TODO</span><span>}).</span><span>listen</span><span>(</span><span>8080</span><span>);</span><span>//處理文件上傳,首先定義一個(gè)用于文件上傳的類,用于一些初始化設(shè)置</span><span>function</span><span></span><span>IncomingForm</span><span>(){</span><span></span><span>if</span><span></span><span>(!(</span><span>this</span><span></span><span>instanceof</span><span></span><span>IncomingForm</span><span>))</span><span></span><span>{</span><span></span><span>return</span><span></span><span>new</span><span></span><span>IncomingForm</span><span>();</span><span></span><span>}</span><span></span><span>EventEmitter</span><span>.</span><span>call</span><span>(</span><span>this</span><span>);</span><span>//使對(duì)象可以調(diào)用事件函數(shù)</span><span></span><span>this</span><span>.</span><span>maxFieldsSize</span><span>=</span><span>2</span><span>*</span><span>1024</span><span>*</span><span>1024</span><span>;</span><span>//設(shè)定最大文件限制</span><span></span><span>this</span><span>.</span><span>upLoadDir</span><span>=</span><span>'/tmp'</span><span>;</span><span>//設(shè)定上傳文件路徑</span><span></span><span>this</span><span>.</span><span>encoding</span><span>=</span><span>"utf-8"</span><span>;</span><span>//設(shè)定文件的編碼</span><span></span><span>this</span><span>.</span><span>headers</span><span>=</span><span>null</span><span>;</span><span>//post數(shù)據(jù)的頭部信息,可以使用request.headers獲得</span><span></span><span>this</span><span>.</span><span>type</span><span>=</span><span>null</span><span>;</span><span></span><span>this</span><span>.</span><span>bytesReceived</span><span>=</span><span>null</span><span>;</span><span>//收到的字節(jié)數(shù)</span><span></span><span>this</span><span>.</span><span>bytesExpected</span><span>=</span><span>null</span><span>;</span><span>//預(yù)計(jì)的字節(jié)數(shù),一般只有最終收到的字節(jié)數(shù)等于預(yù)計(jì)的字節(jié)數(shù),傳輸才是正確的,否則,傳輸出錯(cuò)</span><span></span><span>this</span><span>.</span><span>_parser</span><span>=</span><span>null</span><span>;</span><span>//用于初始化處理post數(shù)據(jù)的類,等下會(huì)定義,類名為multipart_parser.js</span><span></span><span>this</span><span>.</span><span>filedsSize</span><span>=</span><span>0</span><span>;</span><span>}</span><span>util</span><span>.</span><span>inherits</span><span>(</span><span>IncomingForm</span><span>,</span><span>EventEmitter</span><span>);</span><span>exports</span><span>.</span><span>IncomingForm</span><span>=</span><span>IncomingForm</span><span>;</span><span>//導(dǎo)出</span>
<span>//我想用過(guò)node-formidable的童鞋都應(yīng)該知道parser這個(gè)方法的重要性,沒(méi)錯(cuò),他正是上傳文件模塊的入口</span><span>//它有兩個(gè)參數(shù),第一個(gè)是request對(duì)象(大家都懂的),另一個(gè)就是一個(gè)自定義的回調(diào)函數(shù)cb</span><span>IncomingForm</span><span>.</span><span>prototype</span><span>.</span><span>parser</span><span>=</span><span>function</span><span>(</span><span>req</span><span>,</span><span>cb</span><span>){</span><span></span><span>//定義兩個(gè)函數(shù),用于請(qǐng)求的暫定和繼續(xù)。由于前一篇文章已經(jīng)提到過(guò),node-formidable是采用邊接收邊解析的方式</span><span></span><span>//而接收到的文件數(shù)據(jù)往往先存放到buffer緩沖區(qū),然后再寫入指定路徑的文件之中。。因此,在寫文件的過(guò)程中,最后先</span><span></span><span>//暫定請(qǐng)求(req.pause),等文件寫入完畢,再繼續(xù)請(qǐng)求(req.resume),這是為了避免文件數(shù)據(jù)傳輸速度快于寫入速度而導(dǎo)致</span><span></span><span>//緩沖區(qū)溢出,從而使得數(shù)據(jù)丟失</span><span></span><span>this</span><span>.</span><span>pause</span><span>=</span><span>function</span><span>(){</span><span>req</span><span>.</span><span>pause</span><span>();};</span><span></span><span>this</span><span>.</span><span>resume</span><span>=</span><span>function</span><span>(){</span><span>req</span><span>.</span><span>resume</span><span>();};</span><span></span><span>this</span><span>.</span><span>writeHeaders</span><span>(</span><span>req</span><span>.</span><span>headers</span><span>);</span><span>//存儲(chǔ)Post頭部信息,因?yàn)橐褂闷渲械腸ontent-type字段來(lái)判斷傳輸?shù)氖亲址是文件,稍后會(huì)定義</span><span></span><span>var</span><span></span><span>self</span><span>=</span><span>this</span><span>;</span><span></span><span>//對(duì)這個(gè)我想大家不會(huì)陌生,由于傳輸?shù)臄?shù)據(jù)是分包的,而并非所有的數(shù)據(jù)一齊全部傳過(guò)來(lái),因此,沒(méi)接收到一部分?jǐn)?shù)據(jù),就會(huì)觸發(fā)一次</span><span></span><span>//data事件</span><span>req</span><span>.</span><span>on</span><span>(</span><span>'data'</span><span>,</span><span>function</span><span>(</span><span>buffer</span><span>){</span><span></span><span>self</span><span>.</span><span>write</span><span>(</span><span>buffer</span><span>);</span><span>//用于處理傳過(guò)來(lái)的數(shù)據(jù)</span><span></span><span>});</span><span></span><span>//如果定義了回調(diào)</span><span></span><span>if</span><span></span><span>(</span><span>cb</span><span>)</span><span></span><span>{</span><span></span><span>var</span><span>fields</span><span>={},</span><span>files</span><span>={};</span><span></span><span>//定義一個(gè)field事件,當(dāng)接收到一個(gè)完整字段時(shí)手動(dòng)觸發(fā),該字段是指非文件字段數(shù)據(jù)。</span><span></span><span>//比如一個(gè)文本框中的值</span><span></span><span>this</span><span>.</span><span>on</span><span>(</span><span>'field'</span><span>,</span><span>function</span><span>(</span><span>name</span><span>,</span><span>value</span><span>){</span><span>fields</span><span>[</span><span>name</span><span>]=</span><span>value</span><span>;</span><span></span><span>});</span><span></span><span>//與上面的事件類似,區(qū)別是它只有接受完一個(gè)文件數(shù)據(jù)才手動(dòng)觸發(fā),文件數(shù)據(jù)都是二進(jìn)制數(shù)據(jù),而字段數(shù)據(jù)往往是字符串</span><span></span><span>this</span><span>.</span><span>on</span><span>(</span><span>'file'</span><span>,</span><span>function</span><span>(</span><span>name</span><span>,</span><span>file</span><span>){</span><span>files</span><span>[</span><span>name</span><span>]=</span><span>file</span><span>;</span><span></span><span>});</span><span></span><span>//由事件名我們可以看出來(lái),end事件是在接受完所有的數(shù)據(jù)后觸發(fā)</span><span></span><span>//例:(new IncomingForm()).parse(request,function(error,field,files){</span><span></span><span>//	         fs.renameSync(files.upload.path,/tmp);用于重命名上傳的文件</span><span></span><span>//     })</span><span></span><span>this</span><span>.</span><span>on</span><span>(</span><span>'end'</span><span>,</span><span>function</span><span>(){</span><span>cb</span><span>(</span><span>null</span><span>,</span><span>field</span><span>,</span><span>files</span><span>);</span><span></span><span>})</span><span></span><span>}</span><span>};</span>
<span>//下面就定義剛剛提到的self.write函數(shù),該函數(shù)用于處理傳過(guò)來(lái)的數(shù)據(jù)</span><span>IncomingForm</span><span>.</span><span>prototype</span><span>.</span><span>write</span><span>=</span><span>function</span><span>(</span><span>buffer</span><span>){</span><span></span><span>this</span><span>.</span><span>bytesReceived</span><span>+=</span><span>buffer</span><span>.</span><span>length</span><span>;</span><span></span><span>//由于處理post數(shù)據(jù)是個(gè)非常復(fù)雜的過(guò)程,因此,我們把它放在另個(gè)模塊里來(lái)處理,模塊名為multipart_parser</span><span></span><span>//而這里的this._parser就是new mulipartParser()的一個(gè)實(shí)例化對(duì)象</span><span></span><span>var</span><span>bytesParsed</span><span>=</span><span>this</span><span>.</span><span>_parser</span><span>.</span><span>write</span><span>(</span><span>buffer</span><span>);</span><span>}</span>
<span>//好的,這里先暫定IncomingForm模塊的編寫,讓我們來(lái)探究一下神秘的post數(shù)據(jù)是到底是如何處理的呢</span><span>//multipart_parser.js</span><span>var</span><span>s</span><span>=</span><span>0</span><span>,</span><span>S</span><span>={</span><span></span><span>//定義數(shù)據(jù)解析的各個(gè)階段</span><span>PARSER_UNINITIALIZED</span><span>:</span><span>s</span><span>++,</span><span>START</span><span>:</span><span>s</span><span>++,</span><span>START_BOUNDARY</span><span>:</span><span>s</span><span>++,</span><span>HEADER_FIELD_START</span><span>:</span><span>s</span><span>++,</span><span>HEADER_FIELD</span><span>:</span><span>s</span><span>++,</span><span>HEADER_VALUE_START</span><span>:</span><span>s</span><span>++,</span><span>HEADER_VALUE</span><span>:</span><span>s</span><span>++,</span><span>HEADER_VALUE_ALMOST_DONE</span><span>:</span><span>s</span><span>++,</span><span>HEADERS_ALMOST_DONE</span><span>:</span><span>s</span><span>++,</span><span>PART_DATA_START</span><span>:</span><span>s</span><span>++,</span><span>PART_DATA</span><span>:</span><span>s</span><span>++,</span><span>PART_END</span><span>:</span><span>s</span><span>++,</span><span></span><span>END</span><span>:</span><span>s</span><span>++</span><span></span><span>},</span><span>f</span><span>=</span><span>1</span><span>,</span><span></span><span>//由上圖可知,不同的字段數(shù)據(jù)之間是以邊界字符串分隔開的,</span><span></span><span>//而中間的邊界字符串和最終的邊界字符串不一樣,最終的邊界字符串的最后面多了兩個(gè)'--',因此,</span><span></span><span>//F對(duì)象的兩個(gè)屬性是為了識(shí)別當(dāng)前的邊界是中間的邊界(PART_BOUNDARY)還是最后的邊界(LAST_BOUNDARY)</span><span>F</span><span>={</span><span>PART_BOUNDARY</span><span>:</span><span>f</span><span>,</span><span>//用二進(jìn)制表示為01</span><span>LAST_BOUNDARY</span><span>:</span><span>f</span><span>*=</span><span>2</span><span>//用二進(jìn)制表示為10</span><span></span><span>},</span><span></span><span>//每個(gè)特殊字符對(duì)應(yīng)的ascaii碼</span><span>LF</span><span>=</span><span>10</span><span>,</span><span>CR</span><span>=</span><span>13</span><span>,</span><span>SPACE</span><span>=</span><span>32</span><span>,</span><span>HYPHEN</span><span>=</span><span>45</span><span>,</span><span>//短橫杠'-'</span><span>COLON</span><span>=</span><span>58</span><span>;</span><span>//冒號(hào)':'</span>
<span>//定義的MultipartParser類,用于一些初始化設(shè)置</span><span>function</span><span></span><span>MultipartParser</span><span>(){</span><span></span><span>this</span><span>.</span><span>boundary</span><span>=</span><span>null</span><span>;</span><span>//邊界字符串</span><span></span><span>this</span><span>.</span><span>boundaryChars</span><span>=</span><span>null</span><span>;</span><span>//一個(gè)邊界字符串對(duì),字符串中每個(gè)字母為它的一個(gè)鍵,值都是true</span><span></span><span>this</span><span>.</span><span>lookbehind</span><span>=</span><span>null</span><span>;</span><span>//稍后會(huì)講到,是為了防止錯(cuò)誤地識(shí)別邊界字符串</span><span></span><span>this</span><span>.</span><span>state</span><span>=</span><span>S</span><span>.</span><span>PARSER_UNINITIALIZED</span><span>;</span><span></span><span>this</span><span>.</span><span>index</span><span>=</span><span>null</span><span>;</span><span></span><span>this</span><span>.</span><span>flags</span><span>=</span><span>0</span><span>;</span><span>}</span><span>exports</span><span>.</span><span>MultipartParser</span><span>=</span><span>MultipartParser</span><span>;</span><span>//該函數(shù)用于初始化邊界字符串,一般在剛開始的時(shí)候調(diào)用一次</span><span>MultipartParser</span><span>.</span><span>prototype</span><span>.</span><span>initWithBoundary</span><span>=</span><span>function</span><span>(</span><span>str</span><span>){</span><span></span><span>//一般,真正的邊界字符串會(huì)在boundary字段值后另外加四個(gè)字母'--\r\n'</span><span></span><span>//例如,boundary=AaB03x,那么中間的邊界字符串將會(huì)是--AaBo3x\r\n,結(jié)尾的</span><span></span><span>//邊界字符串是--AaBo3x--,如上圖</span><span></span><span>this</span><span>.</span><span>boundary</span><span>=</span><span>new</span><span></span><span>Buffer</span><span>(</span><span>str</span><span>.</span><span>length</span><span>+</span><span>4</span><span>);</span><span></span><span>//可以看到,代碼中把boundary寫成\r\n--str,與事實(shí)--str\r\n不相符,這是出于什么考慮呢,在write函數(shù)中會(huì)有介紹</span><span></span><span>this</span><span>.</span><span>boundary</span><span>.</span><span>write</span><span>(</span><span>'\r\n--'</span><span>,</span><span>'ascaii'</span><span>,</span><span>0</span><span>);</span><span></span><span>this</span><span>.</span><span>boundary</span><span>.</span><span>write</span><span>(</span><span>str</span><span>,</span><span></span><span>'ascii'</span><span>,</span><span></span><span>4</span><span>);</span><span></span><span>this</span><span>.</span><span>lookbehind</span><span>=</span><span>new</span><span></span><span>Buffer</span><span>(</span><span>this</span><span>.</span><span>boundary</span><span>.</span><span>length</span><span>+</span><span>8</span><span>);</span><span></span><span>//初始化為邊界字符串后,buffer的解析狀態(tài)流轉(zhuǎn)為START</span><span></span><span>this</span><span>.</span><span>state</span><span>=</span><span>S</span><span>.</span><span>START</span><span>;</span><span>}</span>
<span>//這是今天的主角,也是最重要的處理邏輯,還記得IncomingForm模塊中德self._write(buffer)嗎,沒(méi)錯(cuò)</span><span>//它實(shí)際調(diào)用的正是這個(gè)函數(shù)。</span><span>MultipartParser</span><span>.</span><span>prototype</span><span>.</span><span>write</span><span>=</span><span>function</span><span>(</span><span>buffer</span><span>){</span><span></span><span>//首先進(jìn)行一些初始化設(shè)置,定義一些局部變量</span><span></span><span>var</span><span></span><span>self</span><span>=</span><span>this</span><span>;</span><span>len</span><span>=</span><span>buffer</span><span>.</span><span>length</span><span>,</span><span>prevIndex</span><span>=</span><span>this</span><span>.</span><span>index</span><span>,</span><span>index</span><span>=</span><span>this</span><span>.</span><span>index</span><span>,</span><span>state</span><span>=</span><span>this</span><span>.</span><span>state</span><span>,</span><span>flags</span><span>=</span><span>this</span><span>.</span><span>flags</span><span>,</span><span>lookbehind</span><span>=</span><span>this</span><span>.</span><span>lookbehind</span><span>,</span><span>boundary</span><span>=</span><span>this</span><span>.</span><span>boundary</span><span>,</span><span>boundaryChars</span><span>=</span><span>this</span><span>.</span><span>boundaryChars</span><span>,</span><span>boundaryLength</span><span>=</span><span>this</span><span>.</span><span>boundaryLength</span><span>,</span><span></span><span>//剛開始一直不明白為什么定義一個(gè)boundaryEnd,不過(guò)看到后面的代碼,再自己琢磨一會(huì),終于弄明白鳥</span><span>boundaryEnd</span><span>=</span><span>boundaryLength</span><span>-</span><span>1</span><span>,</span><span>bufferLength</span><span>=</span><span>buffer</span><span>.</span><span>length</span><span>,</span><span>c</span><span>,</span><span></span><span>//這個(gè)函數(shù)在上面提到的文章中也有講過(guò),實(shí)際上就是作一個(gè)起點(diǎn)標(biāo)識(shí)的。。</span><span></span><span>//比如,我現(xiàn)在字段數(shù)據(jù)(headerField)讀取完畢,要開始讀字段值(headerValue)數(shù)據(jù),那么我便使用一個(gè)mark('headerValue')來(lái)作一個(gè)</span><span></span><span>//字段值數(shù)據(jù)的起點(diǎn)標(biāo)識(shí),存儲(chǔ)我是從哪一個(gè)字符開始讀取字段值的</span><span>mark</span><span>=</span><span>function</span><span>(</span><span>name</span><span>){</span><span></span><span>self</span><span>[</span><span>name</span><span>+</span><span>'Mark'</span><span>]=</span><span>i</span><span>;</span><span></span><span>},</span><span></span><span>//顧名思義,該函數(shù)的作用是為了刪除起點(diǎn)標(biāo)識(shí)的,那何時(shí)該刪除標(biāo)識(shí)呢?</span><span></span><span>//當(dāng)解析的數(shù)據(jù)部分的末尾在buffer內(nèi)部時(shí),這個(gè)時(shí)候,在讀取完該部分?jǐn)?shù)據(jù)后,需將起點(diǎn)標(biāo)識(shí)刪除</span><span></span><span>//例如我現(xiàn)在讀取的是字段數(shù)據(jù)部分,headerField,如果headerField部分在當(dāng)前的buffer內(nèi)部結(jié)束時(shí),就應(yīng)該</span><span></span><span>//刪除該headerField的起點(diǎn)標(biāo)識(shí)</span><span>clear</span><span>=</span><span>function</span><span>(</span><span>name</span><span>){</span><span></span><span>delete</span><span></span><span>self</span><span>[</span><span>name</span><span>+</span><span>'Mark'</span><span>];</span><span></span><span>},</span><span></span><span>//每解析完一段數(shù)據(jù)(我說(shuō)的一段數(shù)據(jù)指的是一段有意義的數(shù)據(jù),例如headerField,headerValue,partData都算是一段數(shù)據(jù))</span><span></span><span>//就要調(diào)用相應(yīng)的回調(diào)進(jìn)行處理,該函數(shù)會(huì)傳遞四個(gè)參數(shù),第一個(gè)參數(shù)為該段數(shù)據(jù)名(headerField等),第二個(gè)參數(shù)為當(dāng)前的buffer,</span><span></span><span>//最后兩個(gè)參數(shù)分別為該數(shù)據(jù)段的起始位置和結(jié)束位置。。這里又要分為兩種情況:</span><span></span><span>//1.該段數(shù)據(jù)在當(dāng)前的buffer內(nèi)結(jié)束,那么start=mark(name),end=i;</span><span></span><span>//2.當(dāng)前的buffer內(nèi),解析的該段數(shù)據(jù)尚未解析完畢,例如當(dāng)前是個(gè)headerField字段數(shù)據(jù),它有一部分在當(dāng)前的buffer內(nèi),剩余的部分在</span><span></span><span>//  下一個(gè)buffer內(nèi),這時(shí)候,start=mark(name),end=buffer.length</span><span>callback</span><span>=</span><span>function</span><span>(</span><span>name</span><span>,</span><span>buffer</span><span>,</span><span>start</span><span>,</span><span>end</span><span>){</span><span></span><span>if</span><span>(</span><span>start</span><span>!=</span><span>undefined</span><span></span><span>&&</span><span>start</span><span>===</span><span>end</span><span>){</span><span></span><span>return</span><span>;</span><span></span><span>}</span><span></span><span>//這是形成回調(diào)函數(shù)名</span><span></span><span>var</span><span>callbackSymbol</span><span>=</span><span></span><span>'on'</span><span>+</span><span>name</span><span>.</span><span>substr</span><span>(</span><span>0</span><span>,</span><span></span><span>1</span><span>).</span><span>toUpperCase</span><span>()+</span><span>name</span><span>.</span><span>substr</span><span>(</span><span>1</span><span>);</span><span></span><span>if</span><span></span><span>(</span><span>callbackSymbol</span><span>in</span><span></span><span>self</span><span>)</span><span></span><span>{</span><span></span><span>//這些回調(diào)函數(shù)是在哪定義的呢?別急,等我們?cè)俅位氐絀ncomingForm中時(shí)你就會(huì)明白了</span><span></span><span>self</span><span>[</span><span>callbackSymbol</span><span>](</span><span>buffer</span><span>,</span><span>start</span><span>,</span><span></span><span>end</span><span>);</span><span></span><span>}</span><span></span><span>},</span><span></span><span>//這是對(duì)callback的一個(gè)封裝,目的是為了給callback傳遞start和end</span><span></span><span>//這個(gè)clear代表什么呢?</span><span></span><span>//沒(méi)錯(cuò),它代表我在講callback時(shí)提到的兩種情況,可以看下源碼對(duì)照一下這兩種情況。</span><span>dataCallback</span><span>=</span><span></span><span>function</span><span>(</span><span>name</span><span>,</span><span>clear</span><span>)</span><span></span><span>{</span><span></span><span>var</span><span>markSymbol</span><span>=</span><span>name</span><span>+</span><span>'Mark'</span><span>;</span><span></span><span>if</span><span></span><span>(!(</span><span>markSymbol</span><span>in</span><span></span><span>self</span><span>))</span><span></span><span>{</span><span></span><span>return</span><span>;</span><span></span><span>}</span><span></span><span>if</span><span></span><span>(!</span><span>clear</span><span>)</span><span></span><span>{</span><span>callback</span><span>(</span><span>name</span><span>,</span><span>buffer</span><span>,</span><span></span><span>self</span><span>[</span><span>markSymbol</span><span>],</span><span>buffer</span><span>.</span><span>length</span><span>);</span><span></span><span>self</span><span>[</span><span>markSymbol</span><span>]</span><span></span><span>=</span><span></span><span>0</span><span>;</span><span></span><span>}</span><span></span><span>else</span><span></span><span>{</span><span>callback</span><span>(</span><span>name</span><span>,</span><span>buffer</span><span>,</span><span></span><span>self</span><span>[</span><span>markSymbol</span><span>],</span><span>i</span><span>);</span><span></span><span>delete</span><span></span><span>self</span><span>[</span><span>markSymbol</span><span>];</span><span></span><span>}</span><span></span><span>};</span><span></span><span>//該進(jìn)入主題部分了,在當(dāng)前buffer內(nèi),逐個(gè)字符地判斷,然后根據(jù)每個(gè)字符地當(dāng)前狀態(tài)</span><span></span><span>//進(jìn)行相應(yīng)地操作。這里提到的每個(gè)字符的狀態(tài)是指上就是指當(dāng)前字符出于哪一個(gè)數(shù)據(jù)段中</span><span></span><span>for</span><span>(</span><span>i</span><span>=</span><span>0</span><span>;</span><span>i</span><span><</span><span>len</span><span>;</span><span>i</span><span>++){</span><span>c</span><span>=</span><span>buffer</span><span>[</span><span>i</span><span>];</span><span>//當(dāng)前字符</span><span></span><span>switch</span><span>(</span><span>state</span><span>){</span><span></span><span>case</span><span>S</span><span>.</span><span>PARSER_UNINITIALIZED</span><span>:</span><span></span><span>return</span><span>i</span><span>;</span><span></span><span>//注意,該case并沒(méi)有break語(yǔ)句,說(shuō)明可以直接執(zhí)行下一個(gè)case,在該case中,只是起到一個(gè)初始化作用,不涉及到字符的處理</span><span></span><span>case</span><span>S</span><span>.</span><span>START</span><span>:</span><span>//數(shù)據(jù)開始進(jìn)入解析階段,一般是在初始化完邊界字符串后,可以看initWithBoundary代碼</span><span>index</span><span>=</span><span>0</span><span>;</span><span>state</span><span>=</span><span>S</span><span>.</span><span>START_BOUNDARY</span><span>;</span><span>//狀態(tài)流轉(zhuǎn),開始解析解析數(shù)據(jù)區(qū)域的邊界字符串</span><span></span><span>case</span><span>S</span><span>.</span><span>START_BOUNDARY</span><span>:</span><span></span><span>if</span><span>(</span><span>index</span><span>==</span><span>boundary</span><span>.</span><span>length</span><span>-</span><span>2</span><span>){</span><span></span><span>if</span><span></span><span>(</span><span>c</span><span>!=</span><span>CR</span><span>)</span><span>//邊界的倒數(shù)第二個(gè)字符串應(yīng)該為CR(最后兩個(gè)字符為\r\n),如果不是,則說(shuō)明解析出錯(cuò)</span><span></span><span>{</span><span></span><span>return</span><span>i</span><span>;</span><span></span><span>}</span><span>index</span><span>++;</span><span>//若正確,則繼續(xù)解析下一個(gè)字符</span><span></span><span>break</span><span>;</span><span></span><span>}</span><span>else</span><span></span><span>if</span><span>(</span><span>index</span><span>-</span><span>1</span><span>==</span><span>boundary</span><span>.</span><span>length</span><span>-</span><span>2</span><span>){</span><span></span><span>if</span><span>(</span><span>c</span><span>!=</span><span>LF</span><span>){</span><span>//如果邊界的最后一個(gè)字符部位LF,則說(shuō)明解析出錯(cuò)</span><span></span><span>return</span><span>i</span><span>;</span><span></span><span>}</span><span></span><span>//否則,說(shuō)明邊界字符串解析完畢,開始解析下面的字段數(shù)據(jù)</span><span>index</span><span>=</span><span>0</span><span>;</span><span></span><span>//觸發(fā)onPartBegin事件(在IncomingForm中定義),目的是為了初始化一些變量,方便接下來(lái)的解析過(guò)程</span><span></span><span>//可以看到,它沒(méi)有傳遞buffer,start,end,因此不涉及傳輸數(shù)據(jù)的處理</span><span>callback</span><span>(</span><span>'partBegin'</span><span>);</span><span>state</span><span>=</span><span>S</span><span>.</span><span>HEADER_FIELD_START</span><span>;</span><span>//狀態(tài)流轉(zhuǎn)</span><span></span><span>break</span><span>;</span><span></span><span>}</span><span></span><span>//為什么index要加2呢,還記得initWithBounday中如何初始化邊界字符串嗎?</span><span></span><span>//沒(méi)錯(cuò),它解析成\r\n--boundary,與實(shí)際的字符串--boundary\r\n對(duì)比一下,</span><span></span><span>//明白為什么要加上2了吧,是為了跳過(guò)CR和LF</span><span></span><span>if</span><span></span><span>(</span><span>c</span><span>!=</span><span>boundary</span><span>[</span><span>index</span><span>+</span><span>2</span><span>])</span><span></span><span>{</span><span></span><span>return</span><span>i</span><span>;</span><span></span><span>}</span><span>index</span><span>++;</span><span></span><span>break</span><span>;</span><span></span><span>//可以看到,該case同樣沒(méi)有break,因此,可以看出,凡是START區(qū)域都是</span><span></span><span>//起到一個(gè)起始點(diǎn)標(biāo)識(shí)和狀態(tài)流轉(zhuǎn)的作用</span><span></span><span>case</span><span>S</span><span>.</span><span>HEADER_FIELD_START</span><span>:</span><span>state</span><span>=</span><span>S</span><span>.</span><span>HEADER_FIELD</span><span>;</span><span>//狀態(tài)流轉(zhuǎn)</span><span>mark</span><span>(</span><span>'headerField'</span><span>);</span><span>//標(biāo)識(shí)字段數(shù)據(jù)的起始點(diǎn)</span><span>index</span><span>=</span><span>0</span><span>;</span><span></span><span>case</span><span>S</span><span>.</span><span>HEADER_FIELD</span><span>:</span><span></span><span>//這里的CR指的是所有的HEADER結(jié)束后于DATA區(qū)域之間的那一個(gè)空行</span><span></span><span>//因?yàn)橛行⿺?shù)據(jù)有兩個(gè)header,比如文件數(shù)據(jù),它不僅有Content-Disposition</span><span></span><span>//還有Content-Type,如上圖?赡苓@里還不太明白,等到下面的S.HEADER_VALUE_ALMOST_DONE狀態(tài)</span><span></span><span>//再回過(guò)頭來(lái)看這一段代碼</span><span></span><span>if</span><span>(</span><span>c</span><span>==</span><span>CR</span><span>){</span><span>clear</span><span>(</span><span>'headerField'</span><span>);</span><span>state</span><span>=</span><span>S</span><span>.</span><span>HEADERS_ALMOST_DONE</span><span>;</span><span></span><span>break</span><span>;</span><span></span><span>}</span><span>index</span><span>++;</span><span></span><span>//如果字符是'-',例如Content-Diposition:中間的'-',則繼續(xù)檢測(cè)下一個(gè)字符的狀態(tài)</span><span></span><span>if</span><span>(</span><span>c</span><span>==</span><span>HYPHTN</span><span>){</span><span></span><span>break</span><span>;</span><span></span><span>}</span><span></span><span>//如果字符是':'則表明header_field部分結(jié)束,可看圖示</span><span></span><span>if</span><span>(</span><span>c</span><span>==</span><span>COLON</span><span>){</span><span></span><span>if</span><span>(</span><span>index</span><span>==</span><span>1</span><span>){</span><span>//是個(gè)空字段</span><span></span><span>return</span><span>i</span><span>;</span><span></span><span>}</span><span></span><span>//header_field部分結(jié)束,調(diào)用相關(guān)的回調(diào)處理該部分?jǐn)?shù)據(jù),</span><span></span><span>//注意,它是在同一個(gè)buffer內(nèi)部結(jié)束的,所有第二個(gè)參數(shù)為true</span><span>dataCallback</span><span>(</span><span>'headerField'</span><span>,</span><span>true</span><span>);</span><span>state</span><span>=</span><span>S</span><span>.</span><span>HEADER_VALUE_START</span><span>;</span><span>//狀態(tài)流轉(zhuǎn),開始處理field_value的數(shù)據(jù)</span><span></span><span>break</span><span>;</span><span></span><span>}</span><span></span><span>break</span><span>;</span><span></span><span>//不用解釋了吧,呵呵</span><span></span><span>case</span><span>S</span><span>.</span><span>HEADER_VALUE_START</span><span>:</span><span>mark</span><span>(</span><span>'headerValue'</span><span>);</span><span>state</span><span>=</span><span>S</span><span>.</span><span>HEADER_VALUE</span><span>;</span><span></span><span>case</span><span>S</span><span>.</span><span>HEADER_VALUE</span><span>:</span><span></span><span>//如果當(dāng)前字符是CR,則HEADER_VALUE解析完畢,還記得每一行的最后是\r\n(CR LF)嗎?</span><span></span><span>if</span><span>(</span><span>c</span><span>==</span><span>CR</span><span>){</span><span>dataCallback</span><span>(</span><span>'headerValue'</span><span>,</span><span></span><span>true</span><span>);</span><span>callback</span><span>(</span><span>'headerEnd'</span><span>);</span><span>//改行header結(jié)束后進(jìn)行回調(diào)</span><span>state</span><span>=</span><span>S</span><span>.</span><span>HEADER_VALUE_ALMOST_DONE</span><span>;</span><span></span><span>}</span><span></span><span>break</span><span>;</span><span></span><span>case</span><span>S</span><span>.</span><span>HEADER_VALUE_ALMOST_DONE</span><span>:</span><span></span><span>//由于上一狀態(tài)最后的字符是CR,所以當(dāng)前字符應(yīng)該是LF,否則就是解析出錯(cuò)</span><span></span><span>if</span><span>(</span><span>c</span><span>!=</span><span>LF</span><span>){</span><span></span><span>return</span><span>i</span><span>;</span><span></span><span>}</span><span></span><span>//為什么這里又要重新回滾狀態(tài)呢?</span><span></span><span>//前面已經(jīng)提到過(guò),有的數(shù)據(jù)段可能有兩行header,比如文件和圖片,

電腦資料

nodeformidable詳解網(wǎng)頁(yè)設(shè)計(jì)》(http://clearvueentertainment.com)。。</span><span></span><span>//現(xiàn)在再講前面HEADER_FIELD中的解釋聯(lián)系起來(lái),是不是懂了呢</span><span>state</span><span>=</span><span>S</span><span>.</span><span>HEADER_FIELD_START</span><span>;</span><span></span><span>break</span><span>;</span><span></span><span>//這是在某個(gè)數(shù)據(jù)段的所有Header全部解析完畢時(shí)的狀態(tài)</span><span></span><span>case</span><span>S</span><span>.</span><span>HEADERS_ALMOST_DONE</span><span>:</span><span></span><span>//回過(guò)頭去看header_field狀態(tài),在header都解析完成時(shí)的字符是CR,所以當(dāng)前字符應(yīng)該是LF</span><span></span><span>//否則就是解析錯(cuò)誤</span><span></span><span>if</span><span>(</span><span>c</span><span>!=</span><span>LF</span><span>){</span><span></span><span>return</span><span>i</span><span>;</span><span></span><span>}</span><span>callback</span><span>(</span><span>'headersEnd'</span><span>);</span><span>state</span><span>=</span><span>S</span><span>.</span><span>PART_DATA_START</span><span>;</span><span></span><span>break</span><span>;</span><span></span><span>case</span><span>S</span><span>.</span><span>PART_DATA_START</span><span>:</span><span>state</span><span>=</span><span>S</span><span>.</span><span>PART_DATA</span><span>;</span><span>mark</span><span>(</span><span>'partData'</span><span>);</span><span></span><span>//數(shù)據(jù)段的解析,這是最難懂的一部分,當(dāng)初也是看了很久</span><span></span><span>case</span><span>S</span><span>.</span><span>PART_DATA</span><span>:</span><span>prevIndex</span><span>=</span><span>index</span><span>;</span><span></span><span>//index上一次的設(shè)置是在header_field_start中,設(shè)置為0</span><span></span><span>if</span><span>(</span><span>index</span><span>==</span><span>0</span><span>){</span><span></span><span>//這一段代碼有些難懂,它的目的是盡快地跳過(guò)數(shù)據(jù)區(qū)域。因?yàn)閿?shù)據(jù)區(qū)域一般</span><span></span><span>//有很多地字符,而我們所需要的僅僅是該數(shù)據(jù)區(qū)域的起始位置和結(jié)束位置。</span><span></span><span>//起始位置我們?cè)赑ART_DATA_START中已經(jīng)設(shè)置了。該如何找到數(shù)據(jù)區(qū)域的結(jié)束位置呢?</span><span></span><span>//如果像前面的headerField,headerValue一樣,逐個(gè)字符進(jìn)行遍歷,那太影響效率,因此</span><span></span><span>//這里采用邊界跳躍法使我們能更快地接近數(shù)據(jù)區(qū)域的結(jié)束位置。。</span><span></span><span>//如何跳躍呢?</span><span></span><span>//此時(shí)i指向的是數(shù)據(jù)其余的第一個(gè)字符,而數(shù)據(jù)區(qū)域和下一個(gè)字段其余的邊界是連在一起的。</span><span></span><span>//這時(shí)候,我們用i加上一個(gè)boundaryEnd(它比真實(shí)的邊界少一個(gè)字符)可以上我們往前跳一部分,</span><span></span><span>//但是它又同時(shí)保證了我們不會(huì)跳出邊界之外,就算數(shù)據(jù)區(qū)域只有一個(gè)字符,我加上一個(gè)boundaryEnd,</span><span></span><span>//也只會(huì)跳到邊界字符串的最尾部。大家可以按照post數(shù)據(jù)格式,假設(shè)一段數(shù)據(jù)來(lái)照著流程走一遍</span><span>i</span><span>+=</span><span>boundaryEnd</span><span>;</span><span></span><span>while</span><span>(</span><span>i</span><span><</span><span>bufferLength</span><span>&&</span><span></span><span>!(</span><span>buffer</span><span>[</span><span>i</span><span>]</span><span></span><span>in</span><span>boundaryChars</span><span>)){</span><span>i</span><span>+=</span><span>boundaryLength</span><span>;</span><span></span><span>}</span><span>i</span><span>-=</span><span>boundaryEnd</span><span>;</span><span>c</span><span>=</span><span>buffer</span><span>[</span><span>i</span><span>];</span><span></span><span>}</span><span></span><span>if</span><span>(</span><span>index</span><span><</span><span>boundary</span><span>.</span><span>length</span><span>){</span><span></span><span>if</span><span>(</span><span>boundary</span><span>[</span><span>index</span><span>]==</span><span>c</span><span>){</span><span></span><span>//表示數(shù)據(jù)區(qū)域解析完畢,這里同時(shí)也說(shuō)明了作者當(dāng)時(shí)在初始化邊界時(shí)為什么要把</span><span></span><span>//\r\n放在首部,在數(shù)據(jù)區(qū)域結(jié)尾的部分正好是\r\n,因此,如果兩者相等,則正好</span><span></span><span>//說(shuō)明了當(dāng)前指針走到了數(shù)據(jù)區(qū)域的尾部</span><span></span><span>if</span><span>(</span><span>index</span><span>==</span><span>0</span><span>){</span><span>dataCallback</span><span>(</span><span>'partData'</span><span>,</span><span>true</span><span>);</span><span></span><span>}</span><span></span><span>//這里開始解析下一個(gè)boundary,也就是緊接著data區(qū)域之后的boundary,可以邊看代碼邊看</span><span></span><span>//圖示</span><span>index</span><span>++;</span><span></span><span>}</span><span></span><span>else</span><span>{</span><span>index</span><span>=</span><span>0</span><span>;</span><span></span><span>}</span><span></span><span>}</span><span>else</span><span></span><span>if</span><span>(</span><span>index</span><span>==</span><span>boundary</span><span>.</span><span>length</span><span>){</span><span></span><span>//當(dāng)解析到邊界字符串的倒數(shù)第二個(gè)字符</span><span></span><span>//注意,因?yàn)閕ndex是從1開始的,所以當(dāng)它等于boundary.length時(shí)</span><span></span><span>//應(yīng)該是倒數(shù)第二個(gè)字符</span><span>index</span><span>++;</span><span></span><span>if</span><span>(</span><span>c</span><span>==</span><span>CR</span><span>){</span><span></span><span>//如果倒數(shù)第二個(gè)字符為CR,表名下面還有數(shù)據(jù)需要解析</span><span></span><span>//前面已經(jīng)解釋過(guò),邊界字符串有兩種情況,可以看上面的圖示</span><span>flags</span><span>|=</span><span>F</span><span>.</span><span>PART_BOUNDARY</span><span>;</span><span>//flag為F.PART_BOUNDARY</span><span></span><span>}</span><span>else</span><span></span><span>if</span><span>(</span><span>c</span><span>==</span><span>HYPHEN</span><span>){</span><span></span><span>//如果倒數(shù)第二個(gè)字符為'-',表明所有的post數(shù)據(jù)解析完畢</span><span></span><span>}</span><span>else</span><span>{</span><span>index</span><span>=</span><span>0</span><span>;</span><span></span><span>}</span><span>else</span><span></span><span>if</span><span>(</span><span>index</span><span>-</span><span>1</span><span>==</span><span>boundary</span><span>.</span><span>length</span><span>){</span><span></span><span>//如果當(dāng)前的邊界字符串為PART_BOUNDARY</span><span></span><span>if</span><span>(</span><span>flags</span><span>&</span><span>F</span><span>.</span><span>PART_BOUNDARY</span><span>){</span><span>index</span><span>=</span><span>0</span><span>;</span><span></span><span>if</span><span>(</span><span>c</span><span>==</span><span>LF</span><span>){</span><span></span><span>//因?yàn)橄旅孢有數(shù)據(jù)需要解析</span><span></span><span>//所以重新初始化flags的值</span><span>flags</span><span>&=</span><span></span><span>~</span><span>F</span><span>.</span><span>PART_BOUNDARY</span><span>;</span><span>callback</span><span>(</span><span>'partEnd'</span><span>);</span><span>callback</span><span>(</span><span>'partBegin'</span><span>);</span><span></span><span>//狀態(tài)重新流轉(zhuǎn)到header_field_start進(jìn)行下一個(gè)字段的解析</span><span>state</span><span>=</span><span>S</span><span>.</span><span>HEADER_FIELD_START</span><span>;</span><span></span><span>break</span><span>;</span><span></span><span>}</span><span></span><span>}</span><span></span><span>else</span><span></span><span>if</span><span>(</span><span>flags</span><span>&</span><span>F</span><span>.</span><span>LAST_BOUNDARY</span><span>){</span><span></span><span>//如果當(dāng)前邊界字符串為L(zhǎng)AST_BOUNDARY</span><span></span><span>if</span><span>(</span><span>c</span><span>==</span><span>HYPHEN</span><span>){</span><span>callback</span><span>(</span><span>'partEnd'</span><span>);</span><span>callback</span><span>(</span><span>'end'</span><span>)</span><span></span><span>//狀態(tài)流轉(zhuǎn),解析結(jié)束</span><span>state</span><span>=</span><span>S</span><span>.</span><span>END</span><span>;</span><span></span><span>}</span><span>else</span><span>{</span><span>index</span><span>=</span><span>0</span><span>;</span><span></span><span>}</span><span></span><span>}</span><span></span><span>else</span><span>{</span><span>index</span><span>=</span><span>0</span><span>;</span><span></span><span>}</span><span></span><span>}</span><span></span><span>if</span><span>(</span><span>index</span><span>></span><span>0</span><span>){</span><span></span><span>//這里是在干嗎呢?</span><span></span><span>//實(shí)際上它就是個(gè)日志功能,在解析遇到錯(cuò)誤的時(shí)候,可以通過(guò)它來(lái)回滾錯(cuò)誤。。</span><span></span><span>//例如,在跳躍邊界那一部分,當(dāng)數(shù)據(jù)區(qū)域中有一段字符和邊界字符串中的某一段字符</span><span></span><span>//相同時(shí),就會(huì)錯(cuò)誤地把它認(rèn)為是邊界字符串,等到解析到后來(lái)發(fā)現(xiàn)了錯(cuò)誤時(shí)怎么辦呢?</span><span></span><span>//就是用這里保存的數(shù)據(jù)來(lái)進(jìn)行回滾操作</span><span>lookbehind</span><span>[</span><span>index</span><span>-</span><span>1</span><span>]=</span><span>c</span><span>}</span><span>else</span><span></span><span>if</span><span>(</span><span>prevIndex</span><span>></span><span>0</span><span>){</span><span></span><span>//可以看前面part_data部分的代碼,在發(fā)現(xiàn)解析錯(cuò)誤時(shí),總是把index置0,</span><span></span><span>//如果這時(shí)候prevIndex不為0,那么說(shuō)明有數(shù)據(jù)需要回滾</span><span>callback</span><span>(</span><span>'partData'</span><span>,</span><span>lookbehind</span><span>,</span><span></span><span>0</span><span>,</span><span>prevIndex</span><span>);</span><span>prevIndex</span><span>=</span><span></span><span>0</span><span>;</span><span>mark</span><span>(</span><span>'partData'</span><span>);</span><span>i</span><span>--;</span><span></span><span>}</span><span></span><span>break</span><span>;</span><span></span><span>case</span><span>S</span><span>.</span><span>END</span><span>:</span><span></span><span>break</span><span>;</span><span></span><span>default</span><span></span><span>return</span><span>i</span><span>;</span><span></span><span>}</span><span></span><span>}</span><span></span><span>//這里是在當(dāng)前buffer完全解析完后調(diào)用的</span><span></span><span>//如果一個(gè)數(shù)據(jù)部分(比如如field信息)已經(jīng)在上面的解析過(guò)程中解析完畢,那么自然已經(jīng)調(diào)用過(guò)clear方法,那下面的dataCallback將什么也不做</span><span></span><span>//否則,下面的調(diào)用將會(huì)把這次數(shù)據(jù)段中的數(shù)據(jù)部分傳遞到回調(diào)函數(shù)中</span><span></span><span>//看看上面提到的調(diào)用dataCallback的兩種情況</span><span>dataCallback</span><span>(</span><span>'headerField'</span><span>);</span><span>dataCallback</span><span>(</span><span>'headerValue'</span><span>);</span><span>dataCallback</span><span>(</span><span>'partData'</span><span>);</span><span></span><span>this</span><span>.</span><span>index</span><span>=</span><span>index</span><span>;</span><span></span><span>this</span><span>.</span><span>state</span><span>=</span><span>state</span><span>;</span><span></span><span>this</span><span>.</span><span>flags</span><span>=</span><span>flags</span><span>;</span><span></span><span>return</span><span>len</span><span>;</span><span></span><span>}</span><span>}</span>
<span>到這里,已經(jīng)完成了整個(gè)數(shù)據(jù)解析部分的流程,如果大家有些地方還沒(méi)弄明白,可以使用實(shí)例數(shù)據(jù)照著流程多走幾次</span><span>這里,還有一個(gè)問(wèn)題是,在解析的過(guò)程中,觸發(fā)的這些回調(diào)都是在哪里定義的呢?</span><span>讓我么能再回到</span><span>IncomingForm</span><span>模塊中</span><span>想一想,該何時(shí)定義這些回調(diào)。。。在</span><span>post</span><span>數(shù)據(jù)傳過(guò)來(lái)時(shí),會(huì)先傳遞頭部信息,就是那些鍵值對(duì),然后才是實(shí)際的數(shù)據(jù)部分(數(shù)據(jù)部分中又分</span><span>header</span><span>和</span><span>data</span><span>,上面的流程就是解析這兩塊)</span><span>因此,我們可以在處理頭部信息時(shí)來(lái)定義這些回調(diào)函數(shù).</span><span>還記得</span><span>parser</span><span>中的</span><span>this</span><span>.</span><span>writeHeaders</span><span>(</span><span>req</span><span>.</span><span>headers</span><span>)這一句嗎,不錯(cuò),這正是處理</span><span>Header</span><span>信息的入口</span>
<span>IncomingForm</span><span>.</span><span>prototype</span><span>.</span><span>writeHeaders</span><span>=</span><span>function</span><span>(</span><span>headers</span><span>){</span><span></span><span>this</span><span>.</span><span>headers</span><span>=</span><span>headers</span><span>;</span><span></span><span>//調(diào)用一個(gè)私有方法</span><span></span><span>this</span><span>.</span><span>_parseContentType</span><span>();</span><span>}</span><span>IncomingForm</span><span>.</span><span>prototype</span><span>.</span><span>_parseContentType</span><span>=</span><span></span><span>function</span><span>(){</span><span></span><span>//headers中的content-type有application/x-www-form-urlencoded和multipart/form-data兩種</span><span></span><span>//由于這里專注講文件上傳,所以我們只講multipart/form-data部分</span><span></span><span>if</span><span>(</span><span>this</span><span>.</span><span>headers</span><span>[</span><span>'content-type'</span><span>].</span><span>match</span><span>(</span><span>/multipart/</span><span>i</span><span>)){</span><span></span><span>var</span><span>m</span><span>;</span><span></span><span>if</span><span>(</span><span>m</span><span>=</span><span></span><span>this</span><span>.</span><span>headers</span><span>[</span><span>'content-type'</span><span>].</span><span>match</span><span>(</span><span>/boundary=(?:"([^"]+)"|([^;]+))/</span><span>i</span><span>)){</span><span></span><span>//獲得boundary字段的指,也就是邊界字符串</span><span></span><span>this</span><span>.</span><span>_initMultipart</span><span>(</span><span>m</span><span>[</span><span>1</span><span>]</span><span></span><span>||</span><span>m</span><span>[</span><span>2</span><span>]);</span><span></span><span>}</span><span></span><span>return</span><span></span><span>}</span><span>}</span><span>IncomingForm</span><span>.</span><span>prototype</span><span>.</span><span>_initMultipart</span><span>(</span><span>boundary</span><span>){</span><span></span><span>this</span><span>.</span><span>type</span><span>=</span><span></span><span>'multipart'</span><span>;</span><span></span><span>//初始化一個(gè)MultipartParser的實(shí)例,用于在后面定義multipart_parser中執(zhí)行的回調(diào)</span><span></span><span>var</span><span>parser</span><span>=</span><span></span><span>new</span><span></span><span>MultipartParser</span><span>(),</span><span></span><span>self</span><span></span><span>=</span><span></span><span>this</span><span>,</span><span>headerField</span><span>,</span><span>headerValue</span><span>,</span><span>part</span><span>;</span><span></span><span>//初始化邊界字符串,initWithBoundary是在multipart_parser中定義的</span><span>parser</span><span>.</span><span>initWithBoundary</span><span>(</span><span>boundary</span><span>);</span><span></span><span>//該函數(shù)是在狀態(tài)流轉(zhuǎn)到S.HEADER_FIELD_START時(shí)執(zhí)行。</span><span>parser</span><span>.</span><span>onPartBegin</span><span>=</span><span></span><span>function</span><span>()</span><span></span><span>{</span><span>part</span><span>=</span><span></span><span>new</span><span></span><span>EventEmitter</span><span>();</span><span>part</span><span>.</span><span>headers</span><span>=</span><span></span><span>{};</span><span>part</span><span>.</span><span>name</span><span>=</span><span></span><span>null</span><span>;</span><span>part</span><span>.</span><span>filename</span><span>=</span><span></span><span>null</span><span>;</span><span>part</span><span>.</span><span>mime</span><span>=</span><span></span><span>null</span><span>;</span><span>headerField</span><span>=</span><span></span><span>''</span><span>;</span><span>headerValue</span><span>=</span><span></span><span>''</span><span>;</span><span></span><span>};</span><span></span><span>//header_field解析完畢時(shí)執(zhí)行</span><span>parser</span><span>.</span><span>onHeaderField</span><span>=</span><span></span><span>function</span><span>(</span><span>b</span><span>,</span><span>start</span><span>,</span><span></span><span>end</span><span>)</span><span></span><span>{</span><span>headerField</span><span>+=</span><span>b</span><span>.</span><span>toString</span><span>(</span><span>self</span><span>.</span><span>encoding</span><span>,</span><span>start</span><span>,</span><span></span><span>end</span><span>);</span><span></span><span>};</span><span></span><span>//header_value解析完畢時(shí)執(zhí)行</span><span>parser</span><span>.</span><span>onHeaderValue</span><span>=</span><span></span><span>function</span><span>(</span><span>b</span><span>,</span><span>start</span><span>,</span><span></span><span>end</span><span>)</span><span></span><span>{</span><span>headerValue</span><span>+=</span><span>b</span><span>.</span><span>toString</span><span>(</span><span>self</span><span>.</span><span>encoding</span><span>,</span><span>start</span><span>,</span><span></span><span>end</span><span>);</span><span></span><span>};</span><span></span><span>//headr_value解析完畢時(shí)執(zhí)行,用于存儲(chǔ)該行header中的一些信息</span><span></span><span>//例如name,filename,filetype等等</span><span>parser</span><span>.</span><span>onHeaderEnd</span><span>=</span><span></span><span>function</span><span>()</span><span></span><span>{</span><span>headerField</span><span>=</span><span>headerField</span><span>.</span><span>toLowerCase</span><span>();</span><span>part</span><span>.</span><span>headers</span><span>[</span><span>headerField</span><span>]</span><span></span><span>=</span><span>headerValue</span><span>;</span><span></span><span>var</span><span>m</span><span>;</span><span></span><span>if</span><span></span><span>(</span><span>headerField</span><span>==</span><span></span><span>'content-disposition'</span><span>)</span><span></span><span>{</span><span></span><span>if</span><span></span><span>(</span><span>m</span><span>=</span><span>headerValue</span><span>.</span><span>match</span><span>(</span><span>/name="([^"]+)"/</span><span>i</span><span>))</span><span></span><span>{</span><span>part</span><span>.</span><span>name</span><span>=</span><span>m</span><span>[</span><span>1</span><span>];</span><span></span><span>}</span><span>part</span><span>.</span><span>filename</span><span>=</span><span></span><span>self</span><span>.</span><span>_fileName</span><span>(</span><span>headerValue</span><span>);</span><span></span><span>}</span><span></span><span>else</span><span></span><span>if</span><span></span><span>(</span><span>headerField</span><span>==</span><span></span><span>'content-type'</span><span>)</span><span></span><span>{</span><span>part</span><span>.</span><span>mime</span><span>=</span><span>headerValue</span><span>;</span><span></span><span>}</span><span>headerField</span><span>=</span><span></span><span>''</span><span>;</span><span>headerValue</span><span>=</span><span></span><span>''</span><span>;</span><span></span><span>};</span><span></span><span>//所有的header解析完畢時(shí)執(zhí)行</span><span>parser</span><span>.</span><span>onHeadersEnd</span><span>=</span><span></span><span>function</span><span>()</span><span></span><span>{</span><span></span><span>self</span><span>.</span><span>onPart</span><span>(</span><span>part</span><span>);</span><span></span><span>};</span><span></span><span>//當(dāng)找到data的結(jié)束位置時(shí)執(zhí)行</span><span>parser</span><span>.</span><span>onPartData</span><span>=</span><span></span><span>function</span><span>(</span><span>b</span><span>,</span><span>start</span><span>,</span><span></span><span>end</span><span>)</span><span></span><span>{</span><span></span><span>//觸發(fā)data事件,該事件是在哪偵聽的呢,稍后會(huì)說(shuō)明</span><span>part</span><span>.</span><span>emit</span><span>(</span><span>'data'</span><span>,</span><span>b</span><span>.</span><span>slice</span><span>(</span><span>start</span><span>,</span><span></span><span>end</span><span>));</span><span></span><span>};</span><span></span><span>//當(dāng)解析完緊接data區(qū)域之后的邊界字符串時(shí)執(zhí)行</span><span>parser</span><span>.</span><span>onPartEnd</span><span>=</span><span></span><span>function</span><span>()</span><span></span><span>{</span><span></span><span>//同理,觸發(fā)end事件</span><span>part</span><span>.</span><span>emit</span><span>(</span><span>'end'</span><span>);</span><span></span><span>};</span><span></span><span>//當(dāng)解析完last_boundary時(shí)執(zhí)行</span><span>parser</span><span>.</span><span>onEnd</span><span>=</span><span></span><span>function</span><span>()</span><span></span><span>{</span><span></span><span>self</span><span>.</span><span>ended</span><span>=</span><span></span><span>true</span><span>;</span><span></span><span>self</span><span>.</span><span>_maybeEnd</span><span>();</span><span></span><span>};</span><span></span><span>this</span><span>.</span><span>_parser</span><span>=</span><span>parser</span><span>;</span><span>}</span>
<span>到此,已經(jīng)定義了所有需要的回調(diào)函數(shù),可是上面代碼中提到的</span><span>data</span><span>和</span><span>end</span><span>事件是在哪定義的呢?</span><span>讓我們按照剛才需找</span><span>"回調(diào)函數(shù)定義之處"</span><span>的方式思維來(lái)思考,由于在</span><span>onPartData</span><span>和</span><span>onPartEnd</span><span>函數(shù)執(zhí)行之前會(huì)先執(zhí)行</span><span>onHeadersEnd</span><span>,</span><span>而</span><span>onHeadersEnd</span><span>中只有一條語(yǔ)句,</span><span>self</span><span>.</span><span>onPart</span><span>(</span><span>part</span><span>),不難看到,這兩個(gè)事件應(yīng)該是在</span><span>onPart</span><span>中定義的。。</span>
<span>IncomingForm</span><span>.</span><span>prototype</span><span>.</span><span>onPart</span><span>=</span><span>function</span><span>(</span><span>part</span><span>){</span><span></span><span>//真糾結(jié)啊</span><span></span><span>this</span><span>.</span><span>handlePart</span><span>(</span><span>part</span><span>);</span><span>}</span><span>//這個(gè)函數(shù)很重要,目的是處理header_field和header_value,以及</span><span>//定義上文提到的事件</span><span>IncomingForm</span><span>.</span><span>prototype</span><span>.</span><span>handlePart</span><span>=</span><span></span><span>function</span><span>(</span><span>part</span><span>){</span><span></span><span>var</span><span></span><span>self</span><span>=</span><span>this</span><span>;</span><span></span><span>//如果當(dāng)前的數(shù)據(jù)部分不是文件</span><span></span><span>if</span><span>(</span><span>part</span><span>.</span><span>filename</span><span>==</span><span>undefined</span><span>){</span><span></span><span>//這里可以看源代碼,不講解了</span><span></span><span>return</span><span>;</span><span></span><span>}</span><span></span><span>//這里的File是另一個(gè)模塊,用于寫文件,很簡(jiǎn)單,</span><span></span><span>//這里就不講了,可以看一下源碼,我想應(yīng)該都能看懂</span><span></span><span>var</span><span>file</span><span>=</span><span>new</span><span></span><span>File</span><span>({</span><span>path</span><span>:</span><span>'tmp/'</span><span>+</span><span>path</span><span>.</span><span>filename</span><span>,</span><span>name</span><span>:</span><span>part</span><span>.</span><span>filename</span><span>,</span><span>type</span><span>:</span><span>part</span><span>.</span><span>mime</span><span>,</span><span></span><span>});</span><span>file</span><span>.</span><span>open</span><span>();</span><span></span><span>//這里就是剛才提到的data事件,當(dāng)文件數(shù)據(jù)解析完畢后,會(huì)觸發(fā)這個(gè)事件</span><span>part</span><span>.</span><span>on</span><span>(</span><span>'data'</span><span>,</span><span></span><span>function</span><span>(</span><span>buffer</span><span>)</span><span></span><span>{</span><span></span><span>//文件寫入過(guò)程中,暫定request請(qǐng)求,寫入完畢后,回復(fù)請(qǐng)求</span><span></span><span>//原因前面已經(jīng)將結(jié)果</span><span></span><span>self</span><span>.</span><span>pause</span><span>();</span><span>file</span><span>.</span><span>write</span><span>(</span><span>buffer</span><span>,</span><span></span><span>function</span><span>()</span><span></span><span>{</span><span></span><span>self</span><span>.</span><span>resume</span><span>();</span><span></span><span>});</span><span></span><span>//end事件</span><span>part</span><span>.</span><span>on</span><span>(</span><span>'end'</span><span>,</span><span></span><span>function</span><span>()</span><span></span><span>{</span><span>file</span><span>.</span><span>end</span><span>(</span><span>function</span><span>()</span><span></span><span>{</span><span></span><span>self</span><span>.</span><span>emit</span><span>(</span><span>'file'</span><span>,</span><span>part</span><span>.</span><span>name</span><span>,</span><span>file</span><span>);</span><span></span><span>self</span><span>.</span><span>_maybeEnd</span><span>();</span><span></span><span>});</span><span></span><span>});</span><span>});</span><span>}</span>

最新文章