<output id="os3gq"><ruby id="os3gq"></ruby></output>

    1. <mark id="os3gq"></mark>
    2. Access培訓
      網站公告
      ·Access快速平臺QQ群號:84825014    ·Access快速開發平臺下載地址及教程    ·歡迎添加微信交流賬號:AccessoftChu    ·如何快速搜索本站文章|示例|資料    
      您的位置: 首頁 > 技術文章 > SharePoint/Access互聯網

      VBA http post 上傳 multipart/form-data 附件

      時 間:2012-12-15 07:10:00
      作 者:dbaseIIIer   ID:22003  城市:深圳
      摘 要:尋遍世界各個網站,各個關鍵字都沒有的情況下,自己終于測試出來了!也回復了google上2006年以來發表都還沒有解決的問題!
      正 文:

      上傳附件,原來邏輯也很簡單,但是因為中文的問題,unicode 的問題,讓上傳變得復雜了!


      方法一:我們可以用 WebBrower 控件來處理網頁的提交動作的。我們可以通過 WebBrowser.Document.form(n).submit() 來提交網頁表單的內容。不過這個方法的缺點是:

      1. 這個控件是IE提供的原因,就有著瀏覽器是IE對的限制,譬如 user-agent 就一定是 ie ,

      2. 交互的服務器也只能是 html 的,不能是wap 服務器(ie 不支持 WML 標識);而且 

      3. WebBrowser 控件的內存耗用也比較大,對于同時多開的操作,就會讓程序變慢,內存耗用激增!


      所以,我針對的研究是用 XMLHttp 直接與服務器交互的 方法二。

      這個方法的好處是,原理通用于任何標準的 網頁服務器,不論 IIS, Apache, tomcat,或不論服務器的開發語言是 jsp , php , asp, asp.net。 我們開發的客戶端也可以是 js, Access/VBA, Excel/VBA, php ... 都能正確使用!


      對于上傳 multipart/form-data 格式的附件,找了很久找到很多段的代碼,標準就是要輸出這樣的結果:

      --boundary
      Content-Disposition: form-data; name="varname";
      
      <變量值>
      --boundary
      Content-Disposition: form-data; name="uploadName"; filename="上傳文件名稱"
      Content-Type:<文件內容>
      --boundary--

      看了下這個標準,會編程的人都會生成文本數據的了,不消一會就寫好了,

      Set http = New MSXML2.ServerXMLHTTP 
      http.Open "POST", URL, False
      postData = ......
      http.setRequestHeader "Content-Type", "multipart/form-data; boundary=" & boundary
      http.setRequestHeader "Content-Length", len( postData)
      http.Send postData http.WaitForResponse
      Debug.Print http.responseText

      可是問題出現了,怎么服務器沒有響應我的提交有變量提交的呢?測試了 iis, apache 都是失??!


      打開了 Chrome 瀏覽器,用網頁打開,提交后,在調試頁面看到提交的內容,跟我提交的內容都是一模一樣的呢,怎么我用 xmlhttp 提交的就沒有結果的呢?玩了幾6個小時,左對右對,都沒有答案!


      第二天,安裝了一個http封包截取器(fiddler 挺好用的?。┛纯次姨峤坏母?Chrome 提交的區別在哪里!發現了重大問題!


      重點1:我們提交的 postData 文本,都是用程序生成的,原來沒經過處理,就是 Unicode 文本!但是 multipart/form-data 的標準就不該是 unicode 文本!所以我們需要轉義!


      在生成的 postData 文本,用了一個 strConv( postData, vbFromUnicode),去測試,還是不行!玩來玩去,把附件去掉,服務器就對我的提交,終于感受到有變量了!但是顯示結果怎么還是有問題的???又玩了一段時間,發現重點2了。


      重點2:我們查看結果一般會用 http.responseText ,可是如果我們輸出網頁的內容包含中文字,這就牽涉 unicode 問題了!所以我們看結果需要用  StrConv(http.responseBody, vbUnicode)


      現在變量可以順利提交了,但是附件呢?還是不行,每次加上附件的那幾行來提交就出錯了!所以我就懷疑 StrConv 把 binary 的數據都改變了,我寫了幾句話去測試:


          cFile = "e:\qq\0.jpg"
          Dim b() As Byte
          ReDim b(FileLen(cFile))
          ff = FreeFile
          Open cFile For Binary As #ff
          Get #ff, , b
          Close #ff
          
          Dim s1 As String, s2 As String
          s1 = b
          s2 = StrConv(s1, vbUnicode)
          s2 = StrConv(s2, vbFromUnicode)
          Debug.Print Len(s1), Len(s2)
      結果發現, StrConv 轉換為 vbUnicode 然后又轉回來的話,是不會一樣的! 連長度都不一樣,我就沒有比較內容了!所以:



      重點3: 千萬不要讓 StrConv 去處理 postData文本 中 附件的部分!


      其實當中,我也花了不少時間去研究怎么把二進制數據與這個提交文本合并在一起的,其中我用了不同的方式來合并文本和二進制數據,不過經過測試后,也知道重點1 和 重點3 之后,就引刃而解了!


      重點4:附件讀入為文本,不能當字符串處理!


      找到有一段錯誤的代碼是


      nFile = FreeFile
      Open strFileName For Binary As #nFile
      strFile = String(LOF(nFile), " ")
      Get #nFile, , strFile
      Close #nFile
      看似很正確的做法,不過,其實對于不同年代的 Basic 語言來說曾經是正確的。因為現在是 Unicode 年代,用 String函數生成的 字符串都是 Unicode的,所以 String( LOF(nFile), " ") 會生成了比原來文件大了一倍的空間!因為Unicode的字符是雙字節的,讀入文件的大小是描述 Byte 的。根據這個方法讀進來的數據,在沒有 Unicode 年代的 VB, VBA, Basic 都是對的!



      這個年代正確的代碼應該是:

      nFile = FreeFile
      Open strFileName For Binary As #nFile
      strFile = String(LOF(nFile)\2+1, " ") '預留足夠空間
      Get #nFile, , strFile
      strFile = LeftB( strFile, LOF(nFile)) '截取 正確的字節數
      Close #nFile
      Dim b() as Byte
      nFile = FreeFile
      Open strFileName For Binary As #nFile
      ReDim b( LOF(nFile))
      Get #nFile, , b
      Close #nFile
      strFile = b  '把數組變回字符串 才可以與其他字符串相加,或進行字符串函數處理
      strFile = LeftB( strFile, UBound( b))  '去掉 VBA Chr(0) 的字串結尾標識
      注意這兩種方法最后的 LeftB,有別于Left 的!最后這個 strFile 就是讀入文件的二進制文本版了!



      重點5:注意回車換行符!

      multipart/form-data 除了那個 boundary 字符串來識別變量,其實換行符也是特別需要 來辨識 變量名稱和 附件內容的分隔的!里面的換行符,千萬不要用 vbCrLf ,這是 Unicode 四個字節的,而 multipart/form-data 格式的文本都是 非Unicode 的 就需要兩個字節的 換行符 (0x0D 0x0A)。 或者可以用 StrConv( vbCrLf, vbFromUnicode) 的結果都可以。


      最后的調試代碼就是:


          cFile = "E:\qq\test1.gif"
          Dim http As MSXML2.ServerXMLHTTP
          Set http = New MSXML2.ServerXMLHTTP
          
          http.Open "POST", URL, False
          
          Dim b() As Byte, strFile As String
                  
          boundary = "WebKitFormBoundary" & RandomString(16)
          postData = "--" & boundary & vbCrLf
          postData = postData & "Content-Disposition: form-data; name=""albumid""" & vbCrLf
          postData = postData & vbCrLf & "天aaaaa123" & vbCrLf
                  
          postData = postData & "--" & boundary & vbCrLf
          postData = postData & "Content-Disposition: form-data; name=""photo[]""; filename=""" & cFile & """" & vbCrLf
          postData = postData & "Content-Type: image/gif" & vbCrLf
          ReDim b(FileLen(cFile))
          ff = FreeFile
          Open cFile For Binary As #ff
          Get #ff, , b
          Close #ff
          strFile = b
          postData = StrConv(postData & vbCrLf, vbFromUnicode) & LeftB(s, UBound(b)) & ChrB(13) & ChrB(10)
                  
          postData = postData & StrConv("--" & boundary & "--" & vbCrLf, vbFromUnicode)
                  
          http.setRequestHeader "Content-Type", "multipart/form-data; boundary=" & boundary
          
          ReDim b(LenB(postData))
          b = postData
          'HexStr = ""         '這一段是我顯示提交的數據
          'For i = 0 To UBound(b)
          '    Debug.Print IIf(b(i) < 16, "0", "") & Hex(b(i)); " ";
          '    HexStr = HexStr & IIf(b(i) < 32 or b(i)>127, ".", Chr(b(i)))
          '    If i Mod 16 = 15 Then Debug.Print "  " & HexStr: HexStr = ""
          'Next
      
          http.setRequestHeader "Content-Length", UBound(b)
          http.send b
          http.waitForResponse
          
          Debug.Print http.getAllResponseHeaders
          Debug.Print StrConv(http.responseBody, vbUnicode)
      
      重點6:這個非常重要的是所有服務器的POST數據(包括multipart/form-data 這個格式)都是支持 Unicode 數據的提交的(我測試的是 Apache 服務器 2.03),但是這是自動檢測的,意思是 如果你整個提交的內容里面都沒有一個中文字,或任何雙字節的內容,這個檢測過程就判定這不是 Unicode 數據,那就變成你提交的文本是 ANSI 單字節文本,也就會造成你的 boundary 文本都檢測不出來!意思是一個變量都檢測不到!



      重點7:最后我的提交 http.send 也是用了 byte array ,也就是免得 VBA 對字符串的處理又多了一個 ChrB(0)在結尾!


      沒有犯上以上的 7宗罪,必定把多個中文字+附件都能正確提交到任何服務器的了!

      今天我就會為這些代碼封裝成類代碼了,有成品再與各位發布!




      開發者你們好,這是 地球信息思維開發者 dbaseIIIer (QQ325613888) 
      更多的 Access/VBA 互聯網交互技術,可以參考 本站內部欄目  互聯網技術欄目
      更多的思維在  新浪微博   Access貼吧  VisualPHP貼吧



      Access軟件網QQ交流群 (群號:143677707)       access源碼網店

      最新評論 查看更多評論(1)

      2012/12/15 8:17:42杜小杰

      發表評論您的評論將提升作者分享的動力!快來評論一下吧!

      用戶名:
      密 碼:
      內 容:
       

      常見問答

      技術分類

      相關資源

      最新帖子

      關于我們 | 服務條款 | 在線投稿 | 友情鏈接 | 網站統計 | 網站幫助