當上傳檔案時,客戶端對伺服器說了些什麼?
在一次寫檔案上傳程式時,突發奇想,想了解一下在通過Form表單上傳檔案過程中,客戶端給伺服器端傳送的資料格式是什麼樣的。 在大概瞭解了檔案上傳協議相關內容之後,寫了一個非常傻瓜的伺服器程式——它只會把客戶端傳送過來的訊息內容原封不動地列印出來。下面是Rebol程式碼:
Rebol [
Title: "Parse file uploads data via Form"
]
server: open tcp://:8080
server/awake: func [ event /local port ][
if event/type = 'accept [
port: first event/port
port/awake: func [ event /local data ][
switch event/type [
read [
print [ to string! event/port/data ]
clear event/port/data
write event/port to-binary {Some data ^/}
]
wrote [
read event/port
]
close [
close event/port
return true
]
]
false
]
read port
]
false
]
wait [ server 20 ] ;20秒後超時
close server
可以看到,這個伺服器程式開啟了8080埠,直接使用TCP協議讀取資料。
客戶端使用curl來模擬:
curl --form upload1=@D:/a.txt --form upload2=@D:/b.txt --form press=OK http://localhost:8080
這條命令相當於:
<form action="http://127.0.0.1:8080/" method="post" enctype="multipart/form-data">
<input type="file" name="upload1" /><br />
<input type="file" name="upload2" /><br />
<input type="text" name="press" value="OK">
<input type="submit" />
</form>
輸出結果如下:
POST / HTTP/1.1
User-Agent: curl/7.39.0
Host: localhost:8080
Accept: */*
Content-Length: 450
Expect: 100-continue
Content-Type: multipart/form-data; boundary=------------------------9de42a043d77defe
--------------------------9de42a043d77defe
Content-Disposition: form-data; name="upload"; filename="a.txt"
Content-Type: text/plain
Hello world!
--------------------------9de42a043d77defe
Content-Disposition: form-data; name="upload2"; filename="b.txt"
Content-Type: text/plain
Hello world, too!
--------------------------9de42a043d77defe
Content-Disposition: form-data; name="press"
OK
--------------------------9de42a043d77defe--
仔細觀察會發現,curl在傳送資料的時候是先傳送請求頭,然後傳送上傳訊息流(包括其中的請求頭和檔案的內容),並且中間有一定的時滯。原因在於curl採用的是HTTP1.1協議。在HTTP1.1中,為了提高效率,設定了一個Expect: 100-continue
訊息頭。它的作用是,在客戶端要傳送大量的資料之前,可以先對伺服器進行詢問,是否接受這個請求。如果伺服器願意接受,則回應一個HTTP/1.1 100 Continue
狀態,否則回應HTTP/1.1 417 Expectation Failed
。另外,對於不能識別Expect: 100-continue
訊息頭的舊伺服器或者像上面展示的很傻很天真的伺服器,客戶端不應該無限等待伺服器的回應(詳見rfc2616規範)。因此,客戶端在等待我們的很傻很天真的伺服器一段時間後,還是默默地把上傳檔案資料的請求傳送了出來。;-)
當然了,我們可以使我們的伺服器“聰明”一些。那就是對Expect: 100-continue
的請求做出反應:
...
read [
print [ to string! event/port/data ]
data: event/port/data
if find data to binary! {Expect: 100-continue} [
write event/port to-binary {HTTP/1.1 100 Continue ^M^/^M^/}
]
clear event/port/data
write event/port to-binary {Some data ^/}
]
...
瞬間客戶端的響應速度就快了很多!:-D
接下來做點什麼呢?(ˇˍˇ)......當然是將上傳的檔案儲存起來啦!:-P上傳的資料流都已經拿到了,接下來只要解析資料流就可以了。程式碼如下:
Rebol [
Title: "Parse file uploads data via Form"
]
server: open tcp://:8080
boundary: ""
segment: to-binary reduce [ crlf crlf ]
flag: to-binary {Content-Type: multipart/form-data; boundary=}
flag2: to-binary {filename="}
server/awake: func [ event /local port ][
if event/type = 'accept [
port: first event/port
port/awake: func [ event /local data ][
switch event/type [
read [
print [ to-string event/port/data ]
data: event/port/data
if find data to-binary "Expect: 100-continue" [
write event/port to-binary {HTTP/1.1 100 Continue ^M^/^M^/}
]
either find data flag [
;print "================Header data"
parse data [
;找到資料的“分割邊界”
thru flag copy boundary to segment (print to-string boundary)
]
][
;print "================file data"
parse data [
any [
thru boundary thru flag2 copy file-name to {"} ;獲取原檔名
thru segment ;越過上傳訊息流的請求頭
copy file-data to boundary ( write to-file file-name file-data) ;將檔案資料拷貝到file-data中並寫入檔案
]
]
]
clear event/port/data
write event/port to-binary {Some data ^/}
]
wrote [
read event/port
]
close [
print "Close"
close event/port
return true
]
]
false
]
read port
]
false
]
wait [ server 20 ]
close server
當然可以上傳各種格式的檔案了:
curl --form upload=@D:/a.txt --form upload=@D:/a.jpg --form press=OK http://localhost:8080
如何執行我們的伺服器?
- 下載Rebol3.0的直譯器;
- 將指令碼另存為server.reb檔案;
- 執行檔案:
do %server.reb
。
參考資料:
- http://www.faqs.org/rfcs/rfc1867.html
- http://www.rebol.net/wiki/Port_Examples
- http://www.w3.org/Protocols/rfc2616/rfc2616-sec8.html#sec8.2.3
- http://blog.zhaojie.me/2011/03/html-form-file-uploading-programming.html
- http://www.snooda.com/read/322
相關文章
- 客戶端svn上傳後,原始檔案在伺服器的什麼位置?客戶端伺服器
- 當多個客戶端併發傳輸大檔案時,其傳輸速度還能保證嗎?客戶端
- 檔案上傳——客戶端檢測繞過(JavaScript檢測)(一)客戶端JavaScript
- 一個端到端的基於 form 表單的檔案上傳程式,包含客戶端和伺服器端ORM客戶端伺服器
- SecureFX for Mac(跨平臺檔案傳輸客戶端)Mac客戶端
- 實現上傳(增刪)多個檔案的客戶端寫法。 (轉)客戶端
- 什麼是客戶端渲染?客戶端
- 故障排查:是什麼 導致了客戶端批量心跳超時掉線客戶端
- 當我們談論Promise時,我們說些什麼Promise
- 故障排查:是什麼導致了客戶端批量心跳超時掉線(轉)客戶端
- SecureCRT +SecureFX for Mac(終端ssh工具+跨平臺檔案傳輸客戶端)SecurecrtMac客戶端
- 為什麼CDN對移動客戶端加速“沒有”效果客戶端
- 客戶端配置檔案tnsname.ora客戶端
- 使用java呼叫fastDFS客戶端進行靜態資原始檔上傳JavaAST客戶端
- 客戶端網路配置上的一點說明客戶端
- 上傳檔案到VPS和雲伺服器用什麼軟體?伺服器
- nio 當客戶端主動關閉連線,伺服器端怎麼才能知道客戶端伺服器
- 關於檔案上傳。以上傳人來設定限額,或者以每天總額度,或者可以設定同時上傳的客戶端總數。客戶端
- Oracle 12.2對客戶端工具的支援說明Oracle客戶端
- 幾年前,為什麼我擼了一套RabbitMQ客戶端?MQ客戶端
- java方法客戶端下載伺服器上的檔案到本地Java客戶端伺服器
- 讓UpdatePanel支援檔案上傳(2):伺服器端元件伺服器元件
- Linux伺服器上傳檔案傳送檔案Linux伺服器
- 當我有一臺雲伺服器時,我做了些什麼伺服器
- React 伺服器端渲染和客戶端渲染效果對比React伺服器客戶端
- 用AnySQL在沒有oracle客戶端的伺服器上傳送郵件SQLOracle客戶端伺服器
- 客戶端怎麼連線到伺服器?客戶端伺服器
- 檔案下載之斷點續傳(客戶端與服務端的實現)斷點客戶端服務端
- 當產品/後端/QA/你自己說了這些話,就要警惕了!後端
- SSH實現客戶按條件查詢\上傳檔案等
- 請教一個web群集下面客戶上傳檔案的問題Web
- 【知識積累】伺服器端獲取客戶端的IP地址(當客戶端呼叫由Axis開發的WebService)伺服器客戶端Web
- 當我談 HTTP 時,我談些什麼?HTTP
- 簡單的C++檔案伺服器--Linux C++客戶端從服務端獲取檔案C++伺服器Linux客戶端服務端
- 科普|不同協議下遠端伺服器檔案上傳_下載優劣對比協議伺服器
- Qt實現基於多執行緒的檔案傳輸(服務端,客戶端)QT執行緒服務端客戶端
- 定時ftp上傳,如何設定定時ftp上傳檔案FTP
- DB2客戶端大家用什麼?DB2客戶端