Question

My Windows Phone application saves an object composed by some textual/numerical information (something like image capture date and so on) and a couple of JPEG image. For example and simplicity, a single file may be composed by the following data order:

from byte 0 to 3 -----> INT NUMBER
from byte 4 to 11 ----> TWO INTs DESCRIBING THE SIZE OF THE NEXT IMAGE
from byte 12 to X ----> FIRST JPEG IMAGE
from byte X+1 to X+7 -> TWO INTs DESCRIBING THE SIZE OF THE NEXT IMAGE
from byte X+8 to Y ---> SECOND JPEG IMAGE

I've implemented the saving method this way:

public bool SaveDataToMemory(string filename) 
{
    try
    {
        IsolatedStorageFile appStorage = IsolatedStorageFile.GetUserStoreForApplication();
        if (!appStorage.DirectoryExists(App.USER_FOLDER))
        {
            appStorage.CreateDirectory(App.USER_FOLDER);
        }

        if (appStorage.FileExists(filename))
        {
            appStorage.DeleteFile(filename);
        }

        IsolatedStorageFileStream fs = null;
        using (fs = appStorage.CreateFile(filename))
        {
            int thumbW = (int)this.thumbnail.PixelWidth;
            int thumbH = (int)this.thumbnail.PixelHeight;
            int thumbLength = thumbW * thumbH;

            int imageW = (int)this.imageSize.Width;
            int imageH = (int)this.imageSize.Height;
            int imageLength = imageW * imageH;

            byte[] byteToWrite;
            int bufferCount;

            byteToWrite = BitConverter.GetBytes(DATA_VERSION); // THE FIRST INT NUMBER
            fs.Write(byteToWrite, 0, byteToWrite.Length);

            byteToWrite = BitConverter.GetBytes(thumbW); // THE FIRST COUPLE OF INTs
            fs.Write(byteToWrite, 0, byteToWrite.Length);
            byteToWrite = BitConverter.GetBytes(thumbH);
            fs.Write(byteToWrite, 0, byteToWrite.Length);

            this.thumbnail.SaveJpeg(fs, thumbW, thumbH, 0, 75); // THE FIRST IMAGE (WriteableBitmap)


            byteToWrite = BitConverter.GetBytes(imageW); // THE SECOND COUPLE OF INTs
            fs.Write(byteToWrite, 0, byteToWrite.Length);
            byteToWrite = BitConverter.GetBytes(imageH);
            fs.Write(byteToWrite, 0, byteToWrite.Length);

            this.image.SaveJpeg(fs, imageW, imageH, 0, 92); // THE SECOND IMAGE
        }

        return true;
    }
    catch (Exception ex)
    {
        System.Diagnostics.Debug.WriteLine(ex);
        return false;
    }
}

The method should work, at least there are no exception thrown. Now the problem is when I try to load them back. I access the stream following the data order described before, I can retrieve and view the first image ("thumbnail") with WriteableBitmap.LoadJpeg, but when I continue in order to retrive the next integers they appear to be 0 and the second image fails to load with an exception.

public bool LoadDataFromMemory(string filename)
{
    try
    {
        IsolatedStorageFile appStorage = IsolatedStorageFile.GetUserStoreForApplication();
        IsolatedStorageFileStream fs = null;

        if (!appStorage.FileExists(filename)) return false;

        using (fs = appStorage.OpenFile(filename, FileMode.Open))
        {
            if (fs == null) return false;

            byte[] dataVersionBuf = new byte[sizeof(Int32)]; // FIRST INTEGER
            fs.Read(dataVersionBuf, 0, dataVersionBuf.Length);
            int dataVersion = BitConverter.ToInt32(dataVersionBuf, 0);

            System.Diagnostics.Debug.WriteLine("Data version:" + dataVersion);

            byte[] thumbSizeBuf = new byte[8];
            fs.Read(thumbSizeBuf, 0, thumbSizeBuf.Length); // SIZE OF THE NEXT IMAGE

            int thumbW = BitConverter.ToInt32(thumbSizeBuf, 0);
            int thumbH = BitConverter.ToInt32(thumbSizeBuf, 4);

            this.thumbnail = new WriteableBitmap(thumbW, thumbH);
            this.thumbnail.LoadJpeg(fs); // FIRST IMAGE

            // (this prints the correct information)
            System.Diagnostics.Debug.WriteLine("Loaded thumbnail " + this.thumbnail.PixelWidth + "x" + this.thumbnail.PixelHeight); 

            byte[] leftSizeBuf = new byte[sizeof(Int32) * 2];
            fs.Read(leftSizeBuf, 0, leftSizeBuf.Length); // <<--- PROBLEMS READING HERE!

            int imageW = BitConverter.ToInt32(leftSizeBuf, 0);
            int imageH = BitConverter.ToInt32(leftSizeBuf, 4);
            // imageW and imageH are equal to 0!!!!

            System.Diagnostics.Debug.WriteLine("Loading left " + imageW + "x" + imageH);

            WriteableBitmap wb = new WriteableBitmap(imageW, imageH);
            wb.LoadJpeg(fs); // <--- EXCEPTION HERE!

            return true;
        }
    }
    catch (Exception ex)
    {
        System.Diagnostics.Debug.WriteLine(ex);
        return false;
    }
}

The thrown exception is a System.ArgumentException: incorrect parameter. Am I implementing the wrong method to store two images in the same stream/file?

Was it helpful?

Solution 2

Done! Starting from Xenolightning's answer, first of all I save the JPEG data into a separate MemoryStream, this way I can get its final length with MemoryStream.Length. I write the length into the IsolatedStorageFileStream as a simple integer and after that I write the whole contents from the previous MemoryStream with MemoryStream.WriteTo().

// preparing thumbnail stream & length
ms = new MemoryStream();
this.thumbnail.SaveJpeg(ms, thumbW, thumbH, 0, 80);
thumbLength = (int)ms.Length;

// THUMB FILESIZE
byteToWrite = BitConverter.GetBytes(thumbLength);
fs.Write(byteToWrite, 0, byteToWrite.Length);

// THUMB DATA
ms.WriteTo(fs);

When loading back, I first read the file size data block, then the proper amount of data with fs.Read() is transferred to a MemoryStream, then it's converted to a bitmap object.

// THUMB FILESIZE
byteToRead = new byte[sizeof(Int32)];
fs.Read(byteToRead, 0, byteToRead.Length);
int thumbSize = BitConverter.ToInt32(byteToRead, 0);

// GET RAW DATA
jpegData = new byte[thumbSize];
fs.Read(jpegData, 0, thumbSize);

// CREATE IMAGE OBJECT
ms = new MemoryStream(jpegData);
this.thumbnail = new WriteableBitmap(thumbW, thumbH);
this.thumbnail.LoadJpeg(ms);

OTHER TIPS

It may be due to unexpected behaviour in the WriteableBitmap.LoadJpeg Method. It could over-run your stream, which may then cause the rest of your stream to be in an unpredictable position. I would suggest updating your format to add the FileSize of the jpeg (in bytes), and pull a MemoryStream out of your FileStream to pass to WriteableBitmap.LoadJpeg.

Something along the lines of: (there may be a compilation issue, I'm not at a dev machine to check)

//...
byte[] jpegSizeBytes = new byte[sizeof(Int32)]; // GET THE JPEG SIZE
fs.Read(jpegSizeBytes , 0, jpegSizeBytes.Length);
int jpegSize = BitConverter.ToInt32(jpegSizeBytes , 0);

byte[] jpegData = new byte[jpegSize];
fs.Read(jpegData, 0, jpegSize);

using(var ms = new MemoryStream(jpegData))
{
    this.thumbnail = new WriteableBitmap(thumbW, thumbH);
    this.thumbnail.LoadJpeg(ms); // FIRST IMAGE
}
//...

Use the same technique as above for loading the second image too.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top