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?
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);