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软件网QQ交流群 (群号:54525238) Access源码网店
常见问答:
技术分类:
源码示例
- 【源码QQ群号19834647...(12.17)
- Access对子窗体数据进行批...(10.30)
- 最精简的组合框行来源数据快速输...(10.25)
- Access仿平台的多值选择器...(10.24)
- 【Access日期区间段查询】...(10.22)
- 【Access源码示例】VBA...(10.12)
- Access累乘示例,Acce...(10.09)
- 数值8.88,把整数8去掉,转...(10.08)
- 【Access自定义函数】一个...(09.30)
- 【Access选项卡示例】Ac...(09.09)
学习心得
最新文章
- Access快速开发平台企业版--...(11.18)
- 不会用多表联合查询,多表查询没结果...(11.16)
- 【案例分享】主键字段值含有不间断空...(11.16)
- Access快速开发平台--后台D...(11.14)
- 微软Access邀测新Monaco...(11.12)
- Access列表框左右互选、列表框...(11.11)
- 高效率在导入数据前删除记录(11.10)
- Access报价单转订单示例代码(11.08)
- Access系统自带的日期选择器不...(11.08)
- 分享一下Access工程中的acw...(11.07)