I have found a class from the link ImageCaching
but when i load from isolated storage i got an exeption "System.InvalidOperationException"
here is my code
public static object DownloadFromWeb(Uri imageFileUri)
{
WebClient m_webClient = new WebClient(); //Load from internet
BitmapImage bm = new BitmapImage();
m_webClient.OpenReadCompleted += (o, e) =>
{
if (e.Error != null || e.Cancelled) return;
WriteToIsolatedStorage(IsolatedStorageFile.GetUserStoreForApplication(), e.Result, GetFileNameInIsolatedStorage(imageFileUri));
bm.SetSource(e.Result);
e.Result.Close();
};
m_webClient.OpenReadAsync(imageFileUri);
return bm;
}
public static object ExtractFromLocalStorage(Uri imageFileUri)
{
byte[] data;
string isolatedStoragePath = GetFileNameInIsolatedStorage(imageFileUri); //Load from local storage
if (null == _storage)
{
_storage = IsolatedStorageFile.GetUserStoreForApplication();
}
using (IsolatedStorageFileStream sourceFile = _storage.OpenFile(isolatedStoragePath, FileMode.Open, FileAccess.Read))
{
// Read the entire file and then close it
sourceFile.Read(data, 0, data.Length);
sourceFile.Close();
BitmapImage bm = new BitmapImage();
bm.SetSource(sourceFile);///here got the exeption
return bm;
}
}
so that i can't set the image.
I'm using the converter you mentioned and it works but you modified the method.
Your ExtractFromLocalStorage method isn't the same. You close your stream before to use it with the SetSource method.
Here is the original method code:
private static object ExtractFromLocalStorage(Uri imageFileUri)
{
string isolatedStoragePath = GetFileNameInIsolatedStorage(imageFileUri); //Load from local storage
using (var sourceFile = _storage.OpenFile(isolatedStoragePath, FileMode.Open, FileAccess.Read))
{
BitmapImage bm = new BitmapImage();
bm.SetSource(sourceFile);
return bm;
}
}
Related
I'm developing a small application which will get RAW image files, convert them to low quality JPEG's then load those JPEG's as thumbnails into a Grid.
My problem: I am having issues with the UI getting blocked while the images are being converted. I am dynamically adding controls to host those images in the grid just after the conversion is taking place for each image. Also I am binding those images to my Image control's Source with my ControlProperties ViewModel in code-behind.
My coding:
Here I am creating a new instance of my ControlProperties view model and inside I am doing the image conversion at the ImageSource.
cp = new ControlProperties()
{
ImageId = controlCount += 1, ImageSource = ThumbnailCreator.CreateThumbnail(imagePath)
};
My question:
Seeing as the images take a while to load, I am in need to get full control of my UI while they are being converted and added into my grid, but I am not getting it right at all. Can someone please help me with some advice or coding snippets to get me going please?
My ThumbnailCreator class
using System;
using System.IO;
using System.Threading.Tasks;
using System.Windows.Media.Imaging;
namespace SomeProjName
{
public class ThumbnailCreator
{
private static string imageLocation;
private static int currentImage;
public static BitmapImage CreateThumbnail(string oldImagePath)
{
ConvertHighQualityRAWImage(oldImagePath);
if (imageLocation != string.Empty && imageLocation != null)
return OpenImage(imageLocation);
else return null;
}
//Creates low quality JPG image from RAW image
private static void ConvertHighQualityRAWImage(string oldImagePath)
{
BitmapImage image = new BitmapImage(new Uri(oldImagePath));
var encoder = new JpegBitmapEncoder() { QualityLevel = 17 };
encoder.Frames.Add(BitmapFrame.Create(image));
using (var filestream = new FileStream(GetImageLocation(), FileMode.Create))
encoder.Save(filestream);
image.UriSource = null;
image.StreamSource = null;
image = null;
GC.WaitForPendingFinalizers();
GC.Collect();
}
//Returns low quality JPG thumbnail to calling method
private static BitmapImage OpenImage(string imagePath)
{
BitmapImage image = new BitmapImage();
image.BeginInit();
image.DecodePixelWidth = 283;
image.CacheOption = BitmapCacheOption.OnLoad;
image.UriSource = new Uri(imagePath, UriKind.Relative);
image.EndInit();
DeleteImage();
return image;
}
private static string GetImageLocation()
{
imageLocation = Directory.CreateDirectory(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "thumbnails")).FullName + GetCurrentImage();
return imageLocation;
}
private static string GetCurrentImage()
{
return "\\" + (currentImage += 1).ToString() + ".jpg";
}
private static void DeleteImage()
{
if (File.Exists(imageLocation))
File.Delete(imageLocation);
}
}
}
You don't need to save your thumbnails to file. Use a MemoryStream instead:
public class ThumbnailCreator
{
public static BitmapImage CreateThumbnail(string imagePath)
{
BitmapFrame source;
using (var stream = new FileStream(imagePath, FileMode.Open, FileAccess.Read))
{
source = BitmapFrame.Create(
stream, BitmapCreateOptions.None, BitmapCacheOption.OnLoad);
}
var encoder = new JpegBitmapEncoder() { QualityLevel = 17 };
encoder.Frames.Add(BitmapFrame.Create(source));
var bitmap = new BitmapImage();
using (var stream = new MemoryStream())
{
encoder.Save(stream);
stream.Position = 0;
bitmap.BeginInit();
bitmap.DecodePixelWidth = 283;
bitmap.CacheOption = BitmapCacheOption.OnLoad;
bitmap.StreamSource = stream;
bitmap.EndInit();
}
bitmap.Freeze();
return bitmap;
}
The intermediate encoding and decoding pass doesn't even seem to be necessary, so you could simply write this:
public class ThumbnailCreator
{
public static BitmapImage CreateThumbnail(string imagePath)
{
var bitmap = new BitmapImage();
using (var stream = new FileStream(imagePath, FileMode.Open, FileAccess.Read))
{
bitmap.BeginInit();
bitmap.DecodePixelWidth = 283;
bitmap.CacheOption = BitmapCacheOption.OnLoad;
bitmap.StreamSource = stream;
bitmap.EndInit();
}
bitmap.Freeze();
return bitmap;
}
}
If you want to call the CreateThumbnail method asynchronously, use Task.Run():
cp.ImageSource = await Task.Run(() => ThumbnailCreator.CreateThumbnail(fileName));
Final Solution:
I just want to add this comment to Clemens solution. I also use the Garbage Collector to stop massive memory usage build-up when loading a lot of images. Here is the final method that I use to create the thumbnails.
public static BitmapImage CreateThumbnail(string imagePath)
{
var bitmap = new BitmapImage();
using (var stream = new FileStream(imagePath, FileMode.Open, FileAccess.Read))
{
bitmap.BeginInit();
bitmap.DecodePixelWidth = 283;
bitmap.CacheOption = BitmapCacheOption.OnLoad;
bitmap.StreamSource = stream;
bitmap.EndInit();
}
bitmap.Freeze();
GC.WaitForPendingFinalizers();
GC.Collect();
return bitmap;
}
am trying to show image directly from zip file without extracting file.
ZipArchiveEntry tumbnail = archive.Entries.Where(s => s.FullName.Equals("coverthumb.png")).FirstOrDefault();
if (tumbnail != null)
{
Stream picdata = tumbnail.Open();
await picdata.CopyToAsync (picdata);
BitmapImage bt = new BitmapImage();
bt.SetSource (picdata.AsRandomAccessStream());
image.Source = bt;
}
but getting an
cannot seek exception
please help.
the following code works for images but when tries to read many images at once some times it gives invalid data exception at bitmap creation
async public static Task<BitmapImage> GetImageFromZipEntry(ZipArchiveEntry zipentry)
{
//for extracting image inside a zip as bitmapimage
BitmapImage tm = new BitmapImage();
try {
if (zipentry != null)
{
using (Stream imstream = zipentry.Open())
{
using (MemoryStream immemorystream = new MemoryStream((int)zipentry.Length))
{
await imstream.CopyToAsync(immemorystream);
using (var sourceStream = new MemoryStream(immemorystream.ToArray()))
{
await tm.SetSourceAsync(sourceStream.AsRandomAccessStream());
}
}
}
}
}
catch(Exception ex)
{
System.Diagnostics.Debug.WriteLine(ex.Message);
tm = null;
}
return tm;
}
I am trying to save an image from a users Clipboard. I am able to get all the correct data from the clipboard image into the Bitmapsource. I am trying to save to a Bitmapimage so I can upload a file to a website. When converting the Bitmapsource to a Bitmapimage, all of the Bitmapimages data stays null and will throw an exception.
private void Window_KeyDown(object sender, KeyEventArgs e)
{
if ((Keyboard.Modifiers == ModifierKeys.Control) && (e.Key == Key.V))
{
if (Clipboard.ContainsImage())
{
BitmapSource bitmapSource = Clipboard.GetImage();
JpegBitmapEncoder encoder = new JpegBitmapEncoder();
MemoryStream memoryStream = new MemoryStream();
BitmapImage bImg = new BitmapImage();
encoder.Frames.Add(BitmapFrame.Create(bitmapSource));
encoder.Save(memoryStream);
bImg.BeginInit();
bImg.StreamSource = new MemoryStream(memoryStream.ToArray());
bImg.EndInit();
var client = new WebClient();
var uri = bImg.UriSource;
var path = uri.AbsolutePath;
//client.UploadFile(link, path);
}
}
}
Both
var uri = bImg.UriSource;
var path = uri.AbsolutePath;
Will throw an unhandled exception of type 'System.NullReferenceException' occurred in WpfApplication1.exe
Additional information: Object reference not set to an instance of an object
Creating a BitmapImage from its StreamSource property will not magically set its UriSource property. You do not need that BitmapImage at all.
Write the original BitmapSource to a FileStream instead of a MemoryStream, and upload that file:
string path = ...
using (var fileStream = new FileStream(path, FileMode.Create))
{
encoder.Save(fileStream);
}
client.UploadFile(link, path);
It may even be unnecessary to create an intermediate file. Just upload the buffer of the MemoryStream:
using (var memoryStream = new MemoryStream())
{
encoder.Save(memoryStream);
client.UploadData(link, memoryStream.ToArray());
}
I made a project to Cache Images. I want to wait in main thread for complete DownloadImage function and then return that saved bitmap. Is that possible?
Am I doing it even properly?
public static ImageSource GetImage(int id)
{
BitmapImage bitmap = new BitmapImage();
String fileName=string.Format("ImageCache/{0}.jpg", id);
using (IsolatedStorageFile myIsolatedStorage = IsolatedStorageFile.GetUserStoreForApplication())
{
if (!myIsolatedStorage.DirectoryExists("ImageCache"))
{
myIsolatedStorage.CreateDirectory("ImageCache");
}
if (myIsolatedStorage.FileExists(fileName))
{
using (IsolatedStorageFileStream fileStream = myIsolatedStorage.OpenFile(fileName, FileMode.Open, FileAccess.Read))
{
bitmap.SetSource(fileStream);
}
}
else
{
DownloadImage(id);
//HERE - how to wait for end of DownloadImage and then do that below??
using (IsolatedStorageFileStream fileStream = myIsolatedStorage.OpenFile(fileName, FileMode.Open, FileAccess.Read))
{
bitmap.SetSource(fileStream);
}
}
}
return bitmap;
}
Here is DownloadImage function:
private static void DownloadImage(Object id)
{
WebClient client = new WebClient();
client.OpenReadCompleted += new OpenReadCompletedEventHandler(client_OpenReadCompleted);
client.OpenReadAsync(new Uri(string.Format("http://example.com/{0}.jpg", id)), id);
}
private static void client_OpenReadCompleted(object sender, OpenReadCompletedEventArgs e)
{
using (IsolatedStorageFile myIsolatedStorage = IsolatedStorageFile.GetUserStoreForApplication())
{
if (e.Error == null && !e.Cancelled)
{
try
{
string fileName = string.Format("ImageCache/{0}.jpg", e.UserState);
IsolatedStorageFileStream fileStream = myIsolatedStorage.CreateFile(fileName);
BitmapImage image = new BitmapImage();
image.SetSource(e.Result);
WriteableBitmap wb = new WriteableBitmap(image);
// Encode WriteableBitmap object to a JPEG stream.
Extensions.SaveJpeg(wb, fileStream, wb.PixelWidth, wb.PixelHeight, 0, 85);
fileStream.Close();
}
catch (Exception ex)
{
//Exception handle appropriately for your app
}
}
}
}
You have many way to achieve what you want. It is possible to wait with async await command which is part of Visual Studio Async.
You can download the latest CTP from here. More how to use it.
Personally I would use events.
This one contains some details and code example: http://www.ben.geek.nz/2010/07/one-time-cached-images-in-windows-phone-7/
I have a xml with data, in this case images stored in the internet..i want to read the xml in windows phone and save it to the memory.. how can i do that? any tutorial?
Lets divide your task into two parts
1. Downloading XML file containing image path
2. Reading that XML file and binding image control to that dynamic path
Lets Proceeds with first case:
1. Downloading XML file containing image path
here Path=http://server_adrs/XML_FILE
iso_path=Path inside Isolated Storage where u want to save XML file.
public void GetXMLFile(string path)
{
WebClient wcXML = new WebClient();
wcXML.OpenReadAsync(new Uri(path));
wcXML.OpenReadCompleted += new OpenReadCompletedEventHandler(wc);
}
void wc(object sender, OpenReadCompletedEventArgs e)
{
var isolatedfile = IsolatedStorageFile.GetUserStoreForApplication();
using (IsolatedStorageFileStream stream = new IsolatedStorageFileStream(iso_path, System.IO.FileMode.Create, isolatedfile))
{
byte[] buffer = new byte[e.Result.Length];
while (e.Result.Read(buffer, 0, buffer.Length) > 0)
{
stream.Write(buffer, 0, buffer.Length);
}
stream.Flush();
System.Threading.Thread.Sleep(0);
}
}
2. Reading XML file and binding image control to the dynamic path
here i am having an List which is showing an images, so i will a function to bind images to this list as per below.
public IList<Dictionary> GetListPerCategory_Icon(string category, string xmlFileName)
{
using (var storage = IsolatedStorageFile.GetUserStoreForApplication())
{
if (storage.FileExists(xmlFileName))
{
using (Stream stream = storage.OpenFile(xmlFileName, FileMode.Open, FileAccess.Read))
{
try
{
loadedData = XDocument.Load(stream);
var data = from query in loadedData.Descendants("category")
where query.Element("name").Value == category
select new Glossy_Test.Dictionary
{
Image=GetImage((string)query.Element("iconpress")),//This is a function which will return Bitmap image
};
categoryList = data.ToList();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message.ToString(), (((PhoneApplicationFrame)Application.Current.RootVisual).Content).ToString(), MessageBoxButton.OK);
return categoryList = null;
}
}
}
}
return categoryList;
}
and here the definition for above function
public BitmapImage GetImage(string imagePath)
{
var image = new BitmapImage();
imagePath = "/Glossy" + imagePath;
using (var storage = IsolatedStorageFile.GetUserStoreForApplication())
{
if (storage.FileExists(imagePath))
{
using (Stream stream = storage.OpenFile(imagePath, FileMode.Open, FileAccess.Read))
{
image.SetSource(stream);
}
}
}
return image;
}
you could use WebClient to pull the xml from the server and then save it as an XDocument in your callback.