Issues with background downloader Windows Phone 8.1 - c#

I am having issues downloading multiple files with the Background Downloader. I'm getting an "Object not set to an instance of an Object" error in my HandleDownloadAsync method. Here is my code.
private async Task StartDownload(List<DownloadData> data)
{
foreach (DownloadData song in data)
{
Uri source = new Uri(song.downloadUrl);
// Create folder stucture
StorageFolder artistFolder = await KnownFolders.MusicLibrary.CreateFolderAsync(song.artistName, CreationCollisionOption.OpenIfExists);
StorageFolder releaseFolder = await artistFolder.CreateFolderAsync(song.releaseName, CreationCollisionOption.OpenIfExists);
// Create file
StorageFile destinationFile;
try
{
destinationFile = await releaseFolder.CreateFileAsync(song.fileName, CreationCollisionOption.GenerateUniqueName);
}
catch
{
throw;
}
BackgroundDownloader downloader = new BackgroundDownloader();
DownloadOperation download = downloader.CreateDownload(source, destinationFile);
List<DownloadOperation> requestOperations = new List<DownloadOperation>();
requestOperations.Add(download);
await HandleDownloadAsync(download, true);
}
}
and the method
private async Task HandleDownloadAsync(DownloadOperation download, bool start)
{
try
{
// Store the download for pause/resume
activeDownloads.Add(download); // Error occurs here
Progress<DownloadOperation> progressCallback = new Progress<DownloadOperation>(DownloadProgress);
if (start)
{
await download.StartAsync().AsTask(cts.Token, progressCallback);
}
else
{
await download.AttachAsync().AsTask(cts.Token, progressCallback);
}
}
catch
{
throw;
}
finally
{
activeDownloads.Remove(download);
}
}
The error get thrown when trying to add the download to activeDownloads. Most of the code is from This MSDN sample, but I added the foreach loop to download multiple items.

Seems like you may have forgotten to initalize the activeDownloads list in your StartDownload method, for example like so:
activeDownloads = new List<DownloadOperation>();

Related

Xamarin: How to delete files using CrossFilePicker Xamarin Forms

I am using the following code to pick a file using CrossFilePicker in Xamarin forms. Goal is to delete the selected file in Xamarin.Android project, file still intact after running the command.
FileData filedata = await CrossFilePicker.Current.PickFile();
Android.Net.Uri uri = Android.Net.Uri.Parse(filedata.FilePath);
Java.IO.File file = new Java.IO.File(uri.Path);
File.Delete(file.AbsolutePath);
await DisplayAlert("✅", file.AbsolutePath, "Okay");
When using the DisplayAlert to check the filepath, I saw the below path, but file doesn't delete!!
/storage/emulated/0/documents/SalesRecords.db3
I recommend you to use Essentials.FilePicker, it allows you to pick file with code like:
async Task<FileResult> PickAndShow(PickOptions options)
{
try
{
var result = await FilePicker.PickAsync(options);
if (result != null)
{
Text = $"File Name: {result.FileName}";
if (result.FileName.EndsWith("jpg", StringComparison.OrdinalIgnoreCase) ||
result.FileName.EndsWith("png", StringComparison.OrdinalIgnoreCase))
{
var stream = await result.OpenReadAsync();
Image = ImageSource.FromStream(() => stream);
}
}
return result;
}
catch (Exception ex)
{
// The user canceled or something went wrong
}
return null;
}
And once you get the result you can delete the file like:
FileResult file= await PickAndShow(PickOptions.Default);
File.Delete(file.FullPath);

How to write the contents of a Onedrive<Item> to a local file

In my app, I am using OneDrive to keep data in sync. I am successfully writing the file to OneDrive, but am having no luck replacing the local outdated data with the newer OneDrive data.
My current method, which completes without throwing an exception, does not return the same text data that the file on OneDrive contains.
Goal of the method is to compare the datemodified to the OneDrive file to the local file, and if OneDrive is newer, write the contents of the OndeDrive file to the local StorageFile, and then return it to be de-serialized.
private async Task<string> GetSavedDataFileAsync(string filename)
{
string filepath = _appFolder + #"\" + KOWGame + #"\" + filename;
StorageFile localread;
BasicProperties localprops = null;
string txt;
try
{
localread = await local.GetFileAsync(filepath);
localprops = await localread.GetBasicPropertiesAsync();
}
catch (FileNotFoundException)
{ localread = null; }
if (_userDrive != null)
{
if (_userDrive.IsAuthenticated)
{
try
{
Item item = await _userDrive.Drive.Special.AppRoot.ItemWithPath(filepath).Request().GetAsync();
if (item != null)
{
DateTimeOffset drivemodified = (DateTimeOffset)item.FileSystemInfo.LastModifiedDateTime;
if (localprops != null)
{
if (drivemodified > localprops.DateModified)
{
Stream stream = await localread.OpenStreamForWriteAsync();
using (stream)
{ await _userDrive.Drive.Special.AppRoot.ItemWithPath(filepath).Request().GetAsync(); }
}
}
}
}
catch (OneDriveException e)
{
if (e.IsMatch(OneDriveErrorCode.ActivityLimitReached.ToString()))
{ string stop; }
}
}
}
if (localread == null) return string.Empty;
txt = await FileIO.ReadTextAsync(localread);
return txt;
}
I tried to reverse engineer another answer I found on Stack regarding writing a StorageFile to OneDrive, in that I needed to open the stream of the local file, but I doesn't appear to be working properly.
To get the content of a OneDrive item, we need use following method:
var contentStream = await _userDrive.Drive.Special.AppRoot.ItemWithPath(filepath).Content.Request().GetAsync();
While using
await _userDrive.Drive.Special.AppRoot.ItemWithPath(filepath).Request().GetAsync();
you are getting the OneDrive Item not its content.
So you can change your code like following to write the content of a Onedrive item to a local file:
if (drivemodified > localprops.DateModified)
{
using (var stream = await localread.OpenStreamForWriteAsync())
{
using (var contentStream = await _userDrive.Drive.Special.AppRoot.ItemWithPath(filepath).Content.Request().GetAsync())
{
contentStream.CopyTo(stream);
}
}
}

