1. 程式人生 > >Http伺服器實現檔案上傳與下載(四)

Http伺服器實現檔案上傳與下載(四)

一、引言

  歡迎大家來到和我一起編寫Http伺服器實現檔案的上傳和下載,現在我稍微回顧一下之前我說的,第一、二章說明說明了整體的HTTP走向,第三章實現底層的網路程式設計。接著這一章我想給大家講的是請求獲取,和響應傳送的內容。這裡主要講解的響應內容,為什麼?因為我們編寫的是一個與瀏覽器互動的HTTP伺服器,所以大多數的情況下我們只進行被動的應答。

  這就是一種"提問--回答"的問題。其實在講解這章的時候,我本來準備給大家講解一下Linux一些訊號中斷的問題。因為在網路層傳送的時候,系統會發送一些訊號給我們的應用程式,所以會導致我們的程式意外的終止。但當我寫的這篇部落格的時候我又放棄,我想在講流程走向的時候再提一箇中斷捕獲吧。在這個請求響應層的類其實真正的設計需要很多的內容,這裡就是HttpResponse類和HttpRequest類的設計,在j2EE中,我們編寫Servlet的時候就用到了這2個類,如HttpServletResquest,HttpServletResponse的類,如果對這裡面的內容感興趣,可以下載tomcat,在servlet-api.jar包裡面有這些類。

  在本文的實現中,Request類只包含了一個獲取請求頭和解析頭的一些方法。如何解析頭,我在《Http伺服器實現檔案上傳與下載(一)》已經講解了,讀者只需要對其封裝一個類即可。

二、HttpRequest類

  請求訊息的解析是通過被定義在名稱空間為Http的類名為HttpRequest。這個類的建構函式接受一個套接字,就是跟我們連線的那個套接字,在網路層我們已經講過了,然後在getHeader方法中呼叫server_read()獲取請求頭,然後通過Utils::parseHeader()函式進行解析。這樣把解析的內容放入需要的string中,當前不太需要的直接在map裡面。這裡我直接貼出程式碼,大家看起來也比較容易。這裡我在這一章節我主要講解的是檔案的下載,所以主要會對HttpResponse的類的分析,而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::map<std::string,std::string> getHeader(int confd) ; 13 ...... 14 protected: 15 private: 16 std::string method; 17 std::string url; 18 std::string host; 19 TCP::Socket &s; 20 }; 21 } 22 23 #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     ......
21 }

三、HttpResponse類

當我們訪問Http伺服器的時候,瀏覽器顯示可以下載的檔案的內容,然後我們點選需要下載的檔案,然後檔案就可下載了。首先我點選這個檔案這個URL時,瀏覽器給我們傳送一些請求頭,例如它傳送一個為/download/HttpServer.zip這個URL,說明他需要下載的檔案,而且該檔案為HttpServer.zip。在上面我們已經可以用getHeader來捕獲這個請求頭,然後獲取這個URL。之後服務端還是要傳送一個響應頭,告訴瀏覽器你的請求我們同意,請求頭結束以空行為標記,接著就是具體的檔案的內容了。

  在傳送響應頭時,還是需要傳送協議版本,狀態碼,響應內容型別,檔案的長度,檔案斷點下載等內容,或者傳輸的時候採用chunk傳輸,但是這裡我採用檔案的長度來標記。讀者可以自行檢視其它方式傳輸內容。特別要注意ed是一定要在響應頭中指定傳輸實體的大小,否則客戶端不知道什麼時候結束,這時可能拒絕接收服務端發來的位元組。在這個類中,請求下載的檔案傳送時,我採用sendFile這個函式,這個函式讀取檔案就是採用二進位制的方式,並且在響應頭中也告知瀏覽器以二進位制的方式接收檔案。這樣都是以二進位制的方式讀取和傳送檔案才不會出現問題。sendLineFile 和sendIndexFile兩者大致相同,都是採用ASCII文字的方式傳送內容,這樣比如HTML這些需要顯示在瀏覽器的內容,可以通過這兩個函式。通過函式名可知在sendLineFile會以檔案行的方式讀取,而sendIndexFile檔案會把內容寫在同一行上。例如:我們瀏覽器請求一個index.html的內容,這時採用2個sendLineFile和sendIndexFile的顯示效果都是一樣的,但是如果點選右鍵檢視原始碼時,sendLineFile的內容是以原始檔一樣的,而sendIndexFile傳送的內容會都在第一行,不會換行。

  說了這麼多大家也比較清楚了,下面貼出具體一些程式碼。

標頭檔案(include/httpresponse.h)

 1 #ifndef HTTPRESPONSE_H
 2 #define HTTPRESPONSE_H
 3 #include "socket.h"
 4 #include<string>
 5 #include<fstream>
 6 #include<sstream>
 7 #include<iterator>
 8 #include<algorithm>
 9 #include<time.h>
10 #include "utils.h"
11 namespace Http{
12     class HttpResponse{
13     public:
14         HttpResponse(TCP::Socket &c);
15         virtual ~HttpResponse();
16         ssize_t send(int confd,std::string content);
17         ssize_t sendIndexFile(int confd,std::string FileName);
18         ssize_t sendFile(int &confd,std::string FileName,int64_t pos);
19         ssize_t sendLineFile(int confd,std::string file);
20         void setProtocal(std::string);
21         void setStatusCode(std::string);
22         void setServerName(std::string);
23         void setContentType(std::string);
24         void setContentRange(std::string);
25         void setContentLength(int64_t);
26     protected:
27         std::string getHeader() const;
28     private:
29         std::string protocal;
30         std::string statusCode;
31         std::string serverName;
32         std::string contentType;
33         std::string contentLength;
34         std::string contentRange;
35         std::string connection;
36         std::string date;
37         TCP::Socket &s;
38     };
39 }
40 #endif // HTTPRESPONSE_H

原始檔(src/httpresponse.cpp)

  1 #include "httpresponse.h"
  2 namespace Http{
  3      HttpResponse::HttpResponse(TCP::Socket &c):s(c){
  4          protocal="HTTP/1.1";
  5          statusCode="200 OK";
  6          serverName="Server:(Unix)";
  7          contentType="Content-type:text/html";
  8          contentLength="Content-length:0";
  9          contentRange="Content-Range:0-";
 10          connection="Connection:Keep-Alive";
 11          time_t timep;
 12          time(&timep);
 13          char s[50];
 14          sprintf(s,ctime(&timep));
 15          date="Date:"+std::string(s,s+(strlen(s)-1));
 16     }
 17 
 18     HttpResponse::~HttpResponse(){
 19     }
 20     void HttpResponse::setProtocal(std::string content){
 21         protocal=content;
 22     }
 23     void HttpResponse::setStatusCode(std::string content){
 24         statusCode=content;
 25     }
 26     void HttpResponse::setServerName(std::string content){
 27         serverName=content;
 28     }
 29     void HttpResponse::setContentType(std::string content){
 30         contentType="Content-type:"+content;
 31     }
 32     void HttpResponse::setContentLength(int64_t len){
 33         contentLength="Content-length:"+Utils::toString(len);
 34     }
 35     void HttpResponse::setContentRange(std::string content){
 36         contentRange="Content-Range:"+content;
 37     }
 38     std::string HttpResponse::getHeader() const{
 39         std::string h1 =protocal+" "+statusCode+"\r\n";
 40         std::string h2 =serverName+"\r\n";
 41         std::string h3 =contentType+"\r\n";
 42         std::string h4 =contentLength+"\r\n";
 43         std::string h5=contentRange+"\r\n";
 44         std::string h6=connection+"\r\n";
 45         std::string h7=date+"\r\n\r\n";
 46         return h1+h2+h3+h4+h5+h6+h7;
 47     }
 48     ssize_t HttpResponse::send(int confd,std::string content){
 49         setContentType("application/octet-stream");
 50         setContentLength(content.size());
 51         std::string header=getHeader();
 52         s.server_write(confd,(char*)header.c_str(),header.size());
 53         ssize_t len =s.server_write(confd,(char*)content.c_str(),content.size());
 54         s.server_close(confd);
 55         return len;
 56     }
 57     ssize_t HttpResponse::sendLineFile(int confd,std::string file){
 58         std::ifstream in(file.c_str());
 59         in.seekg(0,std::ios::end);
 60         int64_t  len = in.tellg();
 61         setContentLength(len);
 62         std::string header=getHeader();
 63         s.server_write(confd,(char*)header.c_str(),header.size());
 64         in.seekg(0,std::ios::beg);
 65         ssize_t n=0;
 66         char buf[1024];
 67         while(!in.eof()){
 68             bzero(buf,sizeof(buf));
 69             in.getline(buf,1024);
 70             buf[strlen(buf)]='\n';
 71             n+=s.server_write(confd,buf,in.gcount());
 72         }
 73         s.server_close(confd);
 74         return n;
 75     }
 76     ssize_t HttpResponse::sendIndexFile(int confd,std::string file){
 77         std::ifstream in(file.c_str());
 78         in.seekg(0,std::ios::end);
 79         int64_t  len = in.tellg();
 80         setContentLength(len);
 81         std::string header=getHeader();
 82         s.server_write(confd,(char*)header.c_str(),header.size());
 83         in.seekg(0,std::ios::beg);
 84         char buf[1024];
 85         int sendCount=0;
 86         while(!in.eof()){
 87             memset(buf,0,sizeof(buf));
 88             in.getline(buf,1024);
 89             sendCount+=s.server_write(confd,buf,in.gcount());
 90         }
 91         s.server_close(confd);
 92         return sendCount;
 93     }
 94     ssize_t HttpResponse::sendFile(int &confd,std::string fileName,int64_t pos){
 95         std::ifstream in(fileName.c_str(),std::ios::binary);
 96         in.seekg(0, std::ios::end);
 97         std::streampos ps = in.tellg();
 98         int64_t len=ps-pos;
 99         if(pos!=0){
100             setStatusCode("206 Partial Content");
101         }
102         setContentType("application/octet-stream");
103         setContentLength(len);
104         std::string content="bytes";
105         content+=" "+Utils::toString(pos)+"-"+Utils::toString((int64_t)ps-1)+"/"+Utils::toString(len);
106         setContentRange(content);
107         std::string header=getHeader();
108         std::cout<<header<<std::endl;
109         s.server_write(confd,(char*)header.c_str(),header.size());
110         in.seekg(pos,std::ios::beg);
111         char buf[1024];
112         ssize_t n=0;
113         while(!in.eof()){
114             in.read(buf,1024);
115             n+=s.server_write(confd,buf,in.gcount());
116         }
117         s.server_close(confd);
118         return n;
119     }
120 }

  在上面響應頭中Content-Range:這個欄位,表示檔案內容的範圍,在一般情況下都是從0到lenth(file)-1。如果在之前已經下了一些內容後,如果是斷點續下載時,瀏覽器在請求頭中有Range知道,表示從Range的開始位元組傳輸,而我們伺服器指定Content-Range為Range欄位開始,接著傳送這些內容即可,實現檔案的斷點下載。接下來的內容請大家看《Http伺服器實現檔案上傳與下載(五)》。

相關推薦

Http伺服器實現檔案下載()

一、引言   歡迎大家來到和我一起編寫Http伺服器實現檔案的上傳和下載,現在我稍微回顧一下之前我說的,第一、二章說明說明了整體的HTTP走向,第三章實現底層的網路程式設計。接著這一章我想給大家講的是請求獲取,和響應傳送的內容。這裡主要講解的響應內容,為什麼?因為我們編寫的是一個與瀏覽器互動的HTTP伺服器

Http伺服器實現檔案下載(二)

一、引言 歡迎大家接著看我的部落格,如何大家有什麼想法的話回覆我哦,閒話不多聊了,接著上一講的內容來說吧,在上一節中已經講到了請求頭字串的解析,並且在解析中我我們已經獲取了url。就是上節中提到的/doing。當瀏覽器傳送了/doing請求後,這是的與伺服器的連線並沒有

Http伺服器實現檔案下載(五)

一、引言      歡迎大家和我一起編寫Http伺服器實現檔案的上傳和下載,現在我回顧一下在上一章節中提到的一些內容,之前我已經提到過檔案的下載,在檔案的下載中也提到了檔案的續下載只需要在響應頭中填寫Content-Range這一欄位,並且伺服器的檔案指標指向讀取的指定

Http伺服器實現檔案下載(一)

一、引言   大家都知道web程式設計的協議就是http協議,稱為超文字傳輸協議。在J2EE中我們可以很快的實現一個Web工程,但在C++中就不是非常的迅速,原因無非就是底層的socket網路編寫需要自己完成,上層的http協議需要我們自己完成,使用者介面需要我們自己完

Http伺服器實現檔案下載(三)

一、引言   在前2章的內容基本上已經講解了整個的大致流程。在設計Http伺服器時,我設計為四層的結構,最底層是網路傳輸層,就是socket程式設計。接著一層是請求和響應層,叫做Request和Response。在上一層是URL解析流程走向層。最頂層我設計為索引層。這一層主要多檔案時對檔案進行記憶體上的索引

struts2實現檔案下載功能

一、Demo介紹 基於struts2框架,實現多檔案的上傳和下載功能。 實現原理圖: 部分介面圖: 上傳成功及下載頁面: 二、主要程式碼 uploadFile.jsp:在form表單中包含一個文字框(上傳使用者的姓名)和兩個檔案上傳選項. <%@

JAVA通訊(1)-- 使用Socket實現檔案下載

客戶端 /** * 檔案上傳客戶端 * * @author chen.lin * */ public class UploadClient extends JFrame { /** * */ priva

使用jsp/servlet簡單實現檔案下載

public class UploadServlet extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException,

Spring 實現檔案下載

檔案上傳 upload.jsp: <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@ include file="

struts2實現檔案下載

一、單檔案上傳 1、檔案上傳條件: (1)請求方法必須是post (2)enctype的屬性值必須為multipart/form-data (3)提供一個檔案選擇域 2、檔案上傳jsp程式碼 <%@ page language="java" c

C# 之 FTP伺服器檔案下載(二)

        通過上一篇部落格《C# 之 FTP伺服器中檔案上傳與下載(一)》,我們已經建立好了一個FTP伺服器,並且該伺服器需要使用者名稱和密碼的驗證。今天我們來實現檔案的上傳。 首先,我們前臺需要一個FileUpload控制元件和一個Button控制元件 <

java實現檔案下載

           東風化宇,檔案上傳 一、對於檔案上傳,瀏覽器在上傳的過程中是將檔案以流的形式提交到伺服器端的,Servlet獲取上傳檔案的輸入流然後再解析裡面的請求引數是比較麻煩。 JSP程式碼,POST請求,表單必須設定為enctype="multipar

Http服務器實現文件下載()

讀取 版本 html 出現問題 type 函數名 range 讀取文件 都是 一、引言   歡迎大家來到和我一起編寫Http服務器實現文件的上傳和下載,現在我稍微回顧一下之前我說的,第一、二章說明說明了整體的HTTP走向,第三章實現底層的網絡編程。接著這一章我想給大家講的是

基於TCP伺服器檔案下載

** ## service.c ** #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/types.h> #include &l

SpringBoot下檔案下載實現

本文歡迎轉載,轉載請註明出處,謝謝~(作者:喝酒不騎馬 Colton_Null) from CSDN SpringBoot後臺如何實現檔案上傳下載? 最近做的一個專案涉及到檔案上傳與下載。前端上傳採用百度webUploader外掛。有關該外掛的

檔案下載的功能實現

檔案上傳 檔案上傳原理分析 1、檔案上傳的必要前提: 一、進行檔案上傳時只能使用post方式提交表單 二、表單必須新增一個屬性:enctype=”multipart/form-data” 三、用於上傳檔案的元素必須時 2、enctype屬性 作

java使用Jsch實現遠端操作linux伺服器進行檔案下載,刪除和顯示目錄資訊

1 package com.fline.aic.utils; 2 3 import java.io.BufferedReader; 4 import java.io.File; 5 import java.io.FileInputStream; 6 import jav

基於tobato的fastdfsspring boot整合實現檔案下載

專案結構: pom.xml檔案新增配置: <!-- fastdfs --> <dependency> <groupId>com.github.tobato</groupId> <artifactId>fastd

基於js-ipfs-api實現ipfs的檔案下載

配置本地的ipfs節點 # 初始化ipfs節點 ipfs init # 執行ipfs節點 ipfs daemon 使用js-ipfs-api呼叫ipfs服務 連線本地ipfs節點 const ipfsAPI = require('ip

Struts2實現檔案,多檔案下載(十)

    “兩個蝴蝶飛”特別喜歡"java1234知識分享網"小峰的實用主義,所以本文及其系列文章均是採用實用主義,從專案和程式碼的角度去分析。由於本人經驗有限,嘴皮子不溜,所以學術性,概念性,底層性的知識點暫時不做介紹。文章中有錯誤之處,歡迎拍磚和指點。特別感謝"java12