views:

72

answers:

3

Hello,

I'm pretty close to losing my head here ;)

I'm developing a service that uses gsoap. I would like to return a mime response. I have everything working, but when reading binary files, all kind of files like jpeg, pdf, etc... contains the \0 char several times over the data (if opened with notepad can see a lot of NUL).

So any code for reading a raw file fails miserably once it finds the end-of-file char. I have tried to replace the \0 but the file becomes incorrect to display.

I have also tried several methods including the example that comes with gsoap.

So resuming,

fstream generic code doesn't work.

for (i = 0; i < MAX_FILE_SIZE; i++)
    { if ((c = fgetc(fd)) == EOF)
        break;
      image.__ptr[i] = c;
    } 

doesn't work also

QFile::ReadAll works but when converting QString to char* the array is trimmed in the first NUL.

So, which is the best aproach to read an entire binary file? Its crazy how sometimes C++ at the basic.

Thanks in advance.

I have tried this as retnick suggested below

    UrlToPdf urlToPdf;  
urlToPdf.getUrl(&input, &result);  

QByteArray raw = urlToPdf.getPdf(QString(result.data.c_str()));  

int   size      = raw.toBase64().size();  
char* arraydata = new char[size];  
strcpy(arraydata, raw.toBase64().data());  

soap_set_mime(this, "MIME_boundary", NULL);  
if(soap_set_mime_attachment(this, arraydata, size, SOAP_MIME_BASE64, "application/pdf", NULL, NULL, NULL))  
{  
    soap_clr_mime(this);  

    soapMessage = this->error;  
}  

but no luck... the mime response is bigger than the actual file...

David G Ortega

+1  A: 

to read binary files use fread() Once you read it treat it as an array of bytes not as a string. No string functions allowed.

EDIT: The gSOAP documentation section 14.1 explains how to send MIME attachments. I only refer to the relevant function (please read it all).

  int soap_set_mime_attachment(struct soap *soap, char *buf_ptr, size_t buf_size, 
        enum soap_mime_encoding encoding, 
        const char *type, const char *id, 
        const char *location, const char *description);

char *buf_ptr is your buffer. size_t buf_size is the length of your buffer.

So just do your QFile::ReadAll.

this gives you back a QByteArray. The QByteArray has the method

QByteArray QByteArray::toBase64 () const

this will return a

 QByteArray base64image = QByteArray::toBase64(rawImage);    

so now just do

soap_set_mime(soap, "MIME_boundary", "<[email protected]>"); 
/* add a base64 encoded image (base64image points to base64 data) */ 
soap_set_mime_attachment(soap, 
        base64image.data(), base64image.size(), 
        SOAP_MIME_BASE64, "image/jpeg", 
        "<[email protected]>", NULL, NULL); 

I have not tested this but should be close to finished.

renick
Hi renick, fread() doesn't work for me, I have tried almost any implementation. Do you mind if you point me to a piece of code?Thanks a lot
David G Ortega
The piece of sample code provided in the fread() hyperlink above should read your whole file and put it in a buffer. Once you have that buffer - just stay away from string manipulation functions (anything in <string.h> that starts with `str`). You can use the `mem` functions (memcpy() etc) because they totally ignore the '\0'
renick
Hi renick, I have tried this... no luck... I working with pdfs and the output is bigger and the pdf is corrupt... any idea why?
David G Ortega
Does it work with small files correctly ? Try first with image smaller than 64K.
renick
A: 

QFile::ReadAll works but when converting QString to char* the array is trimmed in the first NUL.

Are you sure it's actually trimmed or you just can't print/view the array in the debugger [since C-style strings are 0 terminated]?

If the QString itself is not enough for your needs you may want to convert it to a std::vector or similar using the range constructor or range assign, you'll have lots less grief towards the how much data the container holds.

EDIT: Here's some sample code for fstream reading from a binary file:

std::ifstream image( <image_file_name>, std::ios_base::in | std::ios_base::binary );
std::istream_iterator< char > image_begin( image ), image_end;
std::vector< char > vctImage( image_begin, image_end ); 

The std::ios_base::binary is the most important part of the thing (similar to fopen/fread ["rb"] & probably QFile has something similar)

Also posting some sample code usually helps in getting the right answer.

HIH

Eugen Constantin Dinca
Hi Eugen, thanks for the reply. I'm quite skillful at Qt and I have my own debugger for this king of apps since I'm mostly developing scrappers, bots and hybrid apps using C++ and javascript using QtScript and webkit. I have seen in my debugger that the data is holded by QString without any problem but using any QByteArray conversion or toStd(W)String trims the data.The problem is that gsoap uses only char* at mime response... any C++ treatment using std or ANSI is not working... at least for me at this moment ;)Any idea?
David G Ortega
I seem to detect a confusion between char* and C strings in this reply. char* is a string only by convention. If you respect the '\0' then it is a C string . Otherwise it is just an address to a memory buffer. So what I suggest is that you avoid using code that respects the convention. Your code(or gSOAP) has to do MIME conversion at some point. There must be a function for that. Can you just pass the pointer to the memory buffer to this function without any translation at all ?
renick
About the confusion part: I was trying to see how it was determined that when converting to char* the array got trimmed. If you view in debugger or print or do a strlen on a char* it will be treated like a C string and hence may appear trimmed. David addressed that part.The open the file as binary part seemed important to me since you may get EOL conversions when reading - and now that I think about it don't see how that could be seen as a trim to the first \0...I totally agree that using string functions with 'bunch of chars' data will result in some unexpected behavior.
Eugen Constantin Dinca
Hi renick, I'm not confused about char* and C strings ;) I simply remark that any conversion done with QString trims the data.Here is a piece of code (sorry I don't know how to format):<code>QString data = urlToPdf.getPdf(QString(result.data.c_str())); char *arraydata = new char[strlen(data.toStdString().c_str())]; strcpy(arraydata, data.toStdString().c_str()); soap_set_mime(this, "MIME_boundary", NULL); if(soap_set_mime_attachment(this, arraydata, strlen(arraydata), SOAP_MIME_BINARY, "application/pdf", NULL, NULL, NULL))</code>
David G Ortega
If I save the content of the QString data with QFile all the data is ok, but the data passed to arraydata is trimmed with the first \0.I have tried fread also and breaks in the first \0
David G Ortega
Please see my edited main answer above. You cannot use string functions with content that is of binary nature (contains '\0'). This applies to Qt also. So no QStrings allowed either. As you see I am converting a QByteArray to base64 directly.
renick
hi renick i have edited my post. toBase64 does not work as expected
David G Ortega
A: 

Hi!!

I have the solution for this... As renick suggested I tried his idea but it failed without undestanding it so much... From a logical point of view recnick was right... bat the truth is that any king of string manipulation using QT QByteArray, std or mem is going to stop when findind the first \0 char, Qt QString can do it without problems but when converting it to c string (char*) the data will be again trimmed with the first \0

I found that using QDataStream::readRawData reads the file into a char* given the size to read. So thats how I accomplished the deal...

QFile file("test.pdf");
file.open(QIODevice::ReadOnly);

int         size   = file.size();
char*       buffer = new char[size];    
QDataStream stream(&file);
stream.readRawData(buffer, size);

soap_set_mime(this, "MIME_boundary", NULL);
if(soap_set_mime_attachment(this, buffer, size, SOAP_MIME_BINARY, "application/pdf", NULL, NULL, NULL))
{
    soap_clr_mime(this);

    soapMessage = this->error;
}

Note that in the line

if(soap_set_mime_attachment(this, buffer, size, SOAP_MIME_BINARY, "application/pdf", NULL, NULL, NULL))

I'm still using the size var instead of doing sizeof(buffer) or any other aproach since this one is going to trimm again the data qhen finding the first \0...

Hope this helps...

David G Ortega

David G Ortega