boost 实现http断点续传

// testc.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <fstream>
#include <boost/asio.hpp>
#include <boost/asio/ip/tcp.hpp>
#include <boost/algorithm/string.hpp>

using boost::asio::ip::tcp;

struct HttpResponse
{
public:
    explicit HttpResponse(){ clear();}
    std::string http_version;        // 版本
    unsigned int status_code;        // 状态码
    std::string status_message;    // 状态
    std::string header;                    // HTTP包头
    std::string body;           // HTTP返回的内容
    std::string content_type;
    std::string modify_time;
    unsigned int content_length;
    unsigned int total_length;
    unsigned int offset;

    void clear()
    {
        http_version.clear();
        status_code = -1;
        status_message.clear();
        header.clear();
        content_type.clear();
        modify_time.clear();
        content_length = 0;
        total_length = 0;
        offset = 0;
        body.clear();
    }
};

struct DownloadInfo
{
    DownloadInfo()
    {
        id = 0;
        url.clear();
        filename.clear();
        md5.clear();
        writesize = 0;
        continued = false;
        lasterr = 0;
        trycount = 0;
    }
    int id;
    std::string url;
    std::string filename;
    std::string md5;
    int writesize;
    bool continued;
    int lasterr;
    int trycount;
};

int  GetTempFileRange( const std::string& fn );
bool GetHttpFile(const std::string& szHost, const std::string& szParam);
bool AnalyseHeader(HttpResponse& result, std::string& packetString, int nEndHeader);
bool WriteFile( const std::string& fn, int rangeStart, std::string& packetString, DownloadInfo* d_diCurrent );

std::ofstream f_ofsSave;

int _tmain(int argc, _TCHAR* argv[])
{
    std::string szHost ("192.168.1.102");
    std::string szParam("/DoDo/aishen.mp4");

    GetHttpFile(szHost, szParam);

    return 0;
}

// 获取文件
bool GetHttpFile(const std::string& szHost, const std::string& szParam)
{
    // 创建下载信息
    DownloadInfo* d_diCurrent = new DownloadInfo();
    d_diCurrent->filename = "DownLoadFile";
    d_diCurrent->continued = true;

    if(d_diCurrent->continued) {
        d_diCurrent->filename = d_diCurrent->filename+std::string(".td");
    }

    try
    {
        boost::asio::io_service io_serv;

        // Get a list of endpoints corresponding to the server name.
        std::string szService ("http");
        std::string szIp = szHost;
        int i = szHost.find(":") ;
        if (i != -1)
        {
            szService = szHost.substr(i+1);
            szIp = szHost.substr(0, i);
        }
        tcp::resolver::query query(szIp, szService);

        tcp::resolver m_resolver(io_serv);
        // 创建SOCKET
        tcp::socket s_socket(io_serv); 

        tcp::resolver::iterator endpoint_iterator = m_resolver.resolve(query), end_it;

        // Try each endpoint until we successfully establish a connection.
        tcp::resolver::iterator it = boost::asio::connect(s_socket, endpoint_iterator);

        if(it == end_it)
            return false;

        boost::asio::streambuf request;
        {
            // 封装请求HTTP GET
            std::ostream request_stream(&request);        

            request_stream << "GET " ;
            request_stream << szParam << " HTTP/1.1\r\n";
            request_stream << "Host: " << szHost << "\r\n";

            request_stream << "Accept: */*\r\n";
            request_stream << "Pragma: no-cache\r\n";
            request_stream << "Cache-Control: no-cache\r\n";
            request_stream << "Connection: close\r\n";

            // 1. 是否开启断点续传, 如是则读取临时文件长度
            int rangeStart = 0;
            if (d_diCurrent->continued) {
                rangeStart = GetTempFileRange(d_diCurrent->filename);
                if (rangeStart) {
                    request_stream << "Range: bytes=" << rangeStart << "- \r\n";    
                }
                request_stream << "\r\n";
            }

            boost::asio::write(s_socket, request);

            boost::asio::streambuf response;
            std::ostringstream packetStream;
            try
            {
                // Read until EOF, writing data to output as we go.
                bool hasReadHeader = false;

                boost::system::error_code error;

                HttpResponse result;
                result.body.clear();

                while (boost::asio::read(s_socket, response,
                    boost::asio::transfer_at_least(1), error))
                {
                    packetStream.str("");
                    packetStream << &response;

                    std::string packetString = packetStream.str(); 

                    // 2. 是否已分析文件头
                    if (!hasReadHeader)
                    {
                        hasReadHeader = true;
                        // 取出http header
                        size_t nEndHeader = packetString.find("\r\n\r\n");
                        if(nEndHeader == std::string::npos)
                            continue;

                        if(!AnalyseHeader(result, packetString, nEndHeader)) {
                            return false;
                        }
                    }

                    // 3. 写入文件
                    WriteFile(d_diCurrent->filename, rangeStart, packetString, d_diCurrent);
                }

                // 4. 关闭文件
                f_ofsSave.close();

                // 5. 文件改名
                std::string fn = "DownLoadFile.db";
                rename(d_diCurrent->filename.c_str(), fn.c_str());

                if (error != boost::asio::error::eof)
                    throw boost::system::system_error(error);

            }
            catch (std::exception& e)
            {
                std::cout << "Exception: " << e.what() << "\n";
                return false;
            }
        } 
    }
    catch(std::exception& e) {
        std::cout << "Exception: " << e.what() << "\n";
        return false;
    }

    return true;
}

// *****************************************************
// 分析文件头
// *****************************************************
bool AnalyseHeader(HttpResponse& result, std::string& packetString, int nEndHeader)
{
    result.header = packetString.substr(0, nEndHeader);

    // Check that response is OK.
    std::istringstream response_stream(result.header);
    response_stream >> result.http_version;
    response_stream >> result.status_code;

    std::string strLine;
    std::getline(response_stream, strLine);
    while (!strLine.empty())
    {
        if (strLine.find("Content-Type:") != std::string::npos)
        {
            result.content_type = strLine.substr(strlen("Content-Type:"));
            result.content_type.erase(0, result.content_type.find_first_not_of(" "));
        }
        if (strLine.find("Content-Length:") != std::string::npos)
        {
            result.content_length = atoi(strLine.substr(strlen("Content-Length:")).c_str());
            result.total_length = result.content_length;
        }
        if (strLine.find("Last-Modified:") != std::string::npos)
        {
            result.modify_time = strLine.substr(strlen("Last-Modified:"));
            result.modify_time.erase(0, result.modify_time.find_first_not_of(" "));
        }    
        if (strLine.find("Content-Range: bytes") != std::string::npos)
        {
            std::string tmp = strLine.substr(strlen("Content-Range: bytes"));
            result.offset = atoi(tmp.substr(0, tmp.find('-')).c_str());
            int ipos = tmp.find('/');
            int ivalue = 0;
            if (ipos != std::string::npos)
            {
                ivalue = atoi(tmp.substr(ipos+1).c_str());
            }
            if (ivalue) 
                result.total_length = ivalue;
        }
        strLine.clear();
        std::getline(response_stream, strLine);
    }

    if ( result.http_version.substr(0, 5) != "HTTP/")
    {
        std::cout << "Invalid response\n";
        return false;
    }
    if (result.status_code != 200 && result.status_code != 206)
    {
        std::cout << "Response returned with status code " 
            << result.status_code << "\n";
        return false;
    }

    packetString.erase(0, nEndHeader + 4);

    return true;
}

// ************************************************************** 
// 获取临时文件大小
// ************************************************************** 
int GetTempFileRange( const std::string& fn )
{
    int rangeStart = 0;
    std::ifstream ifs;
    ifs.open(fn, std::ios_base::in | std::ios_base::binary );
    if (ifs.is_open()) {
        ifs.seekg(0, std::ios::end);
        rangeStart = ifs.tellg();
    }
    ifs.close();

    return rangeStart;
}

// **************************************************************
// 写入文件
// **************************************************************
bool WriteFile( const std::string& fn, int rangeStart, std::string& packetString, DownloadInfo* d_diCurrent )
{
    if (!f_ofsSave.is_open())
    {
        if (d_diCurrent->continued) 
        {
            f_ofsSave.open(fn, std::ios_base::out | std::ios_base::binary | std::ios_base::app );
            if (f_ofsSave.is_open()) 
            {
                f_ofsSave.seekp(rangeStart);
                d_diCurrent->writesize += rangeStart;
                //int range = f_ofsSave.tellp();
            }
        }
        else
        {
            f_ofsSave.open(fn, std::ios_base::out | std::ios_base::binary | std::ios_base::trunc );
        }

        if (!f_ofsSave.is_open()) 
        {
            return false;
        }

        d_diCurrent->writesize = 0;
    }

    try {
        f_ofsSave.write(packetString.c_str(), packetString.length());
        d_diCurrent->writesize += packetString.length();
    }
    catch (std::exception &e)
    {
        return false;
    }

    std::cout << " write size = "<<d_diCurrent->writesize<<"\n";

    return true;
}

 

posted @ 2015-02-10 20:22  廖先生  阅读(1198)  评论(0编辑  收藏  举报