Saving Images and Binary Files to a Database with Delphi 2 - Checking the First 4 Bytes or Magic Numbers
(Page 4 of 5 )
In the above code we are saving the data to a file with the '.dat' extension. You could save it with a '.bmp' extension if you are sure that it is a bitmap. If you are not sure but save it as a bitmap when the actual data is of the JPEG type or any other type, the saved file will be of little or no use.
I must confess that I was stuck at this juncture for some time. I was aware that files carry their signatures with them which can be extracted on the byte level. In fact Unix systems do not require extensions of a file name since they are handled according to the "magic number" found in the file's header. It is common knowledge that the first 16 bytes of a JPEG image contains information on the data it carries but I did not know the values for which I should be searching. Opening a few JPEG files in a Hex Editor I observed that the first 4 bytes of all of them were identical and and the values were "FFD8 FFE0." A quick search on the web revealed that this was the magic number of the files with '.jpg' extensions. I tried opening different file formats in Hex Editor and looking for their magic numbers; almost all files of a similar type had the same exact first 4 bytes except bitmaps, which had only 2 identical bytes.
Now that we know how to identify the file format from raw binary data, let us focus on how we can use the information to retrieve and save data to a file. Earlier we used TFileStream to save the file to database but it cannot be used to retrieve data from the database since the constructor of TFileStream requires a file as parameter. Remember, each descendant of the TStream class can read and write only to a specific type of media. You can create an instance of a TBlobStream or TADOBlobStream using the Blob field as the constructor parameter, and create an instance of a TFileStream and copy data from the BlobStream, or use TBlobField's "SaveToStream" method to save data to an instance of TMemoryStream. TMemoryStream can be used to store data in a dynamic memory buffer and provides file-like access capabilities. Furthermore, its constructor requires no parameters, which suits us fine at the moment.
So let's get back to coding. I have already demonstrated how to connect to the database and retrieve one or more recordsets by executing a SQL statement through the command text property of the ADODataSet component. So the next step would be to create an instance of TMemoryStream by calling its constructor. Since we are creating an object it will be necessary to put the code dealing with the object in guarded try/finally blocks so that the instance is freed even if an exception is raised in the try block. Read my previous article Using Try and Finally to Help Prevent Memory Leaks During Dynamic Object Creation in Delphi to know why try/finally statements are important during object creation. After the TMemoryStream object has been created we would want to assign data to it by calling TBlobField's SaveToStream method while passing the Field name as the parameter.
TblobField(ADODataSet1.FieldByName('Image')).SaveToStream(MStream);
Now comes the interesting part which is comparing bytes to known magic numbers of different file types. It would be a good idea to declare the variables as the WORD datatype just to make sure that exactly 2 bytes are copied from the stream. The Seek method moves the cursor to the specified position in the stream; in our case we would like to set the cursor at the beginning so it will be necessary to specify the origin offset of Seek method as "soFromBeginning."
To read bytes from the stream to the variable, call the Read method with variable Sig1 as the buffer and Count as 2. The current position of the cursor should now be incremented by 2 but to make doubly sure I will set the cursor position to 2 and read the next 2 bytes into another WORD variable, Sig2. You can put the code that checks for the magic number in a loop to recursively parse through all of the data, but every file I opened in Hex Editor had their magic number in the beginning so I am interested in reading the first four bytes only.
Now Intel machines use a little Endian format to read bytes. What this actually means is that the bytes are read backward; FFD8 become D8FF. So we will convert our hex magic numbers to read backward and then compare them with the values of the variables. When a match is found you can save it to the file format it matches and then use ShellExecute to launch it with the associated application, or you can load it directly from stream to one of the components if it supports the format. For instance, if the data type is found to be of the bitmap type you can load it in the image component by calling the method
Image1.Picture.Bitmap.LoadFromStream (MStream);
Or you can create a JPEG object and copy the data from the stream and load it on Image component. In fact you can do a lot of things with it depending on your requirements.
Next: Code >>
More Delphi-Kylix Articles
More By Danish Ahmed