Http伺服器實現檔案上傳與下載(五)
一、引言
歡迎大家和我一起編寫Http伺服器實現檔案的上傳和下載,現在我回顧一下在上一章節中提到的一些內容,之前我已經提到過檔案的下載,在檔案的下載中也提到了檔案的續下載只需要在響應頭中填寫Content-Range這一欄位,並且伺服器的檔案指標指向讀取的指定位置開始讀取傳輸。在這一章節中我講講解檔案的上傳這一功能,講完這一章節,大致的功能也全部完成,接著就是上面檔案控制模組和一些資源模組。
在檔案的上傳中主要以HttpRequest類為主,在考慮檔案的上傳時我一點迷惑,到底把檔案的上傳功能是放到HttpResponse下還是在HttpRequest下,畢竟HttpResponse中有一些相應的檔案下載功能,在新增一個檔案上傳功能也不為過。但是我最終還是選擇在HttpRequest中,原因是我主要是HttpResponse作為是伺服器到瀏覽器傳送內容,而HttpRequest作為瀏覽器到伺服器傳送內容。這樣下載和上傳的功能就分別坐落在了HttpResponse和HttpRequest上了。
在完成功能上的歸屬問題後,接著直接上程式碼,在檔案的上傳中,涉及到C++流。在這裡其實用到不是很多的內容,但是這卻是C++一個重要的一大塊內容。有時間和大家在一起復習這一塊內容。好了,接著上程式碼咯,上一章的內容有設計一些HttpRequest的程式碼,沒有全部的包括進去。
二、HttpRequest
標頭檔案(include/httprequest.h)
1 #ifndef HTTPREQUEST_H 2 #define HTTPREQUEST_H 3 #include "socket.h" 4 #include <map> 5 #include <string> 6 #include <fstream> 7 namespace Http{ 8 class HttpRequest{ 9 public: 10 HttpRequest(TCP::Socket &c); 11 virtual ~HttpRequest(); 12 std::string getMethod() const; 13 std::string getUrl() const; 14 std::string getHost() const; 15 std::map<std::string,std::string> getHeader(int confd) ; 16 ssize_t upload(int confd,std::string filename); 17 protected: 18 private: 19 std::string method; 20 std::string url; 21 std::string host; 22 TCP::Socket &s; 23 }; 24 } 25 #endif // HTTPREQUEST_H
原始檔(src/httprequest.cpp)
1 #include "httprequest.h" 2 #include "utils.h" 3 namespace Http{ 4 HttpRequest::HttpRequest(TCP::Socket &c):s(c){ 5 } 6 7 HttpRequest::~HttpRequest(){ 8 } 9 std::map<std::string,std::string> HttpRequest::getHeader(int confd){ 10 char recvBuf[1024]; 11 memset(recvBuf,0,sizeof(recvBuf)); 12 s.server_read(confd,recvBuf,1024); 13 std::cout<<recvBuf<<std::endl; 14 std::map<std::string,std::string> mp =Utils::parseHeader(recvBuf); 15 method =mp["Method"]; 16 url=mp["Url"]; 17 host=mp["Host"]; 18 return mp; 19 } 20 ssize_t HttpRequest::upload(int confd,std::string filename){ 21 char buf[1024]; 22 size_t n=0; 23 ssize_t nread=0; 24 std::string boundary; 25 std::string file; 26 std::ofstream outStream; 27 int readlineCount=1; 28 while(1){ 29 memset(buf,0,sizeof(buf)); 30 n=s.server_readline(confd,buf,sizeof(buf)); 31 if(readlineCount==1){ 32 boundary=std::string(buf,buf+strlen(buf)-2); 33 boundary+="--\r\n"; 34 std::cout<<boundary<<std::endl<<boundary.size(); 35 }else if(readlineCount==2){ 36 int i=n; 37 while(buf[i]!='='){ 38 if((buf[i]>='0'&&buf[i]<='9') 39 ||(buf[i]>='a'&&buf[i]<='z') 40 ||(buf[i]>='A'&&buf[i]<='Z') 41 ||(buf[i]=='.')) 42 i--; 43 else{ 44 buf[i]='*'; 45 i--; 46 } 47 } 48 file=std::string(buf+i+2,buf+n-3); 49 }else if(readlineCount==3){ 50 std::string rw; 51 rw=std::string(buf,buf+strlen(buf)); 52 int pos=rw.find('/'); 53 rw=rw.substr(0,pos); 54 filename=filename+file; 55 if(rw=="Content-Type: text") 56 outStream.open(filename.c_str()); 57 else{ 58 outStream.open(filename.c_str(),std::ios::binary); 59 std::cout<<"ios::binary"<<std::endl; 60 } 61 }else if(readlineCount==4){ 62 memset(buf,0,sizeof(buf)); 63 while(1){ 64 n=s.server_readn(confd,buf,sizeof(buf)); 65 if(n==boundary.size()&&strcmp(buf,boundary.c_str())==0){ 66 goto exit; 67 } 68 nread+=n; 69 if(buf[n-1]==0){ 70 outStream.write(buf,n-1); 71 }else{ 72 outStream.write(buf,n); 73 } 74 } 75 } 76 readlineCount++; 77 } 78 exit: 79 outStream.close(); 80 s.server_close(confd); 81 return nread; 82 } 83 std::string HttpRequest::getMethod() const{ 84 return method; 85 } 86 std::string HttpRequest::getUrl() const{ 87 return url; 88 } 89 std::string HttpRequest::getHost() const{ 90 return host; 91 } 92 }
好了上傳檔案的程式碼也已經出來了,現在就是對其稍微的解釋一下把。在解釋程式碼之前先看一下我們在點選上傳檔案按鈕的時候,瀏覽器給伺服器傳送的內容是什麼,比如我有一個test.txt的文字檔案(這裡採用檔案檔案是為了好檢視內容,其實二進位制檔案也是一致的)。test.txt檔案的內容只有一行就是aaabbb這6個字母。接著開啟可以火狐的開發者網路這一功能。並且點擊發送檔案後,可以在訊息頭上看到如下資訊。
這些內容在之前的章節已經講過了,這裡就不重複了,並且點選引數這一選項可以看到如下資訊。
在這裡第1,2行是請求頭的內容,接著一行空行之後是請求體4-9行。看到請求體的內容不是直接是test.txt的內容。顯示‘--23469111452’為開頭,拜師這個是文字的分隔符。前面固定一段'-',加上一個瀏覽器自動產生的資料。並且一個檔案的解釋也是這樣,只是數字後面多了2個'-'。在第5,6行是對上傳的檔案的描述。接著是一行空行。第8行開始就是檔案的內容了。知道這個請求體後,很容易的就可以寫出程式碼。上面的的upload中readlineCount變數就是起到定位功能。看伺服器已經接收到那一行了,這裡s.server_readn這個行數之間沒有提交,現在的程式碼段一直在修改,所以有些與部落格有點差別,大體上還是一致的。
寫到這裡,基本上完成了HttpServer這一檔案上傳和下載功能。接著就是組合這些某塊。將在下一章《Http伺服器實現檔案上傳與下載(六)》中進行講解。