So I have a method implemented in my application that downloads images for a given manga from a site, and places said images into the app's AppData (localstate) folder, in a language
folder inside a title
folder.
Image downloading works perfectly, aside from the fact that, if I try download multiple image sets at once, the results are mixed among the folders involved.
The method that deals with File IO:
private async Task<bool> SaveManga(Data.Posts.Content2 manga)
{
var folder = ApplicationData.Current.LocalFolder;
var safeName = Path.GetInvalidFileNameChars().Aggregate(manga.ContentInfo.ContentName, (current, c) => current.Replace(c, '_').Replace('.', '_'));
// Create folder for issue if does not exist
var issueFolder = await ApplicationData.Current.LocalFolder.CreateFolderAsync(safeName, CreationCollisionOption.OpenIfExists);
// Create folder for language if does not exist
var langFolder = await issueFolder.CreateFolderAsync(manga.ContentInfo.ContentLanguage, CreationCollisionOption.OpenIfExists);
// Populate pages if page count is 0
if (manga.Pages == null)
manga.Pages = new Dictionary<int, Data.Posts.Page>();
if (manga.Pages.Count == 0)
{
if (NetworkInterface.GetIsNetworkAvailable() == false)
{
}
var pages = Fakku.GetMangaInfo(manga.ContentInfo.ContentUrl).Result.Pages;
for (var i = 0; i < pages.Count; i++)
{
var page = new Data.Posts.Page { Image = pages.Values.ElementAt(i).Image, Thumb = pages.Values.ElementAt(i).Thumb };
manga.Pages.Add(i, page);
}
}
var files = await langFolder.GetItemsAsync();
var fileCount = files.Count;
if (fileCount + 3 == manga.ContentInfo.ContentPages) return true;
var cbi = new BitmapImage(new Uri(manga.ContentInfo.ContentImages.Cover));
const string cname = "cover.jpg";
var wcbi = await WriteableBitmapFromBitmapImageExtension.FromBitmapImage(cbi);
await wcbi.SaveToFile(langFolder, cname, CreationCollisionOption.OpenIfExists);
var sbi = new BitmapImage(new Uri(manga.ContentInfo.ContentImages.Sample));
const string sname = "sample.jpg";
var wsbi = await WriteableBitmapFromBitmapImageExtension.FromBitmapImage(sbi);
await wsbi.SaveToFile(langFolder, sname, CreationCollisionOption.OpenIfExists);
for (var i = 1; i < manga.ContentInfo.ContentPages; i++)
{
var bi = new BitmapImage(new Uri(manga.Pages[i].Image));
//var x = await langFolder.CreateFileAsync(String.Format("{0}.png", i), CreationCollisionOption.ReplaceExisting);
var imgName = String.Format("{0}.jpg", i);
try
{
var wbi = await WriteableBitmapFromBitmapImageExtension.FromBitmapImage(bi);
await wbi.SaveToFile(langFolder, imgName, CreationCollisionOption.OpenIfExists);
}
catch (Exception e)
{
var hResult = (uint)e.HResult;
if (hResult.Equals(0x80190194))
{
new MessageDialog("Error loading pages. \n If this is a sample manga, this behavior is to be expected.").ShowAsync();
break;
}
}
}
return true;
}
And the implementation:
private async void SaveButton_Click(object sender, RoutedEventArgs e)
{
SaveButton.IsEnabled = false;
if (isSaved)
{
var i = defaultViewModel.ContentInfo;
Fakku.SavedManga.RemoveAll(x => x.ContentInfo.ContentUrl == i.ContentUrl);
isSaved = !isSaved;
SaveButton.Content = "Save";
var manga = defaultViewModel;
await RemoveManga(manga);
await ProcessSaved();
SaveButton.IsEnabled = true;
}
else if (!isSaved)
{
Fakku.SavedManga.Add(defaultViewModel);
isSaved = true;
SaveButton.Content = "Remove";
try
{
var manga = defaultViewModel;
//pendingWork = SaveManga(manga);
_saves.Enqueue(manga);
await AsyncParallelForEach(
_saves, async save => await SaveManga(manga), 8,
TaskScheduler.FromCurrentSynchronizationContext());
//HandleSaving();
await ProcessSaved();
}
catch (NullReferenceException)
{
}
SaveButton.IsEnabled = true;
}
}
AsParallel function:
private static Task AsyncParallelForEach<T>(
IEnumerable<T> source, Func<T, Task> body,
int maxDegreeOfParallelism = DataflowBlockOptions.Unbounded,
TaskScheduler scheduler = null)
{
var options = new ExecutionDataflowBlockOptions
{
MaxDegreeOfParallelism = maxDegreeOfParallelism
};
if (scheduler != null)
options.TaskScheduler = scheduler;
var block = new ActionBlock<T>(body, options);
foreach (var item in source)
block.Post(item);
block.Complete();
return block.Completion;
}
The usage of the parallel foreach is my most recent attempt, taking from this article, but the results mimic what I described above, and I feel I am still missing something very important.
Could anyone point me in the right direction? I'm trying to find a way in which I can either process these downloads, i.e. multiple instances of the SaveManga method simultaneously, or at least queue them for completion.
Edit: I'm starting to realize what I need to focus on is parallelization, not asynchronicity, if I want to run the downloads simultaneously, which is what I'd prefer.
I think the problem is that you are parallelizing the initialization (folder creation + page count) too and then it mix up everyting.
I would recommend, doing the initialization first :
public async Task InitAsync(Data.Posts.Content2 manga){
var folder = ApplicationData.Current.LocalFolder;
var safeName = Path.GetInvalidFileNameChars().Aggregate(manga.ContentInfo.ContentName, (current, c) => current.Replace(c, '_').Replace('.', '_'));
// Create folder for issue if does not exist
var issueFolder = await ApplicationData.Current.LocalFolder.CreateFolderAsync(safeName, CreationCollisionOption.OpenIfExists);
// Create folder for language if does not exist
var langFolder = await issueFolder.CreateFolderAsync(manga.ContentInfo.ContentLanguage, CreationCollisionOption.OpenIfExists);
// Populate pages if page count is 0
if (manga.Pages == null)
manga.Pages = new Dictionary<int, Data.Posts.Page>();
if (manga.Pages.Count == 0)
{
var pages = Fakku.GetMangaInfo(manga.ContentInfo.ContentUrl).Result.Pages;
for (var i = 0; i < pages.Count; i++)
{
var page = new Data.Posts.Page { Image = pages.Values.ElementAt(i).Image, Thumb = pages.Values.ElementAt(i).Thumb };
manga.Pages.Add(i, page);
}
}
var cbi = new BitmapImage(new Uri(manga.ContentInfo.ContentImages.Cover));
const string cname = "cover.jpg";
var wcbi = await WriteableBitmapFromBitmapImageExtension.FromBitmapImage(cbi);
await wcbi.SaveToFile(langFolder, cname, CreationCollisionOption.OpenIfExists);
var sbi = new BitmapImage(new Uri(manga.ContentInfo.ContentImages.Sample));
const string sname = "sample.jpg";
var wsbi = await WriteableBitmapFromBitmapImageExtension.FromBitmapImage(sbi);
await wsbi.SaveToFile(langFolder, sname, CreationCollisionOption.OpenIfExists);
}
and then only doing some parallelization like this, because the framework will be smart enough to manage the threads :) :
List<Task> tasksToWait = new List<Task>();
for (var i = 1; i < manga.ContentInfo.ContentPages; i++)
{
taskToWait.Add(Task.Run(()=>SaveMangaPage(i));
}
await Task.WhenAll(tasksToWait );
await ProcessSaved();
With SaveMangaPage being :
public async Task SaveMangaPage(int pageNumber) {
var bi = new BitmapImage(new Uri(manga.Pages[pageNumber].Image));
//var x = await langFolder.CreateFileAsync(String.Format("{0}.png", pageNumber), CreationCollisionOption.ReplaceExisting);
var imgName = String.Format("{0}.jpg", pageNumber);
try
{
var wbi = await WriteableBitmapFromBitmapImageExtension.FromBitmapImage(bi);
await wbi.SaveToFile(langFolder, imgName, CreationCollisionOption.OpenIfExists);
}
catch (Exception e)
{
var hResult = (uint)e.HResult;
if (hResult.Equals(0x80190194))
{
new MessageDialog("Error loading pages. \n If this is a sample manga, this behavior is to be expected.").ShowAsync();
break;
}
}
}
}
User contributions licensed under CC BY-SA 3.0