windows phone 8: how to download xml file from web and save it to local? - windows-phone-8

I would like to download a xml file from web, then save it to the local storage but I do not know how to do that. Please to help me clearly or give me an example. Thank you.

Downloading a file is a huge subject and can be done in many ways. I assume that you know the Uri of the file you want to download, and want you mean by local is IsolatedStorage.
I'll show three examples how it can be done (there are also other ways).
1. The simpliest example will dowload string via WebClient:
public static void DownloadFileVerySimle(Uri fileAdress, string fileName)
{
WebClient client = new WebClient();
client.DownloadStringCompleted += (s, ev) =>
{
using (IsolatedStorageFile ISF = IsolatedStorageFile.GetUserStoreForApplication())
using (StreamWriter writeToFile = new StreamWriter(ISF.CreateFile(fileName)))
writeToFile.Write(ev.Result);
};
client.DownloadStringAsync(fileAdress);
}
As you can see I'm directly downloading string (ev.Result is a string - that is a disadventage of this method) to IsolatedStorage.
And usage - for example after Button click:
private void Download_Click(object sender, RoutedEventArgs e)
{
DownloadFileVerySimle(new Uri(#"http://filedress/myfile.txt", UriKind.Absolute), "myfile.txt");
}
2. In the second method (simple but more complicated) I'll use again WebClient and I'll need to do it asynchronously (if you are new to this I would suggest to read MSDN, async-await on Stephen Cleary blog and maybe some tutorials).
First I need Task which will download a Stream from web:
public static Task<Stream> DownloadStream(Uri url)
{
TaskCompletionSource<Stream> tcs = new TaskCompletionSource<Stream>();
WebClient wbc = new WebClient();
wbc.OpenReadCompleted += (s, e) =>
{
if (e.Error != null) tcs.TrySetException(e.Error);
else if (e.Cancelled) tcs.TrySetCanceled();
else tcs.TrySetResult(e.Result);
};
wbc.OpenReadAsync(url);
return tcs.Task;
}
Then I'll write my method downloading a file - it also need to be async as I'll use await DownloadStream:
public enum DownloadStatus { Ok, Error };
public static async Task<DownloadStatus> DownloadFileSimle(Uri fileAdress, string fileName)
{
try
{
using (Stream resopnse = await DownloadStream(new Uri(#"http://filedress/myfile.txt", UriKind.Absolute)))
using (IsolatedStorageFile ISF = IsolatedStorageFile.GetUserStoreForApplication())
{
if (ISF.FileExists(fileName)) return DownloadStatus.Error;
using (IsolatedStorageFileStream file = ISF.CreateFile(fileName))
resopnse.CopyTo(file, 1024);
return DownloadStatus.Ok;
}
}
catch { return DownloadStatus.Error; }
}
And usage of my method for example after Button click:
private async void Downlaod_Click(object sender, RoutedEventArgs e)
{
DownloadStatus fileDownloaded = await DownloadFileSimle(new Uri(#"http://filedress/myfile.txt", UriKind.Absolute), "myfile.txt");
switch (fileDownloaded)
{
case DownloadStatus.Ok:
MessageBox.Show("File downloaded!");
break;
case DownloadStatus.Error:
default:
MessageBox.Show("There was an error while downloading.");
break;
}
}
This method can have problems for example if you try to download very big file (example 150 Mb).
3. The third method - uses WebRequest with again async-await, but this method can be changed to download files via buffer, and therefore not to use too much memory:
First I'll need to extend my Webrequest by a method that will asynchronously return a Stream:
public static class Extensions
{
public static Task<Stream> GetRequestStreamAsync(this WebRequest webRequest)
{
TaskCompletionSource<Stream> taskComplete = new TaskCompletionSource<Stream>();
webRequest.BeginGetRequestStream(arg =>
{
try
{
Stream requestStream = webRequest.EndGetRequestStream(arg);
taskComplete.TrySetResult(requestStream);
}
catch (Exception ex) { taskComplete.SetException(ex); }
}, webRequest);
return taskComplete.Task;
}
}
Then I can get to work and write my Downloading method:
public static async Task<DownloadStatus> DownloadFile(Uri fileAdress, string fileName)
{
try
{
WebRequest request = WebRequest.Create(fileAdress);
if (request != null)
{
using (Stream resopnse = await request.GetRequestStreamAsync())
{
using (IsolatedStorageFile ISF = IsolatedStorageFile.GetUserStoreForApplication())
{
if (ISF.FileExists(fileName)) return DownloadStatus.Error;
using (IsolatedStorageFileStream file = ISF.CreateFile(fileName))
{
const int BUFFER_SIZE = 10 * 1024;
byte[] buf = new byte[BUFFER_SIZE];
int bytesread = 0;
while ((bytesread = await resopnse.ReadAsync(buf, 0, BUFFER_SIZE)) > 0)
file.Write(buf, 0, bytesread);
}
}
return DownloadStatus.Ok;
}
}
return DownloadStatus.Error;
}
catch { return DownloadStatus.Error; }
}
Again usage:
private async void Downlaod_Click(object sender, RoutedEventArgs e)
{
DownloadStatus fileDownloaded = await DownloadFile(new Uri(#"http://filedress/myfile.txt", UriKind.Absolute), "myfile.txt");
switch (fileDownloaded)
{
case DownloadStatus.Ok:
MessageBox.Show("File downloaded!");
break;
case DownloadStatus.Error:
default:
MessageBox.Show("There was an error while downloading.");
break;
}
}
Those methods of course can be improved but I think this can give you an overview how it can look like. The main disadvantage of these methods may be that they work in foreground, which means that when you exit your App or hit start button, downloading stops. If you need to download in background you can use Background File Transfers - but that is other story.
As you can see you can reach your goal in many ways. You can read more about those methods on many pages, tutorials and blogs, compare an choose the most suitable.
Hope this helps. Happy coding and good luck.

Related

Windows Phone: How to load html in webview from a local file

I have a html string and it has local css,js paths. But Html is not working with these local paths. We searced but in every example, they have loaded html with writing inline. But I have to work disconnect and there is so much css,js assests. If i write inline, i am worried about it will load slow and i think it so senseless. Then i decided to change a local html file and load html from that file.
How can i load html from a local file?
This is my example code:
StorageFolder localFolder =
Windows.Storage.ApplicationData.Current.LocalFolder;
string desiredName = "mobile.html";
StorageFile newFile =
await localFolder.CreateFileAsync(desiredName,CreationCollisionOption.OpenIfExists);
using (var stream = await newFile.OpenStreamForWriteAsync())
{
stream.Write(fileBytes, 0, fileBytes.Length);
}
webViewFormResponse.Source = new Uri(newFile.Path);
newFile.Path like this: C:\Data\Users\DefApps\APPDATA\Local\Packages\9f4082ad-ad69-4cb8-8749-751ee4c5e46d_x2xndhe6jjw20\LocalState\mobile.html
You can use the NavigateToLocalStreamUri method of the WebView
e.g.
In WebView Loaded event
private void WebView_Loaded(object sender, RoutedEventArgs e)
{
Uri uri = MyWebView.BuildLocalStreamUri("LocalData", "mobile.html");
LocalUriResolver resolver = new LocalUriResolver();
MyWebView.NavigateToLocalStreamUri(uri, resolver);
}
And the Uri resolver class
public sealed class LocalUriResolver : IUriToStreamResolver
{
public IAsyncOperation<IInputStream> UriToStreamAsync(Uri uri)
{
if (uri == null)
{
throw new Exception();
}
string path = uri.AbsolutePath;
return GetContent(path).AsAsyncOperation();
}
private async Task<IInputStream> GetContent(string uriPath)
{
try
{
Uri localUri = new Uri("ms-appdata:///local" + uriPath);
StorageFile file = await StorageFile.GetFileFromApplicationUriAsync(localUri);
IRandomAccessStream stream = await file.OpenReadAsync();
return stream.GetInputStreamAt(0);
}
catch (Exception)
{
throw new Exception("Invalid path");
}
}
}

IsolatedStorage best practise

I am building my first WP8 application and I ran into a doubt.
I have to save in the IsolatedStorage some data obtained by serializing three different object.
My code for loading this data is this:
public static Statistics GetData()
{
Statistics data = new Statistics();
try
{
using (IsolatedStorageFile myIsolatedStorage = IsolatedStorageFile.GetUserStoreForApplication())
{
using (IsolatedStorageFileStream stream = myIsolatedStorage.OpenFile("stats.xml", FileMode.OpenOrCreate))
{
XmlSerializer serializer = new XmlSerializer(typeof(Statistics));
data = (Statistics)serializer.Deserialize(stream);
}
}
}
catch (Exception e)
{
MessageBox.Show(e.Message + "\n" + e.InnerException);
}
return data;
}
And for saving data of course is this
public static void SaveStats(Statistics stats)
{
XmlWriterSettings xmlWriterSettings = new XmlWriterSettings();
xmlWriterSettings.Indent = true;
try
{
using (IsolatedStorageFile myIsolatedStorage = IsolatedStorageFile.GetUserStoreForApplication())
{
using (IsolatedStorageFileStream stream = myIsolatedStorage.OpenFile("stats.xml", FileMode.Create))
{
XmlSerializer serializer = new XmlSerializer(typeof(Statistics));
using (XmlWriter xmlWriter = XmlWriter.Create(stream, xmlWriterSettings))
{
serializer.Serialize(xmlWriter, stats);
}
}
}
}
catch
{
MessageBox.Show("Salvataggio non riuscito");
}
}
This works fine, now the problem is that I have to do the same also for other two classes.
Do I have to write the same exact code again, only changing Statistics with the other class?
Or there is something way more clever to do?
Take a look at Generics.
Your serialize method would look like this:
public static void SaveStats<T>(T obj) where T : class, new()
{
...
XmlSerializer serializer = new XmlSerializer(typeof(T));
...
}
Method invoke:
SaveStats<Statistics>(new Statistics());
SaveStats<OtherObject>(new OtherObject());

Infinite wait loading remote image into BitmapImage() in Background Agent

I have a valid URL for a remote JPEG which I'm trying to load in the background. But I find I never get control back after invoking the BitmapImage() constructor. My question is, should this approach work, or should I pitch it all, load up BcpAsync project from NuGet and start working with WebClient asynch methods?
A sample URL for which it fails is
http://image.weather.com/images/maps/current/garden_june_720x486.jpg
It is valid. .UpdateAsync() references it from AppViewModel.Instance, it's not explicitly referenced here.
Here's the background agent:
protected override async void OnInvoke(ScheduledTask task)
{
AppViewModel.LoadData();
await AppViewModel.Instance.RemoteImageProxy.UpdateAsync();
AppViewModel.Instance.ImageUrl = AppViewModel.Instance.RemoteImageProxy.LocalFileUri;
AppViewModel.Instance.UpdateCount++;
PinnedTile.Update();
}
AppViewModel.SaveData();
#if DEBUG
ScheduledActionService.LaunchForTest(task.Name, TimeSpan.FromSeconds(AppViewModel.Instance.BgAgentInterval));
#endif
NotifyComplete();
}
Here's the invoked method:
public Task<double> UpdateAsync() {
LastCheckedTime = DateTime.UtcNow;
CompletionTask = new TaskCompletionSource<double>();
// Not usually called on UI thread, not worth optimizing for that case here.
Deployment.Current.Dispatcher.BeginInvoke(() => { //todo determine whether System.Windows.Deployment.Dispatcher can be called from main app, or just bgAgent.
HelperImageControl = new Image();
HelperImageControl.Loaded += im_Loaded;
HelperImageControl.ImageFailed += im_ImageFailed;
HelperImageControl.ImageOpened += im_ImageOpened;
// breakpoint here
HelperImageControl.Source = new BitmapImage(SourceUri);
// stepping over the function, control does not return here. Nor are any of the above events fired.
});
return CompletionTask.Task; // this will be completed in one of the subsequent control events...
}
You need to call CompletionTask.SetResult(); to return control back to the caller method.
This works (I'm returning 100 in case of successful download because you set the task to return double).
TaskCompletionSource<double> CompletionTask;
public Task<double> UpdateAsync()
{
CompletionTask = new TaskCompletionSource<double>();
Deployment.Current.Dispatcher.BeginInvoke(() =>
{
var HelperImageControl = new Image();
var bmp = new BitmapImage();
bmp.ImageOpened += bmp_ImageOpened;
bmp.ImageFailed += bmp_ImageFailed;
bmp.CreateOptions = BitmapCreateOptions.None;
bmp.UriSource = new Uri("http://image.weather.com/images/maps/current/garden_june_720x486.jpg", UriKind.Absolute);
HelperImageControl.Source = bmp;
});
return CompletionTask.Task;
}
void bmp_ImageFailed(object sender, ExceptionRoutedEventArgs e)
{
CompletionTask.SetException(e.ErrorException);
}
void bmp_ImageOpened(object sender, RoutedEventArgs e)
{
CompletionTask.SetResult(100);
}

MvvmCross: NotImplementedException calling EnsureFolderExists method of IMvxFileStore

I'm developing my first Windows Store App, using MvvmCross framework and I have a problem with images management. In particular I have the following simple ViewModel in my PCL project, and a Store project with a button bound with AddPictureCommand.
public class FirstViewModel : MvxViewModel
{
IMvxPictureChooserTask _pictureChooserTask;
IMvxFileStore _fileStore;
public FirstViewModel(IMvxPictureChooserTask pictureChooserTask, IMvxFileStore fileStore)
{
_pictureChooserTask = pictureChooserTask;
_fileStore = fileStore;
}
private byte[] _pictureBytes;
public byte[] PictureBytes
{
get { return _pictureBytes; }
set
{
if (_pictureBytes == value) return;
_pictureBytes = value;
RaisePropertyChanged(() => PictureBytes);
}
}
public ICommand AddPictureCommand
{
get { return new MvxCommand(() =>
{
_pictureChooserTask.ChoosePictureFromLibrary(400, 95, pictureAvailable, () => { });
}); }
}
private void pictureAvailable(Stream stream)
{
MemoryStream memoryStream = new MemoryStream();
stream.CopyTo(memoryStream);
PictureBytes = memoryStream.ToArray();
GenerateImagePath();
}
private string GenerateImagePath()
{
if (PictureBytes == null) return null;
var RandomFileName = "Image" + Guid.NewGuid().ToString("N") + ".jpg";
_fileStore.EnsureFolderExists("Images");
var path = _fileStore.PathCombine("Images", RandomFileName);
_fileStore.WriteFile(path, PictureBytes);
return path;
}
}
The problem is that the method _fileStore.EnsureFolderExists("Images");
gives me the an "NotImplementedException" with message: "Need to implement this - doesn't seem obvious from the StorageFolder API".
Has anyone already seen it before?
Thank you
This not implemented exception is documented in the wiki - see https://github.com/MvvmCross/MvvmCross/wiki/MvvmCross-plugins#File
It should be fairly straightforward to implement these missing methods if they are required. Indeed I know of at least 2 users that have implemented these - but sadly they've not contributed them back.
to implement them, just
fork (copy) the code from https://github.com/MvvmCross/MvvmCross/blob/v3/Plugins/Cirrious/File/Cirrious.MvvmCross.Plugins.File.WindowsStore/MvxWindowsStoreBlockingFileStore.cs
implement the missing methods using the winrt StorageFolder apis
in your Store UI project, don't load the File plugin - so comment out or remove the File bootstrap class.
during setup, register your implementation with ioc using Mvx.RegisterType - e.g.:
protected override void InitializeFirstChance()
{
base.InitializeFirstChance();
Cirrious.CrossCore.Mvx.RegisterType<IMvxFileStore, MyFileStore>();
}
For more on using ioc, see https://github.com/MvvmCross/MvvmCross/wiki/Service-Location-and-Inversion-of-Control
For more on customising the setup sequence, see https://github.com/MvvmCross/MvvmCross/wiki/Customising-using-App-and-Setup
Following Stuart's suggestions I've implemented the following methods for Windows 8 Store App:
public bool FolderExists(string folderPath)
{
try
{
var directory = ToFullPath(folderPath);
var storageFolder = StorageFolder.GetFolderFromPathAsync(directory).Await();
}
catch (FileNotFoundException)
{
return false;
}
catch (Exception ex)
{
MvxTrace.Trace("Exception in FolderExists - folderPath: {0} - {1}", folderPath, ex.ToLongString());
throw ex;
}
return true;
//throw new NotImplementedException("Need to implement this - See EnsureFolderExists");
}
public void EnsureFolderExists(string folderPath)
{
try
{
var directory = ToFullPath(folderPath);
var storageFolder = StorageFolder.GetFolderFromPathAsync(directory).Await();
}
catch (FileNotFoundException)
{
var localFolder = ToFullPath(string.Empty);
var storageFolder = StorageFolder.GetFolderFromPathAsync(localFolder).Await();
storageFolder.CreateFolderAsync(folderPath).Await();
}
catch (Exception ex)
{
MvxTrace.Trace("Exception in EnsureFolderExists - folderPath: {0} - {1}", folderPath, ex.ToLongString());
throw ex;
}
//throw new NotImplementedException("Need to implement this - doesn't seem obvious from the StorageFolder API");
//var folder = StorageFolder.GetFolderFromPathAsync(ToFullPath(folderPath)).Await();
}
The third method we need to implement is DeleteFolder(string folderPath, bool recursive). Unfortunately StorageFolder method "DeleteFolder" doesn't have a "recursive" parameter. So I should implement DeleteFolder ignoring it:
public void DeleteFolder(string folderPath, bool recursive)
{
try
{
var directory = ToFullPath(folderPath);
var storageFolder = StorageFolder.GetFolderFromPathAsync(directory).Await();
storageFolder.DeleteAsync().Await();
}
catch (FileNotFoundException)
{
//Folder doesn't exist. Nothing to do
}
catch (Exception ex)
{
MvxTrace.Trace("Exception in DeleteFolder - folderPath: {0} - {1}", folderPath, ex.ToLongString());
throw ex;
}
//throw new NotImplementedException("Need to implement this - See EnsureFolderExists");
}
or I should check if the folder is empty before to delete it if "recursive" equals false.
Better implementations are welcomed.

ASync JSON REST call problem with MVVM

I am trying to implement the MVVM patter for my WP7 Silverlight app and I am running into a problem with the async JSON Rest call. I moved into my ViewModel class the following two methods that were on my WP7 app Page.
public void FetchGames()
{
ObservableCollection<Game> G = new ObservableCollection<Game>();
//REST call in here
var webClient = new WebClient();
Uri uri = new Uri("http://www.somewebsite.com/get/games/league/" + league);
webClient.OpenReadCompleted += new OpenReadCompletedEventHandler(OpenReadCompletedGames);
webClient.OpenReadAsync(uri);
}
private void OpenReadCompletedGames(object sender, OpenReadCompletedEventArgs e)
{
DataContractJsonSerializer ser = null;
ser = new DataContractJsonSerializer(typeof(ObservableCollection<Game>));
Games = ser.ReadObject(e.Result) as ObservableCollection<Game>;
this.IsDataLoaded = true;
}
Now the problem is that because it is an async call the following code does not work. The following code is on my app Page.
protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
{
base.OnNavigatedTo(e);
if (NavigationContext.QueryString.TryGetValue("league", out league))
{
try
{
App.gViewModel.league = league;
App.gViewModel.FetchGames();
if(App.gViewModel.IsDataLoaded)
{
lbTeams.ItemsSource = App.gViewModel.Games;
}
}
catch ()
{
//error logging in here
}
}
}
Stepping thru the code shows that FetchGames is called then hits the next line ( if(App.gViewModel.IsDataLoaded)
) before the async call is finished. So IsDataLoaded is always false and I cant bind the listbox on the page.
Doing a lot of googleing I have some possible solutions but I am unable convert them to my particular problem. One is like this and it has to do with continuation passing style'. I couldn't get it to work tho and would greatly appreciate some help.
Thanks!
void DoSomethingAsync( Action<string> callback ) {
HttpWebRequest req; // TODO: build your request
req.BeginGetResponse( result => {
// This anonymous function is a closure and has access
// to the containing (or enclosing) function.
var response = req.EndGetResponse( result );
// Get the result string and call the callback
string resultString = null; // TODO: read from the stream
callback(resultString);
}, null );
}
This can be resolved by moving
lbTeams.ItemsSource = App.gViewModel.Games;
to the end of the OpenReadCompletedGames method. You'll need to use the Dispatcher to update the UI from here.
Dispatcher.BeginInvoke( () => { lbTeams.ItemsSource = App.gViewModel.Games; } );