Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C# async file transfer - waiting before continuing loop

I am trying to get my head around the changes in .NET 4.5, mainly the async features. To get my head around it I thought i would create a little app for archiving my massive photo collection. I learn best by doing so the application serves a double purpose.

I have read plenty MSDN articles on using async but I don't think I have a good enough understanding of it (because it's not working). My intention was to have each photo at a source folder copied to a destination folder based on its date taken (or created if taken meta data is missing). At the same time renaming it to a standard naming convention and showing the image as it is archived in an image box. I wanted the application to keep responding during the work, which is where async comes in. Now the app purpose is unimportant, the entire point was getting my head around async.

What actually happens is the app goes unresponsive, archives all the images as intended but the image box only shows the final picture. Async is kicking off the file transfer then moving on to the next image, kicking off the transfer then moving on etc etc so i end up with hundreds of open file streams rather than it waiting for each to close.

Any pointers in where I am going wrong would be appreciated. My understanding of using Tasks is shakey, returning a task serves what purpose?

imgMain is the imagebox in the XAML file. The async/await is in the archive method but showing all code as it may be relevant.

using System;
using System.Drawing.Imaging;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Windows;
using System.Windows.Media.Imaging;
using System.Windows.Forms;
using System.IO;

namespace PhotoArchive
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{

    private string Source 
    {
        get { return txtSource.Text; }
        set { txtSource.Text = value; }
    }

    private string Destination
    {
        get { return txtDestination.Text; }
        set { txtDestination.Text = value; }
    }


    public MainWindow()
    {
        InitializeComponent();

    }

    private void btnBrowseDataSource_Click(object sender, RoutedEventArgs e)
    {
        var dialogue = new FolderBrowserDialog();
        dialogue.ShowDialog();
        Source = dialogue.SelectedPath;

    }

    private void btnBrowseDestination_Click(object sender, RoutedEventArgs e)
    {
        var dialogue = new FolderBrowserDialog();
        dialogue.ShowDialog();
        Destination= dialogue.SelectedPath;
    }

    private void btnSort_Click(object sender, RoutedEventArgs e)
    {
        var files = Directory.GetFiles(Source, "*.*", SearchOption.AllDirectories);
        var result = from i in files
                     where i.ToLower().Contains(".jpg") || i.ToLower().Contains(".jpeg") || i.ToLower().Contains(".png")
                     select i;


        foreach (string f in result)
        {
            DateTime dest = GetDateTakenFromImage(f);
            Archive(f, Destination, dest);
        }

    }

    private async void Archive(string file, string destination, DateTime taken)
    {

        //Find Destination Path
        var sb = new StringBuilder();
        sb.Append(destination);
        sb.Append("\\");
        sb.Append(taken.ToString("yyyy"));
        sb.Append("\\");
        sb.Append(taken.ToString("MM"));
        sb.Append("\\");

        if (! Directory.Exists(sb.ToString()))
        {
            Directory.CreateDirectory(sb.ToString());
        }

        sb.Append(taken.ToString("dd_MM_yyyy_H_mm_ss_"));
        sb.Append((Directory.GetFiles(destination, "*.*", SearchOption.AllDirectories).Count()));
        string[] extension = file.Split('.');
        sb.Append("." + extension[extension.Length-1]);


        using (FileStream fs = File.Open(file, FileMode.Open))
        using (FileStream ds = File.Create(sb.ToString())) 
        {
            await fs.CopyToAsync(ds);
            fs.Close();
            File.Delete(file);
        }

        ImgMain.Source = new BitmapImage(new Uri(sb.ToString()));
    }

    //get date info
    private static Regex r = new Regex(":");

    public static DateTime GetDateTakenFromImage(string path)
    {
        using (var fs = new FileStream(path, FileMode.Open, FileAccess.Read))
        {
            using (System.Drawing.Image img = System.Drawing.Image.FromStream(fs, false, false))
            {
                PropertyItem prop;

                try
                {

                    prop = img.GetPropertyItem(36867);

                }
                catch (Exception)
                {
                    prop = img.GetPropertyItem(306);
                }

                string dateTaken = r.Replace(Encoding.UTF8.GetString(prop.Value), "-", 2);
                return DateTime.Parse(dateTaken);
            }
        }


    }
}

}

like image 367
Lotok Avatar asked Feb 05 '13 10:02

Lotok


People also ask

What C is used for?

C programming language is a machine-independent programming language that is mainly used to create many types of applications and operating systems such as Windows, and other complicated programs such as the Oracle database, Git, Python interpreter, and games and is considered a programming foundation in the process of ...

What is C in C language?

What is C? C is a general-purpose programming language created by Dennis Ritchie at the Bell Laboratories in 1972. It is a very popular language, despite being old. C is strongly associated with UNIX, as it was developed to write the UNIX operating system.

What is the full name of C?

In the real sense it has no meaning or full form. It was developed by Dennis Ritchie and Ken Thompson at AT&T bell Lab. First, they used to call it as B language then later they made some improvement into it and renamed it as C and its superscript as C++ which was invented by Dr.

Is C language easy?

Compared to other languages—like Java, PHP, or C#—C is a relatively simple language to learn for anyone just starting to learn computer programming because of its limited number of keywords.


1 Answers

My understanding of using Tasks is shakey, returning a task serves what purpose?

The Task is a representation of the async operation. When the Task completes, it means the operation completed. And you can await the Task, which means you will asynchronously wait for it to complete (not blocking the UI thread).

But if you make your method async void, there is no way to wait for the operation to complete. When the method returns, you know that the async operation was started, but that's it.

What you need to do is to change Archive() to return a Task, so that you can wait for it to complete in your event handler. The Task will be returned automatically, you don't need to (or can) add any returns.

So, change the signature of Archive() to:

private async Task Archive(string file, string destination, DateTime taken)

And then await it in your event handler (which you also need to change to async):

private async void btnSort_Click(object sender, RoutedEventArgs e)
{
    // snip

    foreach (string f in result)
    {
        DateTime dest = GetDateTakenFromImage(f);
        await Archive(f, Destination, dest);
    }
}

In general, async void methods should be used only for event handlers. All other async methods should be async Task (or async Task<SomeType> if they return some value), so that you can await them.

like image 119
svick Avatar answered Sep 21 '22 12:09

svick