REST equivalent for response.Content.ReadAsByteArrayAsync().Result

Jan 22, 2014 at 9:20 AM
Hello,

I am trying to translate some C# code to C++ and these lines are not working in any way I tried:
var response = ApiClient.SendAsync(request).Result;
/* Read the response */
var content = response.Content.ReadAsByteArrayAsync().Result;
/* Save response to disk */
using(FileStream fs = new FileStream(filename, FileMode.Create, FileAccess.Write))
      {
           fs.Write(content, 0, content.Length);
       }
I found many examples here, here, here but nothing can fit my problem

One of my non working version is this:
Concurrency::streams::istream bodyStream = response.body();
ReadStreamWriteFile(bodyStream);
The ReadStreamWriteFile method is this:
void ReadStreamWriteFile (Concurrency::streams::istream inStream)
{
container_buffer<std::string> inStringBuffer;
std::string bufferToWrite = "";
while(!inStream.is_eof())
{
inStream.read_line(inStringBuffer);
const std::string line = inStringBuffer.collection();
bufferToWrite = line;
}
std::ofstream fileStreamOut;
fileStreamOut.open ("response.txt", ios::binary );
fileStreamOut.write(bufferToWrite.c_str(), bufferToWrite.length());
fileStreamOut.close();
}
The response.txt is like this:
line1line2line3line4line5.....EOF 
and should be like this:
line1
line2
line3
line4
line5
......
EOF
Another non working version is this:
Concurrency::streams::istream bodyStream = response.body();
std::string bufferToWrite = "";
while (!bodyStream.is_eof())
{           
    std::cout << c;
    try{
        std::ofstream fileStreamOut;
        fileStreamOut.open ("response.txt", ios::binary );
        c = bodyStream.extract<unsigned char>().get();
        bufferToWrite +=c;
        fileStreamOut.write(bufferToWrite .c_str(), bufferToWrite .length());
        fileStreamOut.close();
    }
    catch(std::exception &e)
    {
        std::cout << "exception !!!!!!!" << e.what() << endl;
    }

}
The response.txt is the same.

Can someone please tell me how to use read_line or read for an istream?
Or how to detect the '\n' using
bodyStream.extract<char>.get() ?
Thanks,
Adrian
Jan 22, 2014 at 6:30 PM
Hi Adrian,

You have a couple of options. First let me explain why the first two you tried don't work. In the first one you are reading line by line with the read_line(...) method. This isn't going to include the whitespace for each newline. The method reasons until a new line, discards the new line and returns the string. If you appended a new line to each line you read, before writing out to the file stream that would work. In the second version the extract method is also skipping over whitespace.

Here are the two ways I recommend you take a look at. First take a look at our samples you can find them on CodePlex or if you used the MSI they are in a zip file that can be extracted. One of our samples BingRequest does almost exactly what you have here. It makes a GET request and saves the response body to a file. Here is the code:
    // Open a stream to the file to write the HTTP response body into.
    auto fileBuffer = std::make_shared<streambuf<uint8_t>>();
    file_buffer<uint8_t>::open(outputFileName, std::ios::out).then([=](streambuf<uint8_t> outFile) -> pplx::task<http_response>
    {
        *fileBuffer = outFile; 

        // Create an HTTP request.
        // Encode the URI query since it could contain special characters like spaces.
        http_client client(U("http://www.bing.com/"));
        return client.request(methods::GET, uri_builder(U("/search")).append_query(U("q"), searchTerm).to_string());
    })

    // Write the response body into the file buffer.
    .then([=](http_response response) -> pplx::task<size_t>
    {
        printf("Response status code %u returned.\n", response.status_code());

        return response.body().read_to_end(*fileBuffer);
    })

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

    // Wait for the entire response body to be written into the file.
    .wait();
Another option that would have the best performance would be to directly specify the file stream to be used as the incoming response stream. This means at the lowest level possible Casablanca will write the data directly to your specified stream saving any copies. The code looks like the following:
    http_client client(L"http://myhost");
    http_request request(methods::GET);
    // Fill in any other information like request URI....

    // Open file stream and set as response stream.
    auto filestream = concurrency::streams::file_stream<uint8_t>::open_ostream(L"response.txt").get();
    request.set_response_stream(filestream);

    // Make request and wait for entire body to arrive.
    http_response response = client.request(request).get();
    response.content_ready().wait();

    // Close file stream.
    filestream.close().wait();
Please note the above code blocking waiting on some asynchronous operations (.get() and .wait()) and exceptions are not handled.

If you still are having trouble let us know.
Thanks,
Steve
Jan 23, 2014 at 3:49 PM
Thank you very much Steve!

Both versions are working. I have done these days some approaches with (something like this):
fileBuffer = std::make_shared<streambuf<uint8_t>>();
and
filestream = concurrency::streams::file_stream<uint8_t>::open_ostream(L"response.txt").get();
but I used
Concurrency::streams::file_buffer
and
Concurrency::streams::file_stream
but it wasn't working because of my lack of knowledge in templates.

Another issue in my other approaches was not using the auto keyword.
If the server is sending different types of contents ("Content-Type") the filestream just "knows" what type it is (with auto)

I have to admit that I could not solve this problem by myself that fast.

Thanks again,
Adrian
Jan 23, 2014 at 5:32 PM
Hi Adrian,

I'm a little worried about some misunderstanding about what you said with the filestream "knows" what the type is. There is no magic going on here underneath the covers. All that is happening is the raw bytes sent that come across the network are being written into the file_stream. This is done regardless of what the Content-Type HTTP header field is set to. I just used the C++ auto keyword to avoid having to write the name of the file_stream type out again.

Steve