1

I created a simple client and server using CPPRESTSDK. The client sends an image and on the server, I save the incoming file and then start doing the processing phase afterwards.

Now I want to check if the sent file is actually an image file and not some other random file. For that, I check the first few bytes of the file and this allows me to know what I'm dealing with.
This is the snippet responsible for saving the file:

//...
auto fileStream = std::make_shared<Concurrency::streams::ostream >();
Concurrency::streams::ostream outFile = Concurrency::streams::fstream::open_ostream(tmpFileName).get();
*fileStream = outFile;
uintmax_t bytesRead = 0;
int ret = 1;
bool isChecked = false;
while (ret > 0)
{
    ret = request.body().read(fileStream->streambuf(), this->READ_CHUNK).get();
    bytesRead += ret;
    
    if (!isChecked)
    {
        isChecked = true;
        auto streamBuffer = fileStream->streambuf();
        unsigned char byteBuffer[4];
        
        for (int i = 0; i < 4; i++)
        {
            byteBuffer[i] = (unsigned int)streamBuffer.bumpc().get();
            std::cout << byteBuffer[i] << ", ";
        }
        // reset the read pointer
        //streamBuffer.seekpos(std::streampos(), std::ios::in);
        // check and see if this is indeed an image file
        std::string imgFormat = Utils::Misc::GetImageType(byteBuffer);
        if (imgFormat == "")
        {
            fileStream->close().get();
            auto msg = U("Unsupported file has been sent! Expected an image file such as a JPEG, PNG, OR BMP");
            this->ReplyMessage(request, status_codes::BadRequest, json::value::string(msg));
            return;
        }
    }    
    ucout << "--" << bytesRead << U(" bytes read so far[")
          << Utils::Misc::GetFileSize(bytesRead , Utils::Misc::FileSizeTypes::KByte)
          << U(" KB]") << std::endl;
    if (bytesRead > MAX_FILE_SIZE)
        break;
}
// Close the file.
fileStream->close().get();
// ...

This clearly fails as the fileStream is an ostream and its can_read returns false thus bumpc() doesn't work. So how should I be going about this? How can I access the underlying byte buffer and see what was received so far?

Update

Using the request's streambuf() also doesn't yield any success as the file pointer seems not to be moveable, thus doing something like seekpos(std::streampos(0), std::ios::in) will not reset it to the beginning of the buffer. What am I missing here?

halfer
  • 19,824
  • 17
  • 99
  • 186
Hossein
  • 24,202
  • 35
  • 119
  • 224

1 Answers1

1

You need to open the file stream for both reading and writing, by explicitly passing the mode parameter to the open_ostream function. If you don't specify this parameter, it defaults to std::ios_base::out.

Concurrency::streams::fstream::open_ostream(tmpFileName, std::ios_base::in | std::ios_base::out).get();

Then the filestream should be created as readable, and you should be able to write the first chunk, seek back to the start of the file, read four bytes, then seek forwards to the end of the first chunk.

An alternative, possibly cleaner approach would be to read the first chunk to a stringstream, parse the image format from the first four bytes of that, and then (if the image format is valid and the file is to be saved) create the filestream, write the contents of the stringstream to the filestream, and then continue writing the remainder of the file chunks straight to the filestream.

David Scarlett
  • 3,171
  • 2
  • 12
  • 28
  • Thanks a lot. really appreciate it. But how exactly do I read into a ```stringstream_t```? the read() method expects a ```streambuf```. – Hossein Mar 07 '21 at 03:14
  • 1
    `stringstream` is a typedef for `container_stream>`. So if it needs `uint8_t` (suggesting it is using binary data rather than text), try replacing the `stringstream` with `container_stream>`. – David Scarlett Mar 07 '21 at 05:19
  • 1
    See the C++ Rest API concurrency streams documentation for reference: https://microsoft.github.io/cpprestsdk/namespace_concurrency_1_1streams.html – David Scarlett Mar 07 '21 at 05:20
  • I'm facing a weird issue in which even after doing `fileStream->close();` the handle doesn't get closed and I can't delete the file afterward. If I only use ```std::ios::out```, its fine, but it won't work with the two modes combined together as we have here! – Hossein Mar 09 '21 at 07:11
  • You do have some weird logic happening with `fileStream` and `outFile`, in which you construct a null `ostream` (in `fileStream`), then copy assign `outFile` to it. It might be that you have two handles to the file open, one in `fileStream` and another in `outFile`, and you've only closed one of them. Why are you doing this copy? – David Scarlett Mar 09 '21 at 11:33
  • That was a typo, I fixed that and still faced the same behavior. its strange as this doesn't happen with I only use the ```ios::out```, or even ```ios::in``` individually, but together it acts like this. – Hossein Mar 09 '21 at 12:04
  • 1
    Turns out this is a ```cpprestsdk``` bug. posted an issue on their repo. hopefully its fixed soon – Hossein Mar 10 '21 at 06:09