从 Tcl 脚本发布时出现 MalformedStreamException
我制作了一个 servlet,它使用 org.apache.commons.fileupload api 上传 CSV 文件,然后将其加载到 MySQL 表中。当从浏览器中的表单发布到 servlet 时,这非常有效。但是,当尝试通过 Tcl 脚本 http:// /www.tcl.tk/man/tcl/TclCmd/http.htm#M20。
该 servlet 抛出以下 MalformedStreamException
:
org.apache.commons.fileupload.MultipartStream$MalformedStreamException:
Stream ended unexpectedly
该 servlet 托管在 Tomcat 版本 6.0.16 上。
Tcl 脚本成功建立连接,因为它收到 HTTP/1.1 200 OK 响应,并且 servlet 确实将一些打印语句返回到客户端 Tcl 脚本。但是,当尝试读取输入流时,它会失败。
Tcl脚本:
package require http
proc upload { url csv } {
set boundary "-----WebKitFormBoundary[clock seconds][pid]"
set fid [open $csv r]
if {[catch {read $fid [file size $csv]} data]} {
return -code error $data
}
close $fid
set content {}
append content "--${boundary}\n"
append content "Content-Disposition: form-data; name=\"db\"\n\n"
append content "test\n"
append content "--${boundary}\n"
append content "Content-Disposition: form-data; name=\"table\"\n\n"
append content "testing\n"
append content "--${boundary}\n"
append content "Content-Disposition: form-data; name=\"file\"; filename=\"$csv\"\n"
append content "Content-Type: text/csv\n\n"
append content "$data\n"
append content "${boundary}--"
set headers "connection keep-alive"
set token [::http::geturl $url -keepalive 1 -headers $headers -query $content -type "multipart/form-data; boundary=$boundary"]
upvar 0 $token state
if {$state(http) == "HTTP/1.1 200 OK"} {
# no error reported in http headers
puts stdout $state(http)
puts stdout $state(body)
return 1
} else {
# error reported in http headers
puts stdout $state(http)
puts stdout $state(body)
return 0
}
}
set csv "data.csv"
set url "http://ecat:8080/MySqlImport/MySqlImportServlet"
set retVal [upload $url $csv]
I've made a servlet which uses the org.apache.commons.fileupload api to upload a CSV file which it should then load into a MySQL table. This works perfectly when posting to the servlet from a form in a browser. However, it fails when trying to post a form via a Tcl script http://www.tcl.tk/man/tcl/TclCmd/http.htm#M20.
The servlet throws the following MalformedStreamException
:
org.apache.commons.fileupload.MultipartStream$MalformedStreamException:
Stream ended unexpectedly
The servlet is hosted on Tomcat version 6.0.16.
The connection is made successfully by the Tcl script as it receives an HTTP/1.1 200 OK response and the servlet does return some print statements back to the client Tcl script. However it fails when trying to read the input stream.
Tcl script:
package require http
proc upload { url csv } {
set boundary "-----WebKitFormBoundary[clock seconds][pid]"
set fid [open $csv r]
if {[catch {read $fid [file size $csv]} data]} {
return -code error $data
}
close $fid
set content {}
append content "--${boundary}\n"
append content "Content-Disposition: form-data; name=\"db\"\n\n"
append content "test\n"
append content "--${boundary}\n"
append content "Content-Disposition: form-data; name=\"table\"\n\n"
append content "testing\n"
append content "--${boundary}\n"
append content "Content-Disposition: form-data; name=\"file\"; filename=\"$csv\"\n"
append content "Content-Type: text/csv\n\n"
append content "$data\n"
append content "${boundary}--"
set headers "connection keep-alive"
set token [::http::geturl $url -keepalive 1 -headers $headers -query $content -type "multipart/form-data; boundary=$boundary"]
upvar 0 $token state
if {$state(http) == "HTTP/1.1 200 OK"} {
# no error reported in http headers
puts stdout $state(http)
puts stdout $state(body)
return 1
} else {
# error reported in http headers
puts stdout $state(http)
puts stdout $state(body)
return 0
}
}
set csv "data.csv"
set url "http://ecat:8080/MySqlImport/MySqlImportServlet"
set retVal [upload $url $csv]
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
生成正确的多部分消息可能会非常令人烦恼。 Tcllib 中的
mime
包 可以提供帮助。 此页面上的顶部示例看起来高度相关。Generating a proper multipart message can be highly aggravating. The
mime
package in Tcllib can help. The top example on this page looks highly relevant.虽然 Donal Fellows 很可能是对的,但我想指出 Daniel 方法的一个(典型)错误:当人们使用一种具有“字符串”概念的高级语言时,而不是“字符串”,字节数”——在准备将要处理的数据放置在“线路”上时,必须特别小心。
在这种情况下,“线路”是 HTTP 协议,因此准备的数据很可能会经历类似
[encoding conversionto utf-8 ...]
或类似的内容,具体取决于意图。 (顺便说一句,Tcl 对二进制字符串有相当广泛的支持,以便它们可以附加、连接等,而不会失去其 binary。)接下来可以推荐的是习惯网络嗅探器(或特定的 HTTP 嗅探器)。一个出色的跨平台解决方案是 Wireshark,在 Windows 平台上有 网络监视器(通用嗅探器)和Fiddler(特定于 HTTP)。这个想法是首先捕获并仔细研究浏览器执行的“参考”会话,然后捕获并分析程序执行的行为不当的会话。因此,您只需比较会话并查看生成的流量在 HTTP 负载方面有何不同。
While Donal Fellows most probably got it right, I want to point out one (typical) mistake with the Daniel's approach: when one uses a high-level language which has the concept of a "string of characters"—as opposed to a "string of bytes"—one is oblidged to take special care when preparing the data being processed to be placed on "the wire".
In this case "the wire" is the HTTP protocol and so the data prepared should most probably go through something like
[encoding convertto utf-8 ...]
or something like this depending on the intent. (By the way, Tcl has rather extensive support for binary strings so that they can be appended to, concatenated etc without losing their property of being binary.)The next thing which can be recommended is to get accustomed with a network sniffer (or a specific HTTP sniffer). A great cross-platform solution is Wireshark, on Windows platforms there's Network Monitor (a generic sniffer) and Fiddler (HTTP-specific). The idea is to first capture and study closely the "reference" session performed by a browser then to capture and analyze the misbehaving session performed by your program. So you just compare the sessions and see where the generated traffic differ when it comes to HTTP payload.
感谢您对此问题的帮助,但我找到了我的具体问题,并且最终这是一个简单的问题。似乎 apache.commons.fileupload 中的 ServletFileUpload.parseRequest(request) 方法要求行结尾采用 Windows 格式。
正如您在问题中看到的,$content 行结尾是
\n
将其更改为\r\n
纠正了问题。仅使用
\r
意味着错误不再发生,但它无法识别请求中的不同项目或部分。
\r\n
行结尾的组合可以正常工作。Thank you for the help on this one but I have found my specific issue and it was a simple one in the end. It seems as though the ServletFileUpload.parseRequest(request) method in apache.commons.fileupload requires the line endings to be in windows format.
As you can see in the question the $content line endings was
\n
changing this to\r\n
corrected the problem.Using just
\r
meant that the errorno longer occured, however it failed to recognise the different items or parts in the request. The combination of
\r\n
line endings and it works correctly.