Praxis-Test: Bookmark-Auswirkungen auf die eigene Seite

Im Artikel „Bookmark richtig setzen“ habe ich kurz Bookmarks sowie die Nutzung von Bookmark-Submittern beschrieben. Heute möchten wir die Auswirkungen auf das Suchmaschinen Ranking einer Webseite durch das Setzen von Bookmarks prüfen.

test bookmark
Bookmarks erhöhen die Impressionen

Test 1: Setzen von Bookmarks auf die Hauptseite
Zuerst habe ich getestet, wie sich Bookmarks auf eine Hauptseite verhalten. Vorauszuschicken muss man, dass dies genau von vielen SEOs als falsch empfunden wird. Ich persönlich bin auch der Meinung, dass man dies unterlassen sollte, wollte jedoch die Auswirkung testen. Die Seite selbst ist für mich keine Hauptseite und schon einige Jahre alt. Sie dümpelt in den Suchergebnissen vor sich hin, so dass die zu erwartenden Erfahrungen sehr spannend waren. Im ersten Test habe ich also über 70 Bookmarks automatisiert auf die Startseite „geschossen“. Wie wir in der Statistik sehen können, hat die große Suchmaschine sehr positiv auf die Bookmarks reagiert. Die Impressionen haben sich mehr als verdoppelt.

Test 2: Setzen von Bookmarks auf bestehende AVZ-Einträge
Gängige Meinung der SEOs ist, dass man Bookmarks nicht für eine Hauptseite, sondern zur Unterstützung von Backlinks nutzen sollte. Also habe ich versucht, ältere AVZ-Einträge mit Bookmarks zu beglücken. An den darauffolgenden vier Tagen (nach Test 1) habe ich an je einem Tag eine AVZ-URL mit Link zu meiner Hauptseite mit Bookmarks versorgt. In der Grafik ist zu erkennen, dass sich dies positiv auf die Impressionen ausgewirkt hat. Aber eine „wahre Explosion“ ist nicht erfolgt.

Nachhaltigkeit von Test 1 und Test 2
Leider habe ich beide Tests direkt nacheinander durchgeführt, so dass die beiden Tests nur zusammen betrachtet werden können. Zu beobachten war, dass die Impressions massiv zugenommen haben. Leider war die Zunahme nur ein Strohfeuer. Nach rund 14 Tagen fielen die Impressions wieder auf den vorherigen Wert. Das Ranking hat im Testzeitraum etwas zugenommen, war jedoch nicht von Bedeutung.

schaden bookmark dem ranking
Erhöhen Bookmarks auch die Position?

Test 3: Bookmark auf einen Artikel in diesem Blog
Auffallend ist, dass der Suchbegriff „gimp effekte“ bei der großen Suchmaschine 1.000 Impressionen auf einen Artikel in meinem Blog erzeugt. Die durchschnittliche Position ist mit „180“ allerdings sehr schlecht und führt dazu, dass natürlich auch keine Klicks über diese Keyword-Kombination erzeugt werden. Also wollte ich wissen, ob man mittels Bookmark diese Position verbessern kann. Für den Test habe ich also rund 70 Bookmarks auf den betroffenen Artikel mit dem Key „gimp effekte“ gesetzt. Werden die Suchmaschinen-Zugriffe „durch die Decke gehen“?

Weit gefehlt. Rund zehn Tage später zeigt das Bild keine signifikanten Verbesserungen. Der Graf bewegt sich im üblichen Rahmen, die „durchschnittliche Position“ ist jedoch von 180 auf 170 gesunken. Auf die Nachhaltigkeit zu dokumentieren habe ich verzichtet.

Fazit: Sind Bookmark sinnvoll?
Es hat bei meinem Test keine Verschlechterungen gegeben, wenn ich Bookmarks auf das Hauptprojekt gesetzt habe. Allerdings würde ich dem SEO-Rat folgen, keine Bookmarks auf ein Money-Projekt zu setzen. In meinen Tests haben die Bookmarks keine nachhaltige Verbesserung der Ergebnisse gezeigt. Sind Bookmarks also so schlecht wie ihr Ruf?

Bookmarks sind keine Geheimwaffe und das manuelle Setzen von Bookmarks sicherlich Zeitverschwendung (vielleicht von einer kleinen Handvoll bei gut platzierten Bookmark-Dienste mal abgesehen). Trotzdem kann die Anschaffung eines Bookmark Submitters sinnvoll sein. Das erstmalige Versorgen von Satelliten-Seiten wie Freeblogs oder ähnliche, welche ich im Rahmen des noch laufenden Test „Suchmaschinen optimieren in 10 Tage“ noch beschreiben werde, ist eine der Anwendungsmöglichkeiten. Dafür sind die rund 100 Euro Anschaffungspreis und die paar Dollar für dem De-Captcha-Dienst sicherlich gut angelegt. Doch bahnbrechende Erfolgsergebnisse darf man sich von Bookmarks nicht erhoffen.

Bookmark richtig setzen

Wer sich mit Suchmaschinenoptimierung beschäftigt, stößt unweigerlich auch das Angebot, sogenannte „Bookmarks“ (= „Lesezeichen“, die gesetzt werden, bzw. auf Seiten, auf denen es möglich ist, ein solches Bookmark zu setzen. Der Grund für Bookmarkdienste liegt auf der Hand: Durch Eintragung der eigenen Seite kann man schnell und einfach einen Backlink für seine eigene Seite erzeugen. Nachdem die ersten Bookmark-Seiten etabliert waren, schossen hunderte Bookmark-Dienste wie Pilze aus dem Boden. Alle nur zu einem Zweck: Für Suchmaschinen-Optimierer (SEO) eine Backlinkquelle zu erzeugen. Denn durch Automation ist es binnen Minuten möglich, 100, 200 oder gar 1000 Bookmarks und somit Backlinks zu erzeugen.

Bookmarks als Backlink in der untersten Schicht
In SEO-Kreisen werden Bookmarks als sehr „niederwertige Backlinks“ angesehen. Man geht auch davon aus, dass bei Suchmaschinen diese Links nicht als hochwertig angesehen werden. Es gibt die Aussage von Suchmaschinen-Spezialisten, die vor Bookmarks auf ein Moneyprojekt (= Hauptseite) sogar warnen. Wie sich diese Links verhalten, wollen wir hier nun überprüfen.

Bookmarks automatisiert setzen
Man sollte, wenn man sich mit Bookmarks beschäftigt, zuerst einmal einen Nachmittag mit den gängigen Bookmark-Seiten beschäftigen. Es schadet nicht, an diesem Nachmittag Bookmarks per Hand zu setzen. Dies erhöht später den „WOW-Effekt“, wenn man einem Bookmark-Submitter bei der Arbeit zuschaut. Zudem erhält man ein Gefühl dafür, wie man die Felder im Submitter auszufüllen hat bzw. versteht besser, warum der Submitter keine Eintragungsquote von 100 Prozent ausweist.

Ich habe meine ersten Tests mit dem Public Bookmark Submitter durchgeführt. Für knapp 100 Euro ist dieser online erhältlich. Eine knappe, aber ausreichende Doku, erklärt die auszufüllenden Felder. Eine Liste mit knapp 100 Bookmark-Dienste liegt bei. Auch eine kurze Erklärung, dass man De-Captcha-Dienste nutzen kann.

Captcha-Breaker für Bookmarks nutzen
Jeder kennt Captchas, diese kleinen Bilder mit Zahlen oder Text, die dazu dienen, einen Spider von einem Menschen zu unterscheiden. Und nahezu alle Bookmark-Dienste nutzen ein Captcha, um so automatisierte Anmeldungen zu unterbinden. Doch inzwischen ist De-Captcha eine Dienstleistung, die man nicht missen möchte. Die Submitter wie auch der Public Bookmark Submitter liefert das Captcha automatisch an den Dienst weiter und erhält über diesen binnen Sekunden die korrekte Antwort. Für jedes Lösen eines Captchas bezahlt man ein paar Cent. Für meinen Test habe ich per Vorkasse 1000 Captchas für rund fünf Dollar geordert. Geld, das man gerne ausgibt.

Bookmark-Submitter nutzen
Alle Felder ausfüllen und der Submitter legt automatisiert die Konten an, wenn ein De-Captcha-Dienst hinterlegt ist. Der Submitter greift dann automatisiert auf das verwendete Email-Konto zu (POP-Account; eigenes Freemailer-Konto nutzen!), liest das vom Dienst vergebene Passwort aus. Im zweiten Schritt wird das eigentliche Bookmark angelegt. In meinen Tests wurden so rund 70 Bookmarks angelegt. Bei rund 30 Diensten schlug das Anlegen aus unterschiedlichen Gründen fehl. Was nach schlechter Quote klingt, ist letztlich doch nicht so schlimm. Vor allem, wenn man die Gründe analysiert. Neben Hosting-Probleme bei einzelnen Bookmarkdienste haben viele Bookmark-Webmaster auch bewusst „Spezialregeln“ aufgestellt, die das Automatisieren verhindern sollen. Der eine Dienst verlangt mindestens fünf Tags, der andere untersagt ein Bookmark mit fünf oder mehr Tags. Der eine Dienst möchte 500 Zeichen in der Beschreibung, der andere weigert sich, so lange Beschreibungen zu akzeptieren.

Vielleicht hat man nach der ersten Anmeldung noch die Muse, die fehlgeschlagenen Anmeldungen per Hand zu überarbeiten. Doch diese Muse ist schnell dahin und man empfindet die Quote als durchaus akzeptabel.

Auswahl der wichtigen Artikelverzeichnisse (AVZ)

Wie wir im Artikel „Suchmaschinen optimieren in 10 Tagen“ gesehen haben, gehören Links aus Artikelverzeichnisse (AVZ) trotz Pinguin, Panda und weiteren Google-Updates zum Linkmix dazu. Sicherlich wäre es falsch, ausschließlich auf Links aus AVZ zu setzen, so wie wir es bisher im Beispielprojekt getan haben, doch gegen einige Links aus Artikelverzeichnisse, Webkataloge oder ähnliche haben Suchmaschinen nach wie vor nichts auszusetzen.

Zuerst stell sich natürlich die Frage, bringen Links aus Artikelverzeichnisse überhaupt noch etwas?
Dies kann man schnell und eindeutig mit einem JA beantworten. Die große Suchmaschine hat in der Vergangenheit viele AVZ und somit die Links aus den Verzeichnissen abgewertet, doch es macht Sinn sich die Mühe zu machen, aus den verbleibenden Artikelverzeichnissen sich die „Rosinen“ herauszupicken und dort Content zu platzieren.

Daher folgen hier die ultimativen 10 Artikelverzeichniss-Tipps:

  1. Die gesamte eingehende Linkzahl zu Grunde gelegt, sollten AVZ zehn, maximal 20 Prozent ausmachen.
  2. Es macht Sinn, neue AVZs zu Testen, da hier der Webmaster eher gewillt ist, neue Artikel auch freizugeben
  3. Es macht aber noch mehr Sinn, auf „alte“ AVZs zu setzen (sofern diese noch Artikel aufnehmen), da hier per Abfrage geprüft werden kann, wie gut das Artikelverzeichnis in Suchmaschinen vertreten ist. Denn ein Link aus einem abgestraften AVZ ist meines Erachtens nicht schlimm, aber nicht den Aufwand wert.
  4. Bei der Auswahl eines AVZ immer zuerst nachschauen, welches Datum der neuste Artikel trägt. Ist dieser älter als vier Wochen, liegt der Verdacht nahe, dass der Webmaster sein Projekt inzwischen ignoriert.
  5. Einen eingereichten Artikel immer samt Einrechungsdatum dokumentieren. Wenn der Artikel über einige Wochen nicht veröffentlicht wurde, das AVZ gedanklich abhaken und den eingereichten Artikel anderweitig veröffentlichen.
  6. Genau protokollieren, wann ein Artikel eingereicht wurde, wie der Ablauf war und wann die Veröffentlichung erfolgte. Diese Liste als Basis für weitere Projekte nehmen.
  7. Peinlichst genau an die Mindestanzahl der Wörter halten, die der Webmaster vorgibt. Eine subjektive Erfahrung zeigt, dass längere Artikel eher veröffentlicht werden, als knapp gehaltene Artikel.
  8. Nicht auf nennenswerten Besucherstrom über die AVZ-Links hoffen. Meine Auswertung der Logfiles hat ergeben, dass kaum ein Besucher über ein Artikelverzeichnis auf die beworbene Seite kommt. Somit ist dies auch – zumindest nach meiner Einschätzung – leider kein Auswahlkriterium eines Verzeichnisses.
  9. Das Spinning eines Artikels ist möglich. Wer jedoch ein bisschen Geld in die Hand nehmen kann, lässt sich einige einfache Artikel schreiben. Mit wenig Aufwand ist sofort aus einem Artikel eine zweite Version erzeugt, so dass mit fünf bezahlten Artikeln schnell zehn Artikel für Artikelverzeichnisse vorliegen.
  10. „Kein Artikel ist so schlecht, dass er nicht für ein Artikelverzeichnis taugt“. Diesen Satz habe ich vor geraumer Zeit in einem Forum gelesen. Dem (unbekannten) Autor muss ich recht geben: Man muss keine „Meisterwerke“ bei einem AVZ einreichen.

Suchmaschinen optimieren in 10 Tagen – Nachschau nach sechs Monaten

<< Suchmaschinen optimieren in 10 Tagen. Anfang verpasst?

Tag 5: Nachschau nach sechs Monaten
Inzwischen ist es über sechs Monate her, dass wir uns mit der Suchmaschinenoptimierung einer Webeseite beschäftigt haben. Und in der Tat haben wir in den zurückliegenden Monaten keine Tätigkeit unternommen, um die Test-Webseite in irgend einer Art zu pushen. Somit ist es sehr spannend zu analysieren, wie sich die Seite aus Suchmaschinen-Sicht entwickelt hat.

Zunächst einmal ein kurzer Rückblick:
Wir haben Anfang des Jahres eine Webseite ohne nennenswerte Backlinks erstellt und eine OnPage-Optimierung vorgenommen. Dann haben wir mittels Artikelverzeichnisse (AVZ) über 100 Backlinks (direkt, keine Kreuzverlinkung) erzeugt. Wir haben hier lediglich zwei Grund-Artikel zur Hand genommen, daraus jeweils 50 Artikel gespinnt und fleißig an Artikel-Verzeichnisse verteilt. Es hat sich gezeigt, dass dies möglich ist, auch wenn ich nicht dazu rate, dies so zu wiederholen (siehe Artikel Suchmaschinen optimieren in 10 Tagen: Tag 5).
Schon während des Verteilens der Artikel hat sich über die Google-Webmaster-Tools gezeigt, dass die große Suchmaschine sofort damit begonnen hat, die Seite in den Index aufzunehmen und auch in ihren Suchergebnissen auszuliefern. Heute, sechs Monate später, können wir die Ergebnisse nun überprüfen:

Ranking in SuchmaschineHat das Ranking unter dem Google-Pinguin-Update gelitten? Ist das Ranking nachhaltig?
Wie wir sehen können, hat das Ranking nicht gelitten. Im Gegenteil. Das Projekt hat heute mehr Suchmaschineneinblendungen als zu Projektstart. Allerdings ist der Traffic nach wie vor spärlich. Schauen wir uns einmal das Ranking Juni bis Juli dieses Jahres an. Wir sehen einen relativ konstanten Verlauf der Einblendungen einer großen bekannten Suchmaschine.

Auch das Pinguin-Update hat die Seite gut überlebt. Allerdings kann dies auch an der nach wie vor spärlichen  Sichtbarkeit in der Suchmaschine liegen. So liegt der Sichtbarkeitsindex von Sistrix ermittelt mit 0,02 in einem sehr geringen Bereich, doch zeigt dieser, dass die Seite immerhin in Suchmaschinen vertreten ist.

Sollte man nach dem Pinguin-Update noch AVZs nutzen?
Wenn man „bessere“ Linkquellen hat, kann man auf Artikelverzeichnisse verzichten. Doch die Mehrzahl der Webmaster hat eben kaum „bessere“ Linkquellen und so gehören AVZ-Links einfach dazu (Strichwort: Linkmix)

Wieviel AVZ-Links sollte man setzen?
Im Beispielprojekt sind aktuell 125 Links aus Artikelverzeichnisse über Google Webmaster Tools gelistet. Für ein reales Projekt würde ich auf keinen Fall ausschließlich auf AVZ-Links setzen. Ob zehn Prozent AVZ-Links gut sind, oder nicht, wird jeder SEO unterschiedlich beantworten. Es dürfte erstmal eine gute Basis sein.

Welche Artikelverzeichnisse sind gut?
Eine Frage, die man nicht beantworten kann. Zuerst einmal habe ich nur AVZ verwendet, die kostenlos Artikel veröffentlichen. Auch sollte die Freischaltung binnen Tagen erfolgen. Für weitere Projekte kann man aus den Erfahrungen schließen, die man bisher gemacht hat. Wenn ein AVZ Artikel freischaltet, nach längerer Zeit noch am Markt ist und auch über Sichtbarkeit verfügt, kann ein Artikel und somit ein Backlink nicht schaden und ist der Mühe wert, mit einem Artikel bedacht zu werden.

Sollen Artikel gespinnt werden?
Dies ist eine Frage des Geldbeutels und der Überzeugung. Prinzipiell ist das Spinning nichts Schädliches. Keine Frage: 50 Artikel aus einem Grundartikel sind zuviel. Dies zeigt auch die spätere Überprüfung per Copyscape. Die Artikel sind zum Teil zu ähnlich. Besser dürfte es sein, maximal zehn Artikel pro Ausgangsartikel zu erstellen.
Wenn man allerdings davon ausgeht, dass man vielleicht nur zehn oder zwanzig Artikel benötigt, könnte man auch per Textbroker Artikel für wenig Geld in Auftrag geben. Dann sind die Artikel auf jeden Fall „einzigartig“, da jeder Autor das Thema „individuell“ angeht. Und selbst ohne Spinning kann man aus einem neuen Artikel mit einigen textlichen Änderungen einen weiteren Artikel erzeugen.

C# Task Planer: Scheduled Tasks erstellen

Heute möchten wir durch ein C#-Programm automatisch den Task-Planer (Schedueld Task) nutzen, um unsere zuvor erstellten Twitter-Nachrichten zeitversetzt an unser Twitter-Account zu senden. Wir könnten selbstverständliche ein eigenes Programm mit einem Timer schreiben, der zum vordefinierten Zeitpunkt die Twitter-Nachricht sendet. Doch diese wäre eine weitere Anwendung, die ständig auf dem Rechner laufen müsste. Selbst professionelle Anwendungen wie beispielsweise Pinnacles WIN PVR gehen bei der Aufnahme des TV-Programmes inzwischen dazu über, den Windows-eigenen Scheduler zu nutzen. Dies spart Ressourcen und vereinfacht die möglichen Fehlerquellen. Auf einem Linux-System käme schließlich auch niemand auf die Idee, die Crontab nachzubauen …

Unser heutiges Ziel ist, vorhandene WordPress-Artikel über die folgenden Tage verteilt als Tweet zu senden. Hierzu haben wir bereits die WordPress-Tabelle „wp_posts“ von Revisionen befreit und eine Tabelle angelegt, die lediglich die Felder „post_title“ und die „guid“ enthält. Die „Guid“ ist der Link auf die ID in WordPress. Dies ist nicht der SEO-optimierte Link. Da wir sowie einen URL-Verkürzer nutzen, benötigen wir für die Optik keinen lesbaren Link. Suchmaschinen wie Google lieben lesbare Links. Diese werde ich ein wenig enttäuschen. Ich spare mir Zeit und Aufwand. Zumal WordPress von alleine diesen Link per 301 Redirect auf den SEO-optimierten Link auflöst.

Wir erstellen eine weitere SQL-Tabelle, die lediglich eine ID, ein Statusfeld (Tinyint, Default 0) und ein Textfeld enthält. Diese nenne ich „tw_sirmark“. In dieser Tabelle speichere ich die Tweets. Eine eindeutige ID erleichtert das Auffinden der Tweet-Nachricht. Das Statusfeld gibt Auskunft darüber, ob der Tweet „geplant“ oder bereits veröffentlicht wurde.

Scheduled Task: Erstellen und Löschen von Tasks im Task-Planer
Nun schreiten wir zum eigentlichen Thema von Heute: Die Scheduled Task. Wir möchten mittels C#-Konsole für jeden geplanten Tweet einen Scheduled Task erstellen. Wenn dieser abgearbeitet ist, soll der Eintrag gelöscht werden. In unserem Beispiel ist der Weg sicherlich nicht optimal, denn aktuell sollen über 200 Einträge „getweetet“ werden. Doch in einem folgenden Projekt sollen lediglich vier bis fünf Einträge pro Woche generiert werden. So dient dieses Projekt als Vorlage. Ferner werden wir gleich sehen, wie sich der Windows-Scheduler mit einer solchen Menge an Einträgen „schlägt“.

Um den Windows-Task-Scheduler anzusprechen, gibt es viele Möglichkeiten. Mir persönlich sagt die DLL „Microsoft.Win32.TaskScheduler.dll“ am meisten zu. Diese ist über Codeplex kostenfrei erhältlich.

Über den folgenden Beispielcode erstellen wir unseren ersten Test-Task. Dieser ruft einfach testweise das Notepad auf.

static void Main(string[] args)
{
    // Task Service auf der lokalen Maschine
    using (TaskService ts = new TaskService())
    {
        // Task erstellen
        TaskDefinition td = ts.NewTask();

        //Beschreibung hinzufügen
        td.RegistrationInfo.Description = "Meine Task-Beschreibung";

        // Task Trigger: Starte täglich um 15 Uhr  
        td.Triggers.Add(new DailyTrigger {StartBoundary = DateTime.Today + TimeSpan.FromHours(15)});

        // Zum Test das Notepad öffnen
        td.Actions.Add(new ExecAction("notepad.exe", "", null));

        // Register den Task im Root-Ordner
        ts.RootFolder.RegisterTaskDefinition(@"Mein-Test-Trigger 15 Uhr", td);
    }
}

Weitere  Trigger-Einsatzzeiten:

//Task Trigger: Einmaliger Task, 1 Tag nach Erstellung, um 3 Uhr
// Für weitere Verfeinerung: new TimeSpan(hour, min, sec)
td.Triggers.Add(new TimeTrigger { StartBoundary = DateTime.Today + TimeSpan.FromDays(1) + TimeSpan.FromHours(3) });

//Task Trigger: Einmaliger Task am 12.07.2012 um 13:35:36 Uhr
td.Triggers.Add(new TimeTrigger { StartBoundary = new DateTime(2012, 7, 12, 13, 35, 36) });

Für unser Projekt nutzen wir den TimeTrigger. Über diesen können wir einen exakten Zeitpunkt bestimmen. Nun benötigen wir noch eine Möglichkeit, den Task-Trigger wieder zu löschen.

//Zugriff auf einen bestehenden Task
string taskName = "Mein-Test3";

using (TaskService ts = new TaskService())
{
Task t = ts.GetTask(taskName);
       ts.RootFolder.DeleteTask(taskName);
}

Im obigen Beispiel benötigen wir den Namen des Task, den wir löschen möchten. Natürlich können wir dem per Task aufzurufenden Programm auch den Namen übergeben, damit dieser den abgelaufenen Task löschen kann. Besser ist es jedoch, nach abgelaufenen Tasks zu suchen und diese zu löschen. Vorsicht: Eventuell werden somit mehr Tasks gelöscht, als man vielleicht möchte. Da ich den Anfangsbestandteil des Tasknamen kenne („MeinTask1“,“MeinTask2“ -> „MeinTask%“), werde ich nicht nur nach abgelaufenen Tasks, sondern auch nach dem Namenbestandteil suchen, bevor ich den Task lösche.

Hinweis: Es kann einem Task selbstverständlich auch in den Setting mitgegeben werden, dass der Task sich nach Ausführung selbst löscht.

// Task löschen, wenn er nicht erneut geplant wird
td.Settings.DeleteExpiredTaskAfter = TimeSpan.FromMinutes(2);

// Task beenden nach ....
td.Settings.ExecutionTimeLimit = TimeSpan.FromMinutes(2);

Löschen von bestehenden Scheduled Task mit C#
Da wir den Task vom aufzurufenden Konsolenprogramm auch selbst löschen wollen, benötigen wir eine Möglichkeit, auf diesen zuzugreifen. Sonst würden alle abgelaufenen Tasks im Scheduler bestehen bleiben.

//Zugriff auf bestehende Scheduled-Tasks, Auslesen des Root-Ordners
TaskService taskService = new TaskService();
TaskFolder tasksFolder = taskService.GetFolder(@"");
TaskCollection listOfTasks = tasksFolder.Tasks;
foreach (Task theTask in listOfTasks)
{
//Zugriff auf den Namen aller bestehenden Tasks im Root-Order
       string s = theTask.Name;

       //Nächste Laufzeit: Abgelaufene Tasks zeigen: 01.01.0001 00:00:00
       string sNext = theTask.NextRunTime.ToString();
}

Ändern von bestehenden Scheduled Tasks

//Ändern von bestehenden Tasks

//Zugriff auf bestehende Scheduled-Tasks, Auslesen des Root-Ordners
TaskService taskService = new TaskService();
TaskFolder tasksFolder = taskService.GetFolder(@"");
TaskCollection listOfTasks = tasksFolder.Tasks;
foreach (Task theTask in listOfTasks)
{
    // Zugriff auf den Namen aller bestehenden Tasks im Root-Ordner
	// Nur bestimmte Tasks auslesen
    if (theTask.Name.StartsWith("Mein-"))
    {
        //Auslesen der Eigenschaften (Demo)
        string sName = theTask.Name;
        string sBeschreibung = theTask.Definition.RegistrationInfo.Description;
        string sAusfuehrung = theTask.Definition.Triggers.ToString();
        string sAction = theTask.Definition.Actions.ToString();

		using (TaskService ts = new TaskService())
        {
            // Neuen Task erstellen
            TaskDefinition td = ts.NewTask();

            // Setzen der Werte, die sich ändern sollen
            td.RegistrationInfo.Description = "Meine neue Task-Beschreibung";

            // Task löschen, wenn er nicht erneut geplant wird
            td.Settings.DeleteExpiredTaskAfter = TimeSpan.FromMinutes(2);

            // Task beenden nach ....
            td.Settings.ExecutionTimeLimit = TimeSpan.FromMinutes(2);

            td.Actions.Add(new ExecAction(@sAction, "", null));

            //RegisterTaskDefinition ändert den Task, wenn dieser bereits vorhanden ist
            ts.RootFolder.RegisterTaskDefinition(@sName, td);
        }
    }
}

[tweet http://twitter.com/sirmark_de/status/220481183354601472]

Twitter Tweets mittels C# erstellen

Im Artikel „WordPress to Twitter Plugin installieren“ haben wir eine Möglichkeit gesehen, wie wir über ein WordPress-Plugin neue Artikel automatisch als Tweet in Twitter veröffentlichen können. Wer kein WordPress nutzt oder auch ältere Artikel in den kommenden Tagen „tweeten“ möchte, der muss sich nach einer anderen Lösung umsehen.

Im folgenden Artikel möchten wir mittels C# in einer Konsolenanwendung Tweets aus einer Datenbank lesen und an Twitter senden. Selbstverständlich sind auf diesem Wege noch viele weitere Verwendungsmöglichkeiten denkbar.
Wir nutzen für die Kommunikation die DLL „tweetsharp“, die auf Github  kostenfrei erhältlich ist. Unzippen Sie die TweetSharp-Binaries und binden Sie referenzieren Sie „Hammock“, „Newtonsoft.Json“, „TweetSharp“ und „TweetSharp.Twitter“.

using System;
using System.Configuration;
using System.Diagnostics;
using TweetSharp;
using TweetSharp.Model;

namespace TweetingTest
{
    class Program
    {
        static void Main(string[] args)
        {

            TwitterClientInfo twitterClientInfo = new TwitterClientInfo();
            twitterClientInfo.ConsumerKey = "Mein-ConsumerKey"; 
            twitterClientInfo.ConsumerSecret = "Mein-ConsumerSecret-Key"; 

            TwitterService twitterService = new TwitterService(twitterClientInfo);
            twitterService.AuthenticateWith(AccessToken, AccessTokenSecret);

            string tweetMessage = "Dies ist die Tweet-Nachricht";
            TwitterStatus twitterStatus = twitterService.SendTweet(tweetMessage);
        }
}

Wie wir aus dem C#-Beispielcode sehen können, ist die Kommunikation über die eingebundenen DLLs sehr einfach. In unserem Beispiel verzichte ich auf die sichere Verwendung der API-Keys. Diese stecken hardcodiert im Quellcode. Wenn wir eine Anwendung schreiben, die in die Masse geht, ist diese Methode natürlich ungeeignet.
Zum Code selbst gibt es nicht viel zu sagen. Wir erstellen ein TwitterClient-Objekt, versorgen es mit den benötigten API-Keys, sorgen für eine Twitter-Authentifizierung und senden mittels „twitterService.SendTweet(MESSAGE)“ unsere Twitter-Nachricht. An dieser Stelle sei darauf hingewiesen, dass die DLL hier nahezu alle gewünschten Möglichkeiten der Twitter-Kommunikation bereitstellt.

Nachricht aus Datenbank lesen und an Twitter senden
Unser Ziel war es, alle bereits vorhandenen Artikel aus unserem Blog nach und nach an Twitter zu senden. Mit jedem automatisierten Aufruf (Scheduled Task) (LINK#) einer Konsolenanwendung soll ein Artikel „getweetet“ werden. Also erstellen wir uns eine kleine Datenbanktabelle. Diese besteht lediglich aus zwei Feldern: „Text“ und „URL“. Ich persönlich bin ein Fan von einem eindeutigen ID-Feld für eine Zeile; aus diesem Grund erweitere ich die Tabelle um eine ID (Primary Key). Im Feld „Text“ füllen wir die Überschrift des Artikel, im Feld URL benötigen wir die komplette URL des Artikels.

Unsere Konsole soll starten, gemäß SQL-Select den ersten Datensatz laden und diesen Twittern. Vorher muss jedoch noch eine Short-URL angelegt werden. Im Code wird per API auf Bit.ly zugegriffen und eine Short-URL erzeugt. Diese wird dann dem Text angehängt. Hier gilt zu beachten, dass die Nachricht nicht größer als 140 Zeichen wird. Aus diesem Grund müssen längere Nachrichten gekürzt werden. Die Routine zum Kürzen ist nicht optimal. Wer sich Mühe geben will, sollte dafür Sorge tragen, dass die Nachricht nur an einem Wortende abgeschnitten wird.
Ist die Nachricht nun mit einer Short-URL versehen, wird diese per API getwittert. Wir benötigen hier wieder die API-Keys von Twitter sowie von Bit.ly. Wie man diese Schlüssel generiert, wurde im Artikel „WordPress to Twitter Plugin installieren“ beschrieben.
Nun wird noch die verschickte Nachricht aus der Tabelle gelöscht.

[tweet http://twitter.com/sirmark_de/status/220479892029054977]

WordPress to Twitter Plugin installieren

WordPress to Twitter Plugin
WordPress to Twitter Plugin

Wer einen Blog betreibt und bei Twitter aktiv ist, kommt schnell auf den Gedanken, jeden neuen Artikel auch automatisiert bei Twitter zu veröffentlichen. Der Mikro-Blog-Dienst Twitter bietet dank umfangreicher API die Möglichkeit, einen solchen Datenaustausch zu erledigen. Wie wir per PHP und C# auf die API von Twitter zugreifen und Tweets und Nachrichten senden, darauf gehe ich in weiteren Artikeln ein. In diesem Artikel nutzen wir ein fertiges WordPress-Plugin für diese Aufgabe: „WP to Twitter“.

Vorbereitung: API-Keys für Twitter und den Bit.ly generieren
Um auf Twitter und den URL-Shortener Bit.ly zugreifen zu können, benötigen wir entsprechende API-Key. Auch das WP-Plugin „WP to Twitter“ benötigt diese Keys, da das Plugin die Schnittstellen bedient. Wir müssen uns also entsprechend bei diesen Diensten anmelden und die Keys generieren.

API-Keys für Twitter generieren

  1. Registrierung der Application bei Twitter („Twitter´s application registration page“); ausfüllen der Pflichtfelder.
  2. „Application-Name“: Vergeben Sie einen beliebigen Namen. Das Wort „Twitter“, auch als Bestandteil, ist nicht erlaubt
  3. „Description“: Vergeben Sie eine beliebige Beschreibung.
  4. Callback-URL: Geben Sie hier die URL Ihres WordPress-Blogs ein (beispielsweise: http://sirmark.de)
  5. Bestätigen Sie die „Developer Rules“, senden Sie das Formular ab.
  6. Wechseln Sie auf den Reiter „Settings“
  7. Hier ist es wichtig im „Application Type“ das Recht „Read and Write“ zu vergeben.
  8. Ein Update nicht vergessen!
  9. Nun erhalten Sie den „Twitter Consumer Key“, den „Twitter Consumer Secret“ Schlüssel, einen „Access Token“ sowie einen „Access Token Secret“-Schlüssel.
  10. Mit diesen Schlüsseln haben Sie Zugriff auf Ihr Twitter-Konto. Behandeln Sie also die Daten mit gewisser Sorgfalt.

API-Key für Bit.ly generieren
Da Twitter nur 140 Zeichen pro Nachricht erlaubt und ein Link bereits ein Großteil dieser Zeichen beansprucht, sollte man einen URL-Shortener einsetzen. Das Plugin kommt mit diversen Shortener zurecht. Ich nutze und beschreibe hier „Bit.ly.“

  1. Erstellen Sie ein Konto bei bit.ly
  2. Es ist nur ein Mindestmaß an Angaben notwendig.
  3. Wechseln Sie auf die Seite http://bitly.com/a/your_api_key/

WordPress-Plugin „WordPress to Twitter“ installieren

Wordpress to Twitter Plugin installieren
WordPress to Twitter Plugin installieren

Nun kann es endlich losgehen. Suchen Sie in WordPress unter Plugins das Plugin „WP to Twitter“. Zum Zeitpunkt des Artikels ist die Version „2.4.5“ aktuell. Die Installation erfolgt wie bei jedem anderen Plugin in WordPress.

„WP to Twitter“-Plugin konfigurieren

WP to Twitter Einstellungen
WP to Twitter Einstellungen

Nun erfolgt die Konfiguration des Twitter-Plugins. Das Plugin bringt eine eigene und umfangreiche Konfigurations-Seite mit. Im oberen Bereich tragen wir die zuvor bei Twitter erstellten API-Keys („Twitter Consumer Key“, „Twitter Consumer Secret“, „Access Token“ und „Access Token Secret“) ein.

WP to Twitter bit.ly
WP to Twitter bit.ly

Weiter unten auf der Konfigurationsseite finden wir die vom Plugin unterstützen URL-Shortener. Wenn wir Bit.ly nutzen, tragen wir im entsprechenden Block unseren zuvor stellten Bit.ly-API-Schlüssel ein.

Basic-Settings

WP to Twitter Übersicht
WP to Twitter Übersicht

Die „Basic-Settings“ können zuerst einmal nahezu unverändert übernommen werden. Ich habe lediglich einige „Eindeutschungen“ (siehe „Screenshot“) vorgenommen. Für den ersten Test genügen jedoch die Standardeinstellungen.

Was passiert nun?
Wie wir in den „Basic-Settings“ festlegen können, reagiert das Plugin auf neue Artikel, Artikel-Updates und auch neue Kommentare mit einem automatisierten Tweet. Eine Möglichkeit, bestehende Artikel sukzessive per Tweet auf Twitter zu veröffentlichen, habe ich „auf die Schnelle“ nicht gefunden. Für wenige Artikel kann dies über ein Update erledigt werden. Wenn jedoch viele Artikel über Tage verteilt „getweetet“ werden sollen, muss eine andere Lösung her. Diese werden wir im folgenden Artikel in C# programmieren.

MyISAM-Datenbank-Tabelle reparieren

Es darf eigentlich nicht vorkommen, doch es passiert: Ein unbedachter Wackler an einem Stromkabel beim Ein- oder Umbau im Serverschrank oder gar eine Stromschwankung oder Stromausfall: Der Server wird ohne ordentliche Abmeldung einfach stromlos und das Chaos ist perfekt. Sofort werden der Techniker und der zuständige Admin kreideweiß im Gesicht. Wird der Server wieder anlaufen? Wird die Festplatte einen Schaden haben? Und wird die MySQL-Datenbank korrupt sein?
Gehen wir einmal davon aus, dass der Server wieder anläuft, das Betriebsystem zwar das nicht ordnungsgemäße Ausschalten in den Statusmeldungen moniert, aber ansonsten klaglos seinen Dienst verrichtet, wird spätestens MySQL zur Bewährungsprobe. Denn wenn im Moment des Stromausfalles schreibend auf die Datenbank zugegriffen wurde, kann man davon ausgehen, dass die Datenbank nicht ohne Schaden genommen wieder anlaufen wird.

Warning: Table ist maked as crashed and last repair failed
Wenn MySQL ohne Fehlermeldung anläuft bedeutet dies leider nicht, dass auch alle Tabellen ohne Schaden den Stromausfall überstanden haben. Gerade MyISAM-Tabellen werden, wenn während des Stromausfalles darauf zugegriffen wurde, Schaden genommen haben.
Man muss nicht gleich in Panik verfallen, es gilt Ruhe zu bewahren und den Schaden zu minimieren. Die Reparatur wird jedoch, je nach Tabellengröße, längere Zeit in Anspruch nehmen. Es ist daher zuerst zu prüfen, ob es nicht vielleicht sinnvoll ist, ein Backup zurückzuspielen. Je nach Tabelle und Sicherungs-Infrastruktur kann dies der schnellste Weg der Wiederherstellung sein.

MyISAM-Tabelle überprüfen
MySQL: myisammchk Tabelle pruefenLoggen Sie sich auf der Konsole auf dem Server ein. Navigieren Sie zum MySQL-Server („var/lib/mysql“) und wechseln Sie in das Verzeichnis der Datenbank. Prüfen Sie nun die Tabellen mit

myisamchk tabellenname.MYI

Mit Spannung kann das Ende der Prüfung abgewartet werden. Wenn die Konsole „MyISAM-Table ‚TABLENAME’ is corruped“ meldet, ist einen Reparatur der MyISAM-Tabelle angesagt.

Die schnelle MyISAM-Reparatur
Die Konsolenausgabe schlägt vor, mit dem Schalter „-r“ die Reparatur durchzuführen. Mittels dieses „Wiederherstellungsmodus“ werden falsche und gelöschte Datensätze entfernt und die Indexdatei neu erstellt. Wer es schneller möchte, kann den Schalter „-q“ zusätzlich verwenden. Dann wird versucht, die Indexdatei zu reparieren, anstatt sie neu zu erstellen. Probieren Sie die Reparatur aus.
In jedem Fall wird die Reparatur Zeit in Anspruch nehmen. Da die Datenbank sowieso offline ist, können Sie auch über den Speicher des Datenbankservers verfügen. Man beschleunigt die Reparatur ungemein, wenn man zuvor die MySQL-Variablen „sort_buffer_size“ und „key_buffer_size“ jeweils auf 25 Prozent des verfügbaren Hauptspeichers setzt.

MyISAM-table ‚tablename’ is not fixed because of errors
MySQL: myisamchk safe recoverWenn die schnelle MyISAM-Reparatur fehlschlägt, müssen Sie den Modus „safe-recover“ („-0“) wählen:

myisamchk –r –o TABELLENNAME

Je nach Tabellengröße wird diese Wiederherstellung mehrere Stunden benötigen. Sie ist genauer, aber auch sehr langsam.
Bei allen bisher bei mir aufgetretenen MyISAM-Reparatur-Szenarien haben diese Schritte zum Erfolg geführt. Wenn trotzdem Fehler auftreten oder mysiamchk sogar abstürzt, müssen Sie folgende Schritte ausführen:

1.    Kopieren Sie die Datenbank per Filesystem in einen sicheren Bereich
2.    Führen Sie die folgenden Befehle aus:

shell> mysql DATENBANKNAME
mysql> SET AUTOCOMMIT=1;
mysql> TRUNCATE TABLE TABELLENNAME;
mysql> quit

3. Kopieren Sie die Datenbank-Datei aus dem sicheren Bereich wieder auf den Server.
4. Starten Sie „myisamchk -r -q“, also die schnelle Datenbankreparatur, erneut.

C#: Konvertierung von Zahlen in Dezimal

Es klingt banal, dennoch bereitet es oftmals Probleme: Die Konvertierung von Zahlen oder Strings nach Dezimal. Wobei die eigentliche Umrechnung nach Dezimal weniger spannend ist als das Abfangen von falschen Werten. Denn eine Exception auszulösen bei der Prüfung von Werten ist nicht immer wünschenswert. Gerade bei Jobs, die massenweise Daten importieren, kann eine solche Prüfung Zeit kosten. Und zwar massiv Zeit, die sich gewaltig in der Laufzeit des Jobs auswirkt.
Spätestens dann ist der Programmierer gezwungen, sich Gedanken über die beste Konvertierung zu machen. Untersuchen wir doch gemeinsam einmal die unterschiedlichen Methoden, um eine Zahl nach Dezimal zu Konvertieren und falsche Werte zu filtern.

Betrachten wir doch folgenden C#-Code:

string sWert = "5235.65";
decimal dErgebnis = Convert.ToDecimal(sWert);
Console.WriteLine(dErgebnis);

sWert = "falscher string";
dErgebnis = Convert.ToDecimal(sWert);
Console.WriteLine(dErgebnis);

Wir konvertieren den String „5235.65“ per Convert.ToDecimal nach Dezimal. Dies wird funktionieren. Nun nehmen wir beispielsweise eine Benutzereingabe entgegen, die einen Nicht-Dezimal-Wert enthält. C# wird bei „Convert“ eine Exception auslösen. Der Klassiker ist an dieser Stelle, die Konvertierung mit einen try-catch-Block zu umschließen. Wenn nur eine Eingabe so abgefangen werden soll, ist dies sicherlich akzeptabel. Elegant und schnell ist diese Variation jedoch nicht.

Auch die Möglichkeit, die Dezimal-Konvertierung mittels „System.Decimal.Parse“ durchzuführen, wird zum gleichen Ergebnis führen:

string sWert = "5235.65";
decimal dErgebnis = System.Decimal.Parse(sWert);
Console.WriteLine(dErgebnis);

sWert = "falscher string";
dErgebnis = System.Decimal.Parse(sWert);
Console.WriteLine(dErgebnis);

Eleganter ist es auf jeden Fall, mögliche Falscheingaben per „System.Decimal.TryParse“ abzufangen:

string sWert = "5235.65";
decimal dErgebnis = 0.0M;

if (System.Decimal.TryParse(sWert, out dErgebnis)) Console.WriteLine(dErgebnis);
Console.WriteLine(dErgebnis);

sWert = "falscher string";
if (System.Decimal.TryParse(sWert, out dErgebnis)) Console.WriteLine(dErgebnis);
Console.WriteLine(dErgebnis);

Die eben beschriebene Methode eignet sich in der Regel immer, um elegant und schnell Konvertierungen von Werten nach Dezimal vorzunehmen. Allerdings ist es immer noch einem Menge Text, die man angeben muss. Und da Programmieren von Natur aus faul sind, ist auch hier eine Verbesserung denkbar. Um es vorweg zu nehmen: Die folgende Methode erzeugt noch mehr Code. Die eigentliche Umwandlung ist jedoch elegant, schnell und ermöglicht auch einen Default-Wert mitzugeben, wenn die Umwandlung schiefgehen sollte. Wenn also in einem Projekt mehrfach eine Umwandlung nach Dezimal vorgenommen werden muss, sollte man über die folgende Methode nachdenken:

namespace ConsoleApplication1
{
    public static class StringExtensions
    {
        public static Decimal? ToDecimal(this string obj, Decimal? dBaseValue=null)
        {
            Decimal dValue;
            if (Decimal.TryParse(obj, out dValue)) return dValue;
            return dBaseValue;
        }
    }

    class Program
    {
        static void Main(string[] args)
        {

            string sWert = "5235.65";
            decimal dErgebnis = sWert.ToDecimal(0).Value;
            Console.WriteLine(dErgebnis);                     

            sWert = "falscher string";
            dErgebnis = sWert.ToDecimal(0).Value;
            Console.WriteLine(dErgebnis);
        }
     }
}

Amazon S3: Zugriff auf S3 per C#

Im Artikel „Amazon S3: Wie wir einen Image-Server realisiert haben“ habe ich bereits über die Vorteile der Amazon S3 berichtet. Nun folgt der Zugriff auf die S3 mittels C#.

Daten zu Amazon S3 hochladen
Amazon selbst stellt eine einfache Oberfläche zur Verfügung. Mit dieser kann man Daten verwalten, hochladen, löschen und auch Rechte und Metadaten verwalten. Da die Daten jedoch maschinell zu S3 hochgeladen werden sollten, musste die Dokumentation studiert werden. Leider bietet Amazon keinen FTP-Zugriff aus die S3. Von Sicherheitsbedenken mal abgesehen wäre dies für uns der einfachste Weg gewesen.

Erste Schritte mit S3
amazon-s3-management-consoleZuerst sollte man sich mit S3 über die Oberfläche vertraut machen. Man sieht sehr schnell, dass Amazon bewusste die S3 nur mit einem Minimum an Funktionen ausgestattet hat. Man sollte sich mit „Buckets“ (Eimer) auseinandersetzen. In diesem werden die Daten in der S3 gespeichert. Ein besonderes Augenmerk sollte auf die Rechtevergabe fallen, denn alle Daten, die von einem User hochgeladen werden, sind per Default auch nur für diesen zum Download zugänglich. Für einen Image-Server natürlich nicht sinnvoll.
Zudem sollten die Meta-Daten betrachtet werden. Bilder werden bereits mit dem richtigen Header ausgestattet. Um ein Expire-Date zu setzen, muss unter den Meta-Daten dies angegeben werden. Eine allgemeingültige Angabe wie im Apache-Header ist leider nicht möglich. Im Beispielcode unten wird beim Hochladen neben allgemeinen Lese-Rechten auch ein Expires-Date jedem Image vergeben.

Amazon .Net-DLL für .NET
Um die Amazon S3 mittels C# ansprechen zu können, benötigen wir die Amazon DLL „AWSSDK.dll“, die bereits stolze 2.995 KB groß ist. Sie beinhaltet jedoch alles, was man zur Kommunikation mit Amazon benötigt. Also auch weitere Schnittstellen, die über unsere C#-Kommunikation mit S3 hinaus geht. Eingebunden in ein .NET-Projekt, binden wir die DLL per „using Amazon.S3;“ ein. Je nach Anwendung erfolgen noch weitere Using-Anweisungen.

Die S3 ersetzt einen FTP-Server
Untenstehend folgt ein C#-Beispiel samt C#-Code, wie man einen S3-Bucket alternativ eines FTP-Servers nutzen kann. Wir prüfen hier, ob ein Ordner in einem Bucket exisitert und legen gegebenenfalls den Ordner in der Amazon S3 an.
Dann erfolgt der Upload einer Datei. Wir begnügen uns hier, Datei für Datei in die Amazon-Wolke hochzuladen. Die Amazon-DLL bietet auch die Möglichkeit, ganze Ordner in der S3 abzulegen. Hierzu einfach einmal die Amazon-Doku studieren.

S3: Dateien öffentlich lesbar machen
Dateien, die von einem Benutzer hochgeladen werden, sind per default auch nur von diesem lesbar. In unserem Falle möchten wir jedoch, dass alle Besucher die Dateien lesen können. Die Rechte können per Hand über die S3-Oberfläche gesetzt werden. Wenn wir jedoch per C# die Dateien hochladen, können wir den entsprechenden Header-Eintrag gleich setzen:
req.AddHeader(„x-amz-acl“, „public-read“);

S3: Dateien mit einem Expire Date versehen
Um Bandbreite zu sparen und Zugriffe zu minimieren, ist es sinnvoll, Dateien mit einem Expire Date zu versehen. Der lokale Browser wird dadurch angewiesen, bei einem zweiten Aufruf die Datei aus seinem lokalen Cache dem Kunden auszuliefern. Der Vorteil liegt auf der Hand: Die Datei muss nicht erneut angefordert werden und die Kosten für den Webbetreiber werden minimiert, da keine erneute Auslieferung der Datei aus der Cloud erfolgt.
Gerade bei Bildern, die sich selten bis nie ändern, macht es Sinn, das Expire-Date zu setzen. Welches Datum bzw. welchen Zeitraum man wählt, ist Ansichtssache. Es gibt Webmaster, die den Zeitraum nahezu bis ins unendliche ausdehnen. In unserem Beispiel erhöhen wir das aktuelle Datum (Upload-Datum) um fünf Jahre. Eine lange Zeit; die wenigsten Rechner bleiben so lange aktiv bzw. die wenigsten Besucher werden ihren lokalen Internet-Cache in diesem Zeitraum nicht leeren. Doch so haben wir auch genügend Zeit, das Bild irgendwann auszutauschen.

Mit folgendem C#-Beispiel-Code setzen Sie das Expire Date für die Amazon S3-Cloud:

//Optional: Expire Date setzen - für Images sinnvoll
CultureInfo ci = new CultureInfo("en-US");
string sExpire = DateTime.Now.AddYears(5).ToString("ddd, dd MMM yyyy hh:mm:ss", ci) + " GTM";
req.AddHeader("Expires", sExpire);

Der gesamte C#-Beispielcode mit entsprechenden Kommentare:

using System;
using System.Collections.Generic;
using System.Text;
using System.IO;

using Amazon.S3;
using Amazon.S3.Model;
using Amazon.S3.Transfer; //Upload
using Amazon.S3.Util;//Upload
using System.Web; //Header
using System.Globalization;

namespace ConsoleApplication1
{
    class Program
    {

        //Amazon S3
        private const string AWS_ACCESS_KEY = "<mein AWS_ACCESS_KEY>";
        private const string AWS_SECRET_KEY = "<mein AWS_SECRET_KEY>";
        private const string BUCKET_NAME = "<mein BUCKET_NAME>";
        private const string S3_KEY = "<mein S3_KEY>";

        public static AmazonS3Client client = new AmazonS3Client(AWS_ACCESS_KEY, AWS_SECRET_KEY);

        static void Main(string[] args)
        {

            //Prüfen, ob ein Ordner in S3 exitiert, ansonsten Ordner anlegen
            if (!DoesFolderExist("mein Ordner")) CreateFolder("mein Ordner");

            //Hochladen von Dateien in die S3
            if (Upload("<Zukünftiger Dateiname in der S3>", "<ordner>", "<lokaler Filepfad>")) Console.WriteLine("Upload in die S3 erfolgreich.");
            else Console.WriteLine("Datei-Upload leider nicht erfolgreich!");

        }

        private static bool Upload(string filename, string ordner, string sFilepfad)
        {
            //Upload - Hochladen von Dateien zu Amazon S3
            bool bRes = false;

            //Ordnerstuktur: Basisornder/unterordner_ordner1
            string basisverzeichnis = "basisordner/" + ordner;

            //Existiert der Ordner in S3?
            if (!DoesFolderExist(basisverzeichnis)) CreateFolder(basisverzeichnis);

            try
            {
                //Upload Datei in Folder
                string key = string.Format("{0}/{1}", basisverzeichnis, filename);

                Amazon.S3.Model.PutObjectRequest req = new Amazon.S3.Model.PutObjectRequest().WithBucketName(BUCKET_NAME);
                req.FilePath = sFilepfad; // Lokaler vollqualifizierter Pfad inkl. Dateiname zur lokalen Quelldatei
                req.Key = key;
                req.Timeout = int.MaxValue; //Timeout nach Wunsch
                req.AddHeader("x-amz-acl", "public-read"); //Optional: Hier werden öffentliche Leserechte gesetzt

                //Optional: Expire Date setzen - für Images sinnvoll
                CultureInfo ci = new CultureInfo("en-US");
                string sExpire = DateTime.Now.AddYears(5).ToString("ddd, dd MMM yyyy hh:mm:ss", ci) + " GTM";
                req.AddHeader("Expires", sExpire);

                client.PutObject(req);

                bRes = true;
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message, "Upload Error. File: " + filename);

            }
            return bRes;
        }

        public static bool DoesFolderExist(string folderName)
        {
            //Amazon S3: Prüft, ob ein Ordner vorhanden ist
            try
            {
                ListObjectsRequest request = new ListObjectsRequest();
                request.BucketName = BUCKET_NAME;
                request.WithPrefix(folderName + "/");
                request.MaxKeys = 1;

                using (ListObjectsResponse response = client.ListObjects(request))
                {
                    if (response.S3Objects.Count > 0) return true;
                }
            }
            catch (Amazon.S3.AmazonS3Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
            return false;
        }

        public static void CreateFolder(string folderName)
        {
            //Neuanlage eines Ordners in S3
            try
            {
                Console.WriteLine("Lege Ordner an: " + folderName);

                //Amazon S3: Anlegen eines Ordners
                var key = string.Format(@"{0}/", folderName);
                var request = new PutObjectRequest().WithBucketName(BUCKET_NAME).WithKey(key);
                request.InputStream = new MemoryStream();
                client.PutObject(request);
            }
            catch (Amazon.S3.AmazonS3Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
        }
    }
}

UPDATE 22.08.2012: Danke an den Leser „Andreas“, der festgestellt hat, dass der hier angelegte „S3-Key“ im Beispielprojekt nicht verwendet wird. Somit wird der Key auch nicht benötigt.