0

In C++ (Visual C++ MFC) i have char * which is came from database. This is actually a picture. PostgreSQL returns it as char * because no byte[] in C++ (As for as i know -yet-:))

The thing is, i try to write that image like this one:

ofstream myFile ("C:\\picture.jpg", ios::out | ios::binary);
myFile.write(contents, size);
myFile.close();

It output like:

\xffd8ffe000104a46494600010101006000600000ffe1005a4578696600.......

I tried to change contents to reinterpret_cast<char *>(&contents) then i got few binary data like but just few. The rest of them is not in the file.

I also tried this one:

fstream binary_file("C:\\picture.jpg", ios::out | ios::binary | ios::app);
binary_file.write(reinterpret_cast<char *>(&contents),size);
binary_file.close();

For both with reinterpret_cast<char *>(&contents) or without it. Still got few byte data in the file. No more.

I also tried to change size. Size came from postgresql's PQgetlength method so it is true for sure. (Can't be wrong right?)

I finally give size myself and said to C++ that it is 5000. It output binary data with 5.000 but the fact is, it does not meet with original file. It starts with "h" and then something different...

I also tried to load this data to Chilkat's ByteArray and then write with its oen file access method. Still got same result with \xfdd....

So, what is the main goal? What am i missing here? Any help will be appreciated.

Edit: It is char *. Sorry for misunderstanding.

Conclusion: I choose Craig Ringer*'s solution due to my needs. But due to this question's nature i choose H2CO3's answer as an accepted answer.

xangr
  • 879
  • 14
  • 28
  • What is the type of `contents`? If it is `const char*` or even `char*`, writing `reinterpret_cast(&contents)` is completely broken. Don't even bother trying to get it working, and get rid of the thought process that led to it. – R. Martinho Fernandes Apr 02 '13 at 09:05
  • I fixed the question. `PQgetvalue` returns `char *` – xangr Apr 02 '13 at 09:08
  • Have you actually checked what your contents actually contain before writing to file? Maybe this is what you get from DB. – marcinj Apr 02 '13 at 09:09
  • @xangr it doesn't matter. The attempted solution is still broken and will never work. Make a note of why you thought it would work and never think that again. You should explain what you mean by "It ouutput like: ...". Is that what you get in the picture file? Something else? What is in the data that `contents` points to? – R. Martinho Fernandes Apr 02 '13 at 09:10
  • @marcin_j. I am newbie at C++. This is what i want to do. I want to make sure that picture is return from DB successfully. I checked DB with pgAdmin. Yes, Picture stands there. I can export it. – xangr Apr 02 '13 at 09:11
  • @R.MartinhoFernandes there is a picture in my DB and it returns as `char *`. All i want to do is save this data as file to disk. What i got is if i open my JPG file with notepad i see `\xfdd...` string instead of data. So picture is broken. – xangr Apr 02 '13 at 09:13
  • @xangr you can check if your writing code works properly by writing test data from some test array, if it works ok, then forget about finding bug in your writing code, and look into how you get your data from DB – marcinj Apr 02 '13 at 09:13
  • The problem is in getting the data from the database. – R. Martinho Fernandes Apr 02 '13 at 09:25
  • I write a little test code and it works. It seems that it came from database as broken somehow. I will figure it out. Thanks for the help everyone. – xangr Apr 02 '13 at 09:42

4 Answers4

3

If contents is really a char *, and not an array, then the problem is that you're passing the address of the pointer itself. And then you're trying to write the pointer value into the file. Pass contents (instead of &contents) to fstream::write().

  • Sorry for misunderstanding. `PQgetvalue` returns `char *`. I tried both. `contents` writes `\xfdd..` other one returns few data only. – xangr Apr 02 '13 at 09:09
  • 1
    @xangr That is strange. You're missing something. Either in your error description is what I misunderstand, or you overlooked something else, but this way it should work. –  Apr 02 '13 at 09:13
  • 1
    @H2CO3 Just FYI, what's happening is that Pg returns a hex-escaped string for `bytea` unless you explicitly ask for raw binary, so the protocol remains as plain text by default. Not all client libraries and applications understand the binary protocol so it's something they have to ask for if they want it. You normally use `libpqtypes` or just strip the leading `\x` off and decode the hex octets. – Craig Ringer Apr 02 '13 at 10:24
  • @CraigRinger So, that's basically an API question? "Use the right library for the right purpose"? I am asking because I've never used postgreSQL and I have no idea what its programming interface provides. SQLite3 gives you access to raw binary data ("blobs") when appropriate and it never tries to escape it. One thing is sure, though: SQLite is not designed for networking... –  Apr 02 '13 at 10:26
3

As far as I can see your output is messed up

\xffd8ffe000104a46494600010101006000600000ffe1005a4578696600...

this is no valid hex-notation. You would need something like this

\xff\xd8\xff\xe0\x00...
bash.d
  • 13,029
  • 3
  • 29
  • 42
  • \xDEADBEEF is exactly how PostgreSQL's `hex` format for `bytea` works; this is normal, it just isn't a valid C source level hex escape. You can manually convert to bytes by chopping off the leading `\x` and processing each char pair into a byte. – Craig Ringer Apr 02 '13 at 10:11
3

Use libpqtypes. It takes care of bytea conversion and all sorts of other higher level data type handling that isn't built in to libpq proper.

It's possible to use binary transfer mode in libpq, but honestly it's going to be much simpler to use libpqtypes and let it deal with all that. You really don't want to deal with binary transfer of dates and other custom format values, so if you do use binary transfer you should generally specify binary mode only for the bytea column(s).

Craig Ringer
  • 307,061
  • 76
  • 688
  • 778
  • Wonderful extension! Thanks for letting me know. BTW, please read my other comment. I figured it out how to handle this issue. – xangr Apr 02 '13 at 10:18
  • Just want to let you know that i am managed to build it as static lib and using it actively. Thanks for bringing this to my attention. – xangr Apr 03 '13 at 15:23
2

First, your output looks like the start of a good, valid JPG file converted to the text with some self-made procedure. Some database developers don't like storing binary data in BLOBs, and the native PostreSQL format for storing binaries in text fields, bytea, is rather inconvenient. So many developers use Base64, UUENCODE, or self-made solutions.

And your first snippet looks OK. You gave us no definitions of contents and size, but if they are what they seem, it should work. It should output the contents without any alterations. So, to make sure the problem is in the contents, start the debugger, set a breakpoint at myFile.Write and look at contents variable. Most probably it will contain the same "\xffd8ffe000" instead of binary data.

If that is so, you need to manually reconvert this text data.

Mike Tyukanov
  • 579
  • 4
  • 10
  • 1
    I did debug as you said. Interestingly result is same. (I mean broken). And then i realized that it fetch it as `TEXT` but **not** `BINARY`. So, i digg the documentation and found that QueryExecuter has a different alternative. Which is built for `BINARY`!!!!! How did i missed that! I usually first check documentations ( which is i did but didnt realized it is for binary). So, binary data came correctly and write is done. It is working now. Also, thanks for stopping by and giving your idea to me. I really appreciated it. :) – xangr Apr 02 '13 at 10:17
  • @xangr Binary mode is available and is useful, but I recommend avoiding it for everything except `bytea`. You can specify binary- or text-mode on a column by column basis in queries. – Craig Ringer Apr 02 '13 at 10:21