Ich versuche, die Änderungen in .NET 4.5, hauptsächlich die asynchronen Funktionen, zu verstehen. Um mich daran zu gewöhnen, dachte ich, ich würde eine kleine App erstellen, um meine riesige Fotosammlung zu archivieren. Ich lerne am besten damit, dass die Anwendung einen doppelten Zweck erfüllt.C# asynchrone Dateiübertragung - Warten vor dem Fortsetzen der Schleife
Ich habe viele MSDN Artikel über die Verwendung von Async gelesen, aber ich glaube nicht, dass ich ein gutes Verständnis davon habe (weil es nicht funktioniert). Ich wollte, dass jedes Foto in einem Quellordner basierend auf seinem Aufnahmedatum in einen Zielordner kopiert wird (oder erstellt wird, wenn die übernommenen Metadaten fehlen). Gleichzeitig wird es in eine Standardbenennungskonvention umbenannt und das Bild wird so angezeigt, wie es in einer Bildbox archiviert ist. Ich wollte, dass die Anwendung während der Arbeit immer wieder antwortet. An dieser Stelle kommt Async ins Spiel. Jetzt ist der App-Zweck unwichtig, der ganze Punkt brachte mich um den Async herum.
Was tatsächlich passiert, ist die App reagiert nicht mehr, archiviert alle Bilder wie vorgesehen, aber die Bildbox zeigt nur das endgültige Bild. Async startet die Dateiübertragung, geht dann zum nächsten Bild über, startet die Übertragung und geht dann weiter usw. usw., so dass ich am Ende Hunderte von offenen Dateiströmen habe, anstatt darauf zu warten, dass alle schließen.
Irgendwelche Hinweise, in denen ich falsch liege, würden geschätzt werden. Mein Verständnis von der Verwendung von Tasks ist shakey, die Rückgabe einer Aufgabe dient welchem Zweck?
imgMain ist die Bildbox in der XAML-Datei. Das async/await ist in der Archivierungsmethode, zeigt aber den gesamten Code an, der relevant sein könnte.
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);
}
}
}
}
}
Ich bin kein Experte für die neuen async/await-Funktionen von .Net 4.5, aber eine Sache, die mir auffällt, ist, dass das einzige, was Sie asynchron ausführen, die Dateikopie ist. Ich bin sicher, dass Sie mit besserer Anleitung einige nützliche Antworten bekommen werden. –
@DanielKelley Was möchten Sie sonst asynchron ausführen? – svick
@svick Nach Ihrer Antwort - alles innerhalb von Archiv. Abgesehen von "erwarten" 'fs.CopyToAsync' hat alles andere den UI-Thread gebunden. –