I am using below code to save a remote image in Windows Phone 8. But i keep hitting with System.NotSupportedException: Specified method is not supported. exception at SaveJpeg() method call.
I tried different combinations of method call (you can see commented line). I couldn't able to figure out what i am doing incorrectly.
using (HttpClient client = new HttpClient())
{
HttpResponseMessage response = await client.GetAsync(imageUrl);
await Task.Run(async () =>
{
if (response.IsSuccessStatusCode)
{
// save image locally
Debug.WriteLine("Downloading image..." + imageName);
using (IsolatedStorageFile myIsolatedStorage = IsolatedStorageFile.GetUserStoreForApplication())
{
if (!myIsolatedStorage.DirectoryExists("Images"))
myIsolatedStorage.CreateDirectory("Images");
string path = imageName;
IsolatedStorageFileStream fileStream = myIsolatedStorage.CreateFile(path);
var buffer = await response.Content.ReadAsStreamAsync();
Deployment.Current.Dispatcher.BeginInvoke(() =>
{
BitmapImage bitmap = new BitmapImage { CreateOptions = BitmapCreateOptions.None };
bitmap.SetSource(buffer);
WriteableBitmap wb = new WriteableBitmap(bitmap);
//System.Windows.Media.Imaging.Extensions.SaveJpeg(wb, fileStream, wb.PixelWidth, wb.PixelHeight, 0, 100);
wb.SaveJpeg(fileStream, wb.PixelWidth, wb.PixelHeight, 0, 98);
});
fileStream.Close();
}
}
});
}
By putting the code block in BeginInvoke block you are calling the SaveJpeg on a different thread (the "UI thread") to the code which calls fileStream.Close().
In effect this means it is very likely that the call to fileStream.Close() will be called before wb.SaveJpeg.
If you move the fileStream.Close() inside the BeginInvoke block, after wb.SaveJpeg() it should work.
Related
I am trying to save a file(BitmapImage) to a certain location, but as soon as I use async & await I get an error saying the file is in use:
The process cannot access the file 'C:\Users\ ... \image1.jpg' because
it is being used by another process.
My coding:
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))
await Task.Run(() => encoder.Save(filestream)); //Error here
When I use the code without the await, the code works perfectly fine. I think it might be because another thread might be using it, but can anyone help or explain to me a work around for my issue? Thank you.
In your case when you use Task with await another thread is used to save your encoder. But your encoder is also used by your main thread so new thread can't use it.
Change your code a little:
await Task.Run(() =>
{
using (var filestream = new FileStream(GetImageLocation(), FileMode.Create))
{
BitmapImage image = new BitmapImage(new Uri(oldImagePath));
var encoder = new JpegBitmapEncoder() { QualityLevel = 17 };
encoder.Frames.Add(BitmapFrame.Create(image));
encoder.Save(filestream);
}
}
Now you create and save your encoder in the same task and it will use only one thread.
I think you should move the code inside the Task.Run, because it gets called inside another thread.
You could encode to a MemoryStream, get the byte array, use WriteAsync on the FileStream, and avoid using Task.Run altogether.
BitmapImage image = new BitmapImage(new Uri(oldImagePath));
var encoder = new JpegBitmapEncoder() { QualityLevel = 17 };
encoder.Frames.Add(BitmapFrame.Create(image));
using (var mem = new MemoryStream())
using (var filestream = new FileStream(GetImageLocation(), FileMode.Create))
{
encoder.Save(mem);
var data = mem.ToArray();
await filestream.WriteAsync(date, 0, data.Length);
}
Note that this will block your thread during the encoding and will use more memory.
This question already has an answer here:
Download multiple files async and wait for all of them to finish before executing the rest of the code
(1 answer)
Closed 6 years ago.
Following up from my question at this link and trying to implement the accepted answer:
Invoking a method asynchronously and/or on its own thread to increase performance
I'm trying to turn a simple method called DownloadImageFromUrl that takes a string Url and returns a Bitmap into one that will run using async. The current method:
private Bitmap DownloadImageFromUrl(string url)
{
//// METHOD A:
//WebRequest request = System.Net.WebRequest.Create(url);
//WebResponse response = request.GetResponse();
//Stream responseStream = response.GetResponseStream();
//return new Bitmap(responseStream);
// METHOD B:
using (WebClient client = new WebClient())
{
byte[] data = client.DownloadData(url);
using (MemoryStream mem = (data == null) ? null : new MemoryStream(data))
{
return (data == null || mem == null) ? null : (Bitmap)Image.FromStream(mem);
}
}
}
The idea of making this Async is so that in another method, I can use this one to do something like this:
public async Task<HttpResponseMessage> process(string image)
{
var task = DownloadFromBlobAsync(image);
var setupData = DoSomeSetup();
var image = await task;
return DrawTextOnImage(image, setupData);
}
The DoSomeSetup takes fairly long and so does downloading the image, so i'd like to download the image on its own thread while the setup happens.
I'm not sure what tools are available to change this downloadImageFromUrl to return a task.. Any resources or code examples would be helpful.
Sample by charlie from How to download image from url using c#:
using (WebClient client = new WebClient())
{
client.DownloadFileAsync(new Uri(url), #"c:\temp\image35.png");
client.DownloadFile(new Uri(url), #"c:\temp\image35.png");
}
EDITED
using (IsolatedStorageFile myIsolatedStorage = IsolatedStorageFile.GetUserStoreForApplication())
{
IsolatedStorageFileStream fileStream = myIsolatedStorage.CreateFile("fileNameHere");
BitmapImage bitmap = new BitmapImage();
var stream = await DownloadFile(new Uri("http://someuri.com", UriKind.Absolute));
bitmap.SetSource(stream);
WriteableBitmap wb = new WriteableBitmap(bitmap);
// Encode WriteableBitmap object to a JPEG stream.
Extensions.SaveJpeg(wb, fileStream, wb.PixelWidth, wb.PixelHeight, 0, 85);
fileStream.Close();
}
possible duplicated:
How to download image from url using c#
I am trying to read read a BitmapImage from local storage. The path is selected by FileOpenPicker. But when I try to convert it into Byte[], it fails. It seems like image is not downloaded at all, because image.PixelWidth returns '0'.
StorageFile storageFile = args.Files[0];
//var stream = await storageFile.OpenAsync(Windows.Storage.FileAccessMode.Read);
//var bitmapImage = new Windows.UI.Xaml.Media.Imaging.BitmapImage();
//await bitmapImage.SetSourceAsync(stream);
var decoder = await Windows.Graphics.Imaging.BitmapDecoder.CreateAsync(stream);
BitmapImage wb = new BitmapImage(new Uri(storageFile.Path));
byte[] pixeBuffer = null;
using (MemoryStream ms = new MemoryStream())
{
WriteableBitmap wbit = new WriteableBitmap(wb.PixelWidth,wb.PixelHeight);
Stream s1 = wb.PixelBuffer.AsStream();
s1.CopyTo(ms);
pixeBuffer = ms.ToArray();
}
The program crashes at
WriteableBitmap wbit = new WriteableBitmap(wb.PixelWidth,wb.PixelHeight);
I have checked that storageFile.Path points to right path, I have tried URL from internet too but same results.
I am new to C# and don't know how to trigger exception handling for windows phone, please guide me if it may help.
After calling the decoder you should be doing something like:
try
{
PixelDataProvider provider = await decoder.GetPixelDataAsync(decoder.BitmapPixelFormat, decoder.BitmapAlphaMode, new BitmapTransform(), ExifOrientationMode.RespectExifOrientation, ColorManagementMode.ColorManageToSRgb);
byte[] pixelBuffer = provider.DetachPixelData();
}
catch(Exception ex)
{
Debug.WriteLine(ex);
}
The try/catch block is the way you would want to catch exceptions in your code so I included it too.
I want to share the screenshot of my app. The screenshot is saved into the phone's picture library then it's shared as StorageFile
The problem is, the image is not attached to the sharing apps. I've verified that the screenshot was saved successfully in the phone's picture library.
Here's my code. What am I missing?
private async void askFacebook()
{
// Render some UI to a RenderTargetBitmap
var renderTargetBitmap = new RenderTargetBitmap();
await renderTargetBitmap.RenderAsync(this.gridRoot, (int)this.gridRoot.ActualWidth, (int)this.gridRoot.ActualHeight);
// Get the pixel buffer and copy it into a WriteableBitmap
var pixelBuffer = await renderTargetBitmap.GetPixelsAsync();
var width = renderTargetBitmap.PixelWidth;
var height = renderTargetBitmap.PixelHeight;
var wbmp = await new WriteableBitmap(1, 1).FromPixelBuffer(pixelBuffer, width, height);
imageToShare = await saveWriteableBitmapAsJpeg(wbmp, string.Format("{0}.jpg", getAppTitle()));
DataTransferManager.ShowShareUI();
}
private string getAppTitle()
{
// Get the assembly with Reflection:
Assembly assembly = typeof(App).GetTypeInfo().Assembly;
// Get the custom attribute informations:
var titleAttribute = assembly.CustomAttributes.Where(ca => ca.AttributeType == typeof(AssemblyTitleAttribute)).FirstOrDefault();
// Now get the string value contained in the constructor:
return titleAttribute.ConstructorArguments[0].Value.ToString();
}
private async Task<StorageFile> saveWriteableBitmapAsJpeg(WriteableBitmap bmp, string fileName)
{
// Create file in Pictures library and write jpeg to it
var outputFile = await KnownFolders.PicturesLibrary.CreateFileAsync(fileName, CreationCollisionOption.ReplaceExisting);
using (var writeStream = await outputFile.OpenAsync(FileAccessMode.ReadWrite))
{
await encodeWriteableBitmap(bmp, writeStream, BitmapEncoder.JpegEncoderId);
}
return outputFile;
}
private async Task encodeWriteableBitmap(WriteableBitmap bmp, IRandomAccessStream writeStream, Guid encoderId)
{
// Copy buffer to pixels
byte[] pixels;
using (var stream = bmp.PixelBuffer.AsStream())
{
pixels = new byte[(uint)stream.Length];
await stream.ReadAsync(pixels, 0, pixels.Length);
}
// Encode pixels into stream
var encoder = await BitmapEncoder.CreateAsync(encoderId, writeStream);
var logicalDpi = DisplayInformation.GetForCurrentView().LogicalDpi;
encoder.SetPixelData(BitmapPixelFormat.Bgra8,
BitmapAlphaMode.Premultiplied,
(uint)bmp.PixelWidth,
(uint)bmp.PixelHeight,
logicalDpi,
logicalDpi,
pixels);
await encoder.FlushAsync();
}
private void ShareImageHandler(DataTransferManager sender, DataRequestedEventArgs e)
{
DataRequest request = e.Request;
request.Data.Properties.Title = "Ask Social Media";
request.Data.Properties.Description = "Do you know the answer to this question?";
// Because we are making async calls in the DataRequested event handler,
// we need to get the deferral first.
DataRequestDeferral deferral = request.GetDeferral();
// Make sure we always call Complete on the deferral.
try
{
request.Data.SetBitmap(RandomAccessStreamReference.CreateFromFile(imageToShare));
}
finally
{
deferral.Complete();
}
}
Apparently for Windows Phone, the image needs to be a StorageItem as the SetBitmap methods only works with Windows 8.x
So for Windows phone, instead of
request.Data.SetBitmap(RandomAccessStreamReference.CreateFromFile(imageToShare));
I created a storage item and use SetStorageItems to share it. It works with native Windows Phone apps such as emails, OneNote but I haven't tested it for sharing on Facebook, Twitter etc.
var imageItems = new List<IStorageItem>();
imageItems.Add(imageToShare);
request.Data.SetStorageItems(imageItems);
In my application I have list of urls to images. And what I need to do is download this images and save them in Isolated Storage.
What I already have:
using (IsolatedStorageFile localFile = IsolatedStorageFile.GetUserStoreForApplication()) {
...
foreach (var item in MyList)
{
Uri uri = new Uri(item.url, UriKind.Absolute);
BitmapImage bitmap = new BitmapImage(uri);
WriteableBitmap wb = new WriteableBitmap(bitmap);
using (IsolatedStorageFileStream fs = localFile.CreateFile(GetFileName(item.url)))//escape file name
{
wb.SaveJpeg(fs, wb.PixelWidth, wb.PixelHeight, 0, 85);
}
}
...
}
This code have place inside function in my App.xaml.cs file. I have tried many solutions, in this one the problem is "Invalid cross-thread access".
How can I make it work?
You get invalid cross-thread access if you create WriteableBitmap on non-UI thread. Ensure that that code is run on the main thread by using Dispatcher:
Deployment.Current.Dispatcher.BeginInvoke(() =>
// ...
);
Solution for this problem is:
foreach (var item in MyList)
{
Uri uri = new Uri(item.url, UriKind.Absolute);
HttpWebRequest request = HttpWebRequest.Create(uri) as HttpWebRequest;
request.BeginGetResponse((ar) =>
{
var response = request.EndGetResponse(ar);
Deployment.Current.Dispatcher.BeginInvoke(() =>
{
using (var stream = response.GetResponseStream())
{
var name = GetFileName(item.url);
if (localFile.FileExists(name))
{
localFile.DeleteFile(name);
}
using (IsolatedStorageFileStream fs = localFile.CreateFile(name))
{
stream.CopyTo(fs);
}
}
});
}, null);
}
#Mateusz Rogulski
You should use WebClient, and i suggest you following solution for your problem. just try.
public string YourMethod(string yoursUri)
{
BitmapImage image=new BitmapImage();
WebClient client = new WebClient();
client.OpenReadCompleted += async (o, args) =>
{
Stream stream = new MemoryStream();
await args.Result.CopyToAsync(stream);
image.SetSource(stream);
};
client.OpenReadAsync(new Uri(yoursUri));//if passing object than you can write myObj.yoursUri
return image;
}
now you have image and you can save into your isolatedStorage with valid checks wherever you call this function