System.UnauthorizedAccessException: Access is denied. Windows Phone while using files

i'm getting a "System.UnauthorizedAccessException: Access is denied. " message when im trying to save a list on the Windows Phone.
Scenario:
When clicked on the FavoriteButton, the system will check if the item is already a favorite.
The next step is to call the Load method. This will return a list with favorites.
When the item is favorite I will remove it from the list, otherwise i will add it.
Final step is to save the new list with my Save method.
When executing this code i get the following exception:
System.UnauthorizedAccessException: Access is denied. (Exception from HRESULT: 0x80070005 (E_ACCESSDENIED
I've looked around and saw that it could be the manifest capabilities. So i have checked them all just to be sure. After trying again I still get the exception!
What must i do? code below:
private async void favoriteClicked(object sender, EventArgs e)
{
if (_progressIndicator.IsVisible == false)
{
try
{
Boolean isFavorite = Settings.GetValueOrDefault<Boolean>(currentArtist.id, false);
ArtistSaving favorite = new ArtistSaving(currentArtist.id, currentArtist.name, currentArtist.shortBio);
artistList = await Task.Run(() => Utils.Load<ArtistSaving>());
if (isFavorite == true)
{
artistList.Remove(favorite);
displayDeletePopup();
Settings.AddOrUpdateValue(currentArtist.id, false);
await Task.Run(() => Utils.Save<ArtistSaving>(artistList));
}
else
{
artistList.Add(favorite);
displayXamlPopup();
Settings.AddOrUpdateValue(currentArtist.id, true);
await Task.Run(() => Utils.Save<ArtistSaving>(artistList));
}
}
catch (ArgumentException ex)
{
}
}
}
Favorite button click
public static async Task<ObservableCollection<T>> Load<T>()
{
// Create a new folder name DataFolder.
var file = await Windows.Storage.ApplicationData.Current.LocalFolder.CreateFileAsync(JSONFILENAME,
CreationCollisionOption.OpenIfExists);
var jsonSerializer = new DataContractJsonSerializer(typeof(ObservableCollection<T>));
var myStream = await file.OpenStreamForReadAsync();
ObservableCollection<T> myObj = (ObservableCollection<T>)jsonSerializer.ReadObject(myStream);
if (myObj == null)
{
return new ObservableCollection<T>();
}
return myObj;
}
This is the Load method
public static async void Save<T>(ObservableCollection<T> obj)
{
var serializer = new DataContractJsonSerializer(typeof(ObservableCollection<T>));
using (var stream = await ApplicationData.Current.LocalFolder.OpenStreamForWriteAsync(
JSONFILENAME, CreationCollisionOption.ReplaceExisting))
{
serializer.WriteObject(stream, obj);
}
}
and this was the Save method
EDIT -- SOLUTION
public static async Task<ObservableCollection<T>> Load<T>()
{
ObservableCollection<T> myObj = null;
// Create a new folder name DataFolder.
var file = await Windows.Storage.ApplicationData.Current.LocalFolder.CreateFileAsync(JSONFILENAME,
CreationCollisionOption.OpenIfExists);
var jsonSerializer = new DataContractJsonSerializer(typeof(ObservableCollection<T>));
using (var myStream = await file.OpenStreamForReadAsync())
{
myObj = (ObservableCollection<T>)jsonSerializer.ReadObject(myStream);
if (myObj == null)
{
return new ObservableCollection<T>();
}
return myObj;
}
}
Most likely the file is still locked from when you read from it. Modify your Load method like this to release the file lock:
using (var myStream = await file.OpenStreamForReadAsync())
{
ObservableCollection<T> myObj = (ObservableCollection<T>)jsonSerializer.ReadObject(myStream);
}

0x80070005 (E_ACCESSDENIED) on Windows 8.1 Store app while replacing/opening a hidden file

I'm getting "Access is denied. (Exception from HRESULT: 0x80070005 (E_ACCESSDENIED))" error when I'm trying to replace an existing hidden file. I cannot put conditions to check the attributes, as this file may or may not be hidden at times. here is the code snippent i'm using:
private static readonly SemaphoreSlim slimWrite = new SemaphoreSlim(initialCount: 1);
public void CreateFile(string fileName, string content)
{
StorageFolder storageFolder = ApplicationData.Current.LocalFolder;
try
{
slimWrite.WaitAsync();
var localFolderTask = storageFolder.CreateFolderAsync(dirName, CreationCollisionOption.OpenIfExists);
localFolderTask.AsTask().Wait(timeout); //timeout = 10000
if (localFolderTask.Status == AsyncStatus.Completed)
{
var localFolder = localFolderTask.GetResults();
if (localFolder != null)
{
var fileTask = localFolder.CreateFileAsync(fileName, Windows.Storage.CreationCollisionOption.ReplaceExisting);
fileTask.AsTask().Wait(timeout);
if (fileTask.Status == AsyncStatus.Completed)
{
var file = fileTask.GetResults();
var writeTask = FileIO.WriteTextAsync(file, content);
writeTask.AsTask().Wait(timeout);
if (writeTask.Status == AsyncStatus.Completed)
{//Job complete; write to cache}
}
}
}
}
catch (Exception)
{ Log here... }
finally
{
slimWrite.Release();
}
}
the error is raised by the CreateFileAsync(...) line. Did anyone faced similar problems with Windows 8.1 Store projects?
EDIT: The same error occurs when I'm opening (GetFileAsync()) a hidden file too!

Make a C# Windows Store app wait for an unzip to finish

I'm trying to make an epub parsing app in a Windows Store with C#, and it won't wait for the archive (epubs are actually zip files) to finish extracting before it tries to parse the not-yet-existing table of contents. How do I make my app be a bit more patient?
I've tried making my UnZip() function return a task and having the epub constructor (epub is a class) use UnZip().Wait(), but that just freezes the app. What do I do?
Edit: Here's my relevant code:
public class epub
{
public string filename;
private StorageFolder unzipFolder;
private IList<epubChapter> _contents;
private bool _parsed = false;
public bool parsed { get { return _parsed; } } //Epub and contents are fully parsed
public epub(string newFilename)
{
_contents = new List<epubChapter>();
filename = newFilename;
UnZipFile().Wait();
getTableOfContents();
}
private async Task UnZipFile()
{
var sourceFolder = Windows.ApplicationModel.Package.Current.InstalledLocation;
StorageFolder localFolder = ApplicationData.Current.LocalFolder;
unzipFolder = await localFolder.CreateFolderAsync(filename, CreationCollisionOption.OpenIfExists);
using (var zipStream = await sourceFolder.OpenStreamForReadAsync(filename))
{
using (MemoryStream zipMemoryStream = new MemoryStream((int)zipStream.Length))
{
await zipStream.CopyToAsync(zipMemoryStream);
using (var archive = new ZipArchive(zipMemoryStream, ZipArchiveMode.Read))
{
foreach (ZipArchiveEntry entry in archive.Entries)
{
if (entry.Name != "")
{
using (Stream fileData = entry.Open())
{
try
{
await unzipFolder.GetFileAsync(entry.Name);
Debug.WriteLine("File at {0} already exists", entry.Name);
continue;
}
catch (FileNotFoundException)
{
Debug.WriteLine("Creating file {0}", entry.Name);
}
StorageFile outputFile = await unzipFolder.CreateFileAsync(entry.Name, CreationCollisionOption.OpenIfExists);
//Debug.WriteLine("Output file created at {0}", outputFile.Path);
using (Stream outputFileStream = await outputFile.OpenStreamForWriteAsync())
{
await fileData.CopyToAsync(outputFileStream);
await outputFileStream.FlushAsync();
}
}
if (entry.Name == "toc.ncx")
{
Debug.WriteLine("toc.ncx found in epub file; parsing it");
getTableOfContents();
}
}
}
}
}
}
}
public void getTableOfContents()
{
string contentsPath = unzipFolder.Path + #"\toc.ncx"; //The file is always called this in valid epubs
try
{
XDocument toc = XDocument.Load(contentsPath);
string nameSpace = getNameSpace(toc);
XElement navMap = firstElementNamed(toc.Root, "navMap");
parseNavPoints(navMap, nameSpace, 0);
_parsed = true;
}
catch(FileNotFoundException)
{
Debug.WriteLine("File toc.ncx was not found!");
}
}
Basically, your question seems to be: How do I call an async method from a constructor?
The short answer is that you don't, instead create an async factory method for your class.
Longer answer: As you noticed, if you call Wait(), your code will block. You can't use await, because constructors can't be async. And if you don't do anything, the constructor is going to return too early.
The solution here is to use an async factory method instead of a constructor. Something like:
private epub(string newFilename)
{
_contents = new List<epubChapter>();
filename = newFilename;
}
public static async Task<epub> CreateAsync(string newFilename)
{
var result = new epub(newFilename);
await result.UnZipFile();
result.getTableOfContents();
return result;
}
For some more information and alternative solutions, see Stephen Cleary's article about async and contructors.

Categories

Resources