好用的http client庫CPP REST SDK

好用的http client庫CPP REST SDK

此處輸入圖片的描述

轉自: https://www.cnblogs.com/highway-9/p/6021238.html

前言

C++中http client庫本身就少,好用的就更少了,在瞭解微軟開源的CPP REST SDK庫之前,我知道的C++ http client庫有libcurl(這個是C語言的),Qt的QNetworkAccessManager,還有VC++ http client,Qt的QNetworkAccessManager庫我在開發CZPlayer的時候用來下載過音樂、專輯圖片和歌詞,不得不說Qt提供的API還是比較好用的,如果不涉及界面開發,難道我們在linux上就只能用libcurl,在windows上就用VC++的http client?答案是否定的,在絕望之際CPP REST SDK出現在我的眼前,CPP REST SDK是微軟開源的基於PPL的異步http client,網絡層使用的是Boost.Asio,跨平臺,並且支持json解析,在使用CPP REST SDK之前要確保你已經安裝了boost和openssl,下面是微軟官方提供的例子。

微軟官方例子

#include <cpprest/http_client.h>
#include <cpprest/filestream.h>

using namespace utility;                    // Common utilities like string conversions
using namespace web;                        // Common features like URIs.
using namespace web::http;                  // Common HTTP functionality
using namespace web::http::client;          // HTTP client features
using namespace concurrency::streams;       // Asynchronous streams

int main(int argc, char* argv[])
{
    auto fileStream = std::make_shared<ostream>();

    // Open stream to output file.
    pplx::task<void> requestTask = fstream::open_ostream(U("results.html")).then([=](ostream outFile)
    {
        *fileStream = outFile;

        // Create http_client to send the request.
        http_client client(U("http://www.bing.com/"));

        // Build request URI and start the request.
        uri_builder builder(U("/search"));
        builder.append_query(U("q"), U("cpprestsdk github"));
        return client.request(methods::GET, builder.to_string());
    })

    // Handle response headers arriving.
    .then([=](http_response response)
    {
        printf("Received response status code:%u\n", response.status_code());

        // Write response body into the file.
        return response.body().read_to_end(fileStream->streambuf());
    })

    // Close the file stream.
    .then([=](size_t)
    {
        return fileStream->close();
    });

    // Wait for all the outstanding I/O to complete and handle any exceptions
    try
    {
        requestTask.wait();
    }
    catch (const std::exception &e)
    {
        printf("Error exception:%s\n", e.what());
    }

    return 0;
}

上面的例子主要內容是訪問一個網站並將該內容保存在results.html裏面,這裏用到了微軟自家的PPL並行計算庫(還有一個是英特爾的TBB),該例子使用lambda表達式作爲異步回調的handler,打開文件流、請求、迴應和關閉文件流都是異步的,爲了方便起見,我們將該例子改爲同步方式。

使用同步方式

auto fileStream = std::make_shared<ostream>();
ostream outFile = fstream::open_ostream(U("results.html")).get();
*fileStream = outFile;

// Create http_client to send the request.
http_client client(U("http://www.bing.com/"));

// Build request URI and start the request.
uri_builder builder(U("/search"));
builder.append_query(U("q"), U("cpprestsdk github"));
http_response response = client.request(methods::GET, builder.to_string()).get();
// Write response body into the file.
response.body().read_to_end(fileStream->streambuf()).get();
fileStream->close().get();

使用同步方式的代碼比較清晰,可以看到每一個函數調用後面都會調用get函數,因爲CPP REST SDK是基於PPL的,所以在request、read_to_end、close等函數調用後都會返回一個Task對象,而Task裏面的get和wait函數是等待任務執行完成。

實戰

我之前在看別人博客的時候看到每一篇博客前都有一副圖,看起來比較好看,聽博主講他是使用bing的每日一圖,後來我也效仿,我在主題之家找到了自己比較喜歡的圖片,最開始自己寫博客的時候都是直接去主題之家一頁一頁的找看,然後再下載圖片,寫了幾篇博客之後感覺每次都要去下載圖片感覺有點low,所以我決定直接將主題之家的某類型的圖片都下載下來,這樣選圖片就方便多了,下面是我在主題之家抓圖的代碼。

#include <iostream>
#include <string>
#include <vector>
#include <fstream>
#include <memory>
#include <regex>
#include <cpprest/http_client.h>
#include <cpprest/filestream.h>
#include <cpprest/containerstream.h>

// 請求並解析url
bool get_result(const std::string& url, const std::string& pattern, std::vector<std::string>& vec)
{
    try
    {
        web::http::client::http_client client(web::uri(utility::conversions::to_string_t(url)));
        web::http::http_response response = client.request(web::http::methods::GET).get();

        concurrency::streams::stringstreambuf buffer;
        response.body().read_to_end(buffer).get();
        std::string& str = buffer.collection();
        
            // 使用C++11提供的正則表達式庫
        std::regex r(pattern);
        for (std::sregex_iterator iter(str.begin(), str.end(), r), end; iter != end; ++iter)
        {
            std::cout << iter->str() << std::endl;
            vec.emplace_back(iter->str());
        }
    }
    catch (std::exception& e)
    {
        std::cout << "Exception: " << e.what() << std::endl;
        return false;
    }

    return true;
}

// 獲取圖片
bool get_result(const std::string& url, std::string& picture)
{
    try
    {
        web::http::client::http_client client(web::uri(utility::conversions::to_string_t(url)));
        web::http::http_response response = client.request(web::http::methods::GET).get();

        concurrency::streams::stringstreambuf buffer;
        response.body().read_to_end(buffer).get();
        picture = buffer.collection();
    }
    catch (std::exception& e)
    {
        std::cout << "Exception: " << e.what() << std::endl;
        return false;
    }

    return true;
}

// 保存圖片
bool write_to_file(const std::string& file_path, const std::string& data)
{
    try
    {
        std::ofstream file;
        file.open(file_path, std::ios::out | std::ios::binary);
        if (!file.good())
        {
            return false;
        }
        file.write(data.c_str(), data.size());
        file.close();
    }
    catch (std::exception& e)
    {
        std::cout << "Exception: " << e.what() << std::endl;
        return false;
    }

    return true;
}

int main()
{
    // [1] 請求每一頁,將子頁面的url保存在sub_url_vec裏面
    std::vector<std::string> sub_url_vec;
    std::string pattern = "/desk/[0-9]+.htm";
    for (int i = 1; i <= 32; ++i)
    {
        // 創意主題
        std::string url = "http://www.51ztzj.com/dbizhi/category_27_" + std::to_string(i) + ".htm#content_anchor";
        std::cout << "Start get " << i << " page, url: " << url << std::endl;
        // 請求並解析url
        if (!get_result(url, pattern, sub_url_vec))
        {
            std::cout << "Get " << i << " page failed" << std::endl;
        }
    }

    // 最終的圖片url:http://img.51ztzj.com//upload/image/20130220/2013022014_670x419.jpg
    // [2] 將子頁面的圖片url解析出來放入picture_url_vec
    std::vector<std::string> picture_url_vec;
    pattern = "http://img.51ztzj.com//upload/image/.+/.+_670x419.jpg";
    for (std::size_t i = 0; i < sub_url_vec.size(); ++i)
    {
        std::string url = "http://www.51ztzj.com" + sub_url_vec[i];
        std::cout << "Start get " << i + 1 << " sub page, url: " << url << std::endl;
        // 請求並解析url
        if (!get_result(url, pattern, picture_url_vec))
        {
            std::cout << "Get " << i + 1 << " sub page failed" << std::endl;
        }
    }

    // [3] 最後遍歷picture_url_vec,然後一個一個的下載圖片
    for (std::size_t i = 0; i < picture_url_vec.size(); ++i)
    {
        std::cout << "Start download " << i + 1 << " picture, url: " << picture_url_vec[i] << std::endl;
        std::string picture;
        // 獲取圖片
        if (!get_result(picture_url_vec[i], picture))
        {
            std::cout << "Download " << i + 1 << " picture failed" << std::endl;
        }

        std::string file_path = "./download/" + std::to_string(i) + ".jpg";
        // 保存圖片
        if (!write_to_file(file_path, picture))
        {
            std::cout << "Write to file failed: " << i + 1 << std::endl;
        }
    }

    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章