I can get a URI from Bing Maps but when I try to save the resulting file (eg, a .jpg) it is empty (eg, 1 x 1 size). I've got it working fine in a WPF app with event handling but how do I do it with a console app?
Here's the code that works with WPF but the same code fails in a console app - I think it must be because the file is saved in an event handler and (far as I know) you can't do that in a console app.
public void vSaveBitmapImage(string sURI, // created with Bing Maps GeocodeServices
string sLocFname)
{
// save file name so event handler knows it
this.sFname = sLocFname;
try
{
BitmapImage bmpImage = new BitmapImage(new Uri(sURI, UriKind.RelativeOrAbsolute));
// setup event handler - file is saved just fine here
bmpImage.DownloadCompleted += vImage_DownloadCompleted;
}
catch (SystemException sex)
{
string s = sex.Message;
}
}
/// <summary>
/// handle the event that an image download has completed
/// </summary>
private void vImage_DownloadCompleted(object sender,
EventArgs e)
{
try
{
JpegBitmapEncoder encoder = new JpegBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create((BitmapImage)sender));
using (var filestream = new FileStream(this.sFname, FileMode.Create))
encoder.Save(filestream);
}
catch (SystemException sex)
{
string s = sex.Message;
}
}
For a console application, it may be simpler to download and save the file synchronously, e.g. with WebClient.DownloadFile().
var client = new WebClient();
client.DownloadFile( sURI, sLocFname );
I'm not sure what is going wrong with your event-based code.
My first guess is that the application drops out of Main() and exits before the background download completes. In that case, you may need to block the main thread until your vImage_DownloadCompleted event is completed.
Related
With the help of this Post I can write the unhandledException to a file. Now how to modify the default popup message that comes when the app get crashed, Default popup will show saying Unfortunately, App has stopped
I need to add report button along with that. Is there any method in xamarin?
Default message When app get crashed:
Modified popup we get when app gets crashed:
i wanted to do like this.
You will get the "Unfortunately, App has stopped." popup when you get an unhandled exception.
You need a try-catch around code which can potentially throw an exception.
Example:
public bool CompressBitmap(string path, Bitmap bitmap)
{
var filestream = new FileStream(path, FileMode.Create);
bitmap.Compress(Bitmap.CompressFormat.Png, 100, filestream);
}
If you call CompressBitmap and bitmap is null, you will get the "Unfortunately, App has stopped." popup because you are not handling the NullReferenceException. You will need a try-catch:
public bool CompressBitmap(string path, Bitmap bitmap)
{
try
{
var filestream = new FileStream(path, FileMode.Create);
bitmap.Compress(Bitmap.CompressFormat.Png, 100, filestream);
}
catch (Exception e)
{
// Show your custom dialog with report button and have access to the Exception message with e.Message, e.Stacktrace, etc.
}
}
I'm developing an application that uses a mobile device to take a photo and send it using a webservice. But after I've taken 4 photos I am getting an OutOfMemoryException in the code below. I tried calling GC.Collect() but it didn't help either. Maybe someone here could be give me an advice how to handle this problem.
public static Bitmap TakePicture()
{
var dialog = new CameraCaptureDialog
{
Resolution = new Size(1600, 1200),
StillQuality = CameraCaptureStillQuality.Default
};
dialog.ShowDialog();
// If the filename is empty the user took no picture
if (string.IsNullOrEmpty(dialog.FileName))
return null;
// (!) The OutOfMemoryException is thrown here (!)
var bitmap = new Bitmap(dialog.FileName);
File.Delete(dialog.FileName);
return bitmap;
}
The function is called by an event handler:
private void _pictureBox_Click(object sender, EventArgs e)
{
_takePictureLinkLabel.Visible = false;
var image = Camera.TakePicture();
if (image == null)
return;
image = Camera.CutBitmap(image, 2.5);
_pictureBox.Image = image;
_image = Camera.ImageToByteArray(image);
}
I suspect you are holding onto references. As a minor cause, note that dialogs don't dispose themselves when using ShowDialog, so you should be using the dialog (although I would expect GC to still collect an undisposed but non-referenced dialog).
Likewise, you should probably be using the image, but again: not sure I'd expect this to make-or-break; worth a try, though...
public static Bitmap TakePicture()
{
string filename;
using(var dialog = new CameraCaptureDialog
{
Resolution = new Size(1600, 1200),
StillQuality = CameraCaptureStillQuality.Default
}) {
dialog.ShowDialog();
filename = dialog.FileName;
}
// If the filename is empty the user took no picture
if (string.IsNullOrEmpty(filename))
return null;
// (!) The OutOfMemoryException is thrown here (!)
var bitmap = new Bitmap(filename);
File.Delete(filename);
return bitmap;
}
private void _pictureBox_Click(object sender, EventArgs e)
{
_takePictureLinkLabel.Visible = false;
using(var image = Camera.TakePicture()) {
if (image == null)
return;
image = Camera.CutBitmap(image, 2.5);
_pictureBox.Image = image;
_image = Camera.ImageToByteArray(image);
}
}
I'd also be a little cautious of the CutBitmap etc, to ensure that things are released ASAP.
Your mobile device usually does not have any memory swapping to disk option, so since you choose to store your images as bitmaps in memory rather than files on disk, you quickly consume your phone's memory. Your "new Bitmap()" line allocates a large chunk of memory, so it is very likely to throw the exception there. Another contender is your Camera.ImageToByteArray that will allocate a large amount of memory. This probably isn't large to what you're used to with your computer, but for your mobile this is gigantic
Try keeping the pictures on disk until you use them, i.e. until sending them to the webservice. For displaying them, use your built-in controls, they are probably the most memory efficient and you can usually point them to the image files.
Cheers
Nik
In my application I can download some media files from web. Normally I used WebClient.OpenReadCompleted method to download, decrypt and save the file to IsolatedStorage. It worked well and looked like that:
private void downloadedSong_OpenReadCompleted(object sender, OpenReadCompletedEventArgs e, SomeOtherValues someOtherValues) // delegate, uses additional values
{
// Some preparations
try
{
if (e.Result != null)
{
using (isolatedStorageFile = IsolatedStorageFile.GetUserStoreForApplication())
{
// working with the gained stream, decryption
// saving the decrypted file to isolatedStorage
isolatedStorageFileStream = new IsolatedStorageFileStream("SomeFileNameHere", FileMode.OpenOrCreate, isolatedStorageFile);
// and use it for MediaElement
mediaElement.SetSource(isolatedStorageFileStream);
mediaElement.Position = new TimeSpan(0);
mediaElement.MediaOpened += new RoutedEventHandler(mediaFile_MediaOpened);
// and some other work
}
}
}
catch(Exception ex)
{
// try/catch stuff
}
}
But after some investigation I found out that with large files(for me it's more than 100 MB) I'm getting OutOfMemory exception during downloading this file. I suppose that's because WebClient.OpenReadCompleted loads the whole stream into RAM and chokes... And I will need more memory to decrypt this stream.
After another investigation, I've found how to divide large file into chunks after OpenReadCompleted event at saving this file to IsolatedStorage(or decryption and then saving in my ocasion), but this would help with only a part of problem... The primary problem is how to prevent phone chokes during download process. Is there a way to download large file in chunks? Then I could use the found solution to pass through decryption process. (and still I'd need to find a way to load such big file into mediaElement, but that would be another question)
Answer:
private WebHeaderCollection headers;
private int iterator = 0;
private int delta = 1048576;
private string savedFile = "testFile.mp3";
// some preparations
// Start downloading first piece
using (IsolatedStorageFile isolatedStorageFile = IsolatedStorageFile.GetUserStoreForApplication())
{
if (isolatedStorageFile.FileExists(savedFile))
isolatedStorageFile.DeleteFile(savedFile);
}
headers = new WebHeaderCollection();
headers[HttpRequestHeader.Range] = "bytes=" + iterator.ToString() + '-' + (iterator + delta).ToString();
webClientReadCompleted = new WebClient();
webClientReadCompleted.Headers = headers;
webClientReadCompleted.OpenReadCompleted += downloadedSong_OpenReadCompleted;
webClientReadCompleted.OpenReadAsync(new Uri(song.Link));
// song.Link was given earlier
private void downloadedSong_OpenReadCompleted(object sender, OpenReadCompletedEventArgs e)
{
try
{
if (e.Cancelled == false)
{
if (e.Result != null)
{
((WebClient)sender).OpenReadCompleted -= downloadedSong_OpenReadCompleted;
using (IsolatedStorageFile myIsolatedStorage = IsolatedStorageFile.GetUserStoreForApplication())
{
using (IsolatedStorageFileStream fileStream = new IsolatedStorageFileStream(savedFile, FileMode.Append, FileAccess.Write, myIsolatedStorage))
{
int mediaFileLength = (int)e.Result.Length;
byte[] byteFile = new byte[mediaFileLength];
e.Result.Read(byteFile, 0, byteFile.Length);
fileStream.Write(byteFile, 0, byteFile.Length);
// If there's something left, download it recursively
if (byteFile.Length > delta)
{
iterator = iterator + delta + 1;
headers = new WebHeaderCollection();
headers[HttpRequestHeader.Range] = "bytes=" + iterator.ToString() + '-' + (iterator + delta).ToString();
webClientReadCompleted.Headers = headers;
webClientReadCompleted.OpenReadCompleted += downloadedSong_OpenReadCompleted;
webClientReadCompleted.OpenReadAsync(new Uri(song.Link));
}
}
}
}
}
}
To download a file in chunks you'll need to make multiple requests. One for each chunk.
Unfortunately it's not possible to say "get me this file and return it in chunks of size X";
Assuming that the server supports it, you can use the HTTP Range header to specify which bytes of a file the server should return in response to a request.
You then make multiple requests to get the file in pieces and then put it all back together on the device. You'll probably find it simplest to make sequential calls and start the next one once you've got and verified the previous chunk.
This approach makes it simple to resume a download when the user returns to the app. You just look at how much was downloaded previously and then get the next chunk.
I've written an app which downloads movies (up to 2.6GB) in 64K chunks and then played them back from IsolatedStorage with the MediaPlayerLauncher. Playing via the MediaElement should work too but I haven't verified. You can test this by loading a large file directly into IsolatedStorage (via Isolated Storage Explorer, or similar) and check the memory implications of playing that way.
Confirmed: You can use BackgroundTransferRequest to download multi-GB files but you must set TransferPreferences to None to force the download to happen while connected to an external power supply and while connected to wi-fi, else the BackgroundTransferRequest will fail.
I wonder if it's possible to use a BackgroundTransferRequest to download large files easily and let the phone worry about the implementation details? The documentation seems to suggest that file downloads over 100 MB are possible, and the "Range" verb is reserved for it's own use, so it probably uses this automatically if it can behind the scenes.
From the documentation regarding files over 100 MB:
For files larger than 100 MB, you must set the TransferPreferences
property of the transfer to None or the transfer will fail. If you do
not know the size of a transfer and it is possible that it could
exceed this limit, you should set the value to None, meaning that the
transfer will only proceed when the phone is connected to external
power and has a Wi-Fi connection.
From the documentation regarding use of the "Range" verb:
The Headers property of the BackgroundTransferRequest object is used
to set the HTTP headers for a transfer request. The following headers
are reserved for use by the system and cannot be used by calling
applications. Adding one of the following headers to the Headers
collection will cause a NotSupportedException to be thrown when the
Add(BackgroundTransferRequest) method is used to queue the transfer
request:
If-Modified-Since
If-None-Match
If-Range
Range
Unless-Modified-Since
Here's the documentation:
http://msdn.microsoft.com/en-us/library/windowsphone/develop/hh202955(v=vs.105).aspx
I have found a very usefull class on this link: images caching - that help me to make logic for caching images. But in my case I have this:
private void DetailView_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
SaveAndLoadImage(feedItem);
}
And in this method I save and load image from isolated storage. But I can't load file imidiately because of some permission (Operation not permitted on IsolatedStorageFileStream.). How can I correct my logic to save and load images immediately?
public void SaveAndLoadImage(MediaItemViewModel curItem)
{
string url = string.Empty;
if (!string.IsNullOrEmpty(curItem.ThumbUrl))
{
url = curItem.ThumbUrl;
}
if ((string.IsNullOrEmpty(curItem.ThumbUrl)) && (!string.IsNullOrEmpty(curItem.MediaUrl)))
{
url = curItem.MediaUrl;
}
if ((!string.IsNullOrEmpty(url)) && (CacheImageFile.GetInstance().IsOnStorage(new Uri(url)) == false))
{
CacheImageFile.DownloadFromWeb(new Uri(url));
}
curItem.ImageSource = CacheImageFile.ExtractFromLocalStorage(new Uri(url)) as BitmapImage;
}
have a look at below link
http://www.windowsphonegeek.com/tips/All-about-WP7-Isolated-Storage---Read-and-Save-Images
for loading images from isolated storage. using streams
BitmapImage bi = new BitmapImage();
using (IsolatedStorageFile myIsolatedStorage = IsolatedStorageFile.GetUserStoreForApplication())
{
using (IsolatedStorageFileStream fileStream = myIsolatedStorage.OpenFile("logo.jpg", FileMode.Open, FileAccess.Read))
{
bi.SetSource(fileStream);
this.img.Height = bi.PixelHeight;
this.img.Width = bi.PixelWidth;
}
}
this.img.Source = bi;
You are getting the image asynchronously from the web, but immediately go to the next line to read the file that hasn't even been written to isolated storage. This is what's causing you the exception.
You could try editing the caching library you found on github, using ManualResetEvent. Notice that you will have to make the method calls on another thread!
For example:
public class CacheImageFileConverter : IValueConverter
{
...
private static ManualResetEvent mre = new ManualResetEvent(true);
private static object DownloadFromWeb(Uri imageFileUri)
{
mre.Reset();
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();
mre.Set();
};
m_webClient.OpenReadAsync(imageFileUri);
return bm;
}
private static object ExtractFromLocalStorage(Uri imageFileUri)
{
mre.WaitOne();
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;
}
}
.... other methods
}
Notice the use of Reset, Set and WaitOne for signaling.
You can use JetImageLoader, I created it for application, where we need to load, cache and show big amount of logos, icons and so on.
It can be used as binding converter, so you should not even change your code! Just update your XAMLs!
Please, check out samples in repository, you'll love it ;)
Features:
Caching on disk
Caching in memory
Fully asynchronous
Available as binding converter or programmatically from your code
Fully open source, fork and improve it!
Here is the example:
<Image Source="{Binding ImageUrl, Converter={StaticResource MyAppJetImageLoaderConverter}}"/>
P.S. I am sorry, that I copying my answer from another questions, but image caching on windows phone is huge problem and I want to share my solution, so everybody can use it and improve for developers community
I download an image from an URL asynchronously using WebRequest this way:
public void Download(string url)
{
byte[] buffer = new byte[0x1000];
WebRequest request = HttpWebRequest.Create(url);
request.Method = "GET";
request.ContentType = "image/gif";
request.BeginGetResponse(result =>
{
WebRequest webRequest = result.AsyncState as WebRequest;
WebResponse response = webRequest.EndGetResponse(result);
ReadState readState = new ReadState()
{
Response = response.GetResponseStream(),
AccumulatedResponse = new MemoryStream(),
Buffer = buffer,
};
readState.Response.BeginRead(buffer, 0,
readState.Buffer.Length, ReadCallback, readState);
}, request);
}
public void ReadCallback(IAsyncResult result)
{
ReadState readState = result.AsyncState as ReadState;
int bytesRead = readState.Response.EndRead(result);
if(bytesRead > 0)
{
readState.AccumulatedResponse.BeginWrite(readState.Buffer, 0, bytesRead, writeResult =>
{
readState.AccumulatedResponse.EndWrite(writeResult);
readState.Response.BeginRead(readState.Buffer, 0, readState.Buffer.Length, ReadCallback, readState);
}, null);
}
else
{
readState.AccumulatedResponse.Flush();
readState.Response.Close();
pictureBox1.Image = Image.FromStream(readState.AccumulatedResponse);
}
}
public class ReadState
{
public Stream Response { get; set; }
public Stream AccumulatedResponse { get; set; }
public byte[] Buffer { get; set; }
}
and it works ok, but I would like to show the progress of the download as the browsers do, and not to show only when it finishes.
If I do
pictureBox1.Image = Image.FromStream(readState.AccumulatedResponse);
before it finishes I get an exception that the picture is not valid, even though it has some data.
Is there anyway to show partial data?
JPEG has a special encoding mode called "Progressive JPEG" in which data is compressed in multiple passes of progressively higher detail. Windows 7 has built-in support for this.
I would like to show the progress of
the download as the browsers do, and
not to show only when it finishes.
You have two options:
Use WebClient.DownloadDataAsync. This will raise progress events via DownloadProgressChanged and provide a final notification of when the data is available via the DownloadDataCompleted event. At that point, you could assign the image to, say, the PictureBox.
If you are downloading an image to eventually display in a PictureBox control then it may even be easier to just go with PictureBox.LoadAsync. This too will provide progress updates via its LoadProgressChanged event and finally LoadCompleted on completion.
You've problem with the PictureBox control, not with the partial download.
An hack is to use the WebBrowserControl, as IE is able to show partial images.
When you begin the request, check the WebResponse.ContentLength property, that should give you the total number of bytes to expect. Then keep track of the total number of bytes read in your ReadCallback method. The rest should be obvious. :)
[edit] Oops, I re-read the question, and that wasn't quite what you wanted. I'm still leaving this up though, in case somebody might benefit from it.
I don't think you will manage that using the built-in methods of the .NET framework, since they will first read the full image and then display it. You will need to either find some other library that supports displaying partially downloaded images or write a parser yourself.
You can probably do this, however, it will require some additional knowledge of the structure of gif files. Wotsit.org has several links to gif structure documents, to get you started.
The general approach would be to take your total accumulated data, and from the end, search back until you find the end of a block terminator. The data between this last valid block and the end of the stream is only partially complete, and must be considered junk data. You want to remove it, and add a file trailing block to the stream. Then, you should be able to load this edited stream into a PictureBox.
There's an event you can use on an asynchronous web download request that gets called periodically and allows you to directly update a ProgressBar control. Here's an example of how I've used this event:
delegate void SetProgressDelegate(int percentComplete);
/// <summary>
/// A thread-safe method for updating the file download progress bar.
/// </summary>
/// <param name="bytesReceived"></param>
/// <param name="totalBytes"></param>
void SetDownloadProgress(int percentComplete)
{
if (this.InvokeRequired)
{
SetProgressDelegate d = new SetProgressDelegate(SetDownloadProgress);
this.Invoke(d, new object[] { percentComplete });
}
else
{
this.progressFileDownload.Value = percentComplete;
}
}
/// <summary>
/// Event handler to update the progress bar during file download.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
void web_DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e)
{
SetDownloadProgress(e.ProgressPercentage);
}
And here's how I wire up the event and start the download:
WebClient web = new WebClient();
web.DownloadFileCompleted += new AsyncCompletedEventHandler(web_DownloadFileCompleted);
web.DownloadProgressChanged += new DownloadProgressChangedEventHandler(web_DownloadProgressChanged);
web.DownloadFileAsync(sourceUri, destinationFileName);
Your solution reads the file directly into memory, whereas mine saves it to a file on disk, so this solution may or may not work for you. If displaying a progress indication is important to you, this method does work and you can always download to a temporary file, then load that file into the image control once it's complete.