C#: Wie man eine Grafik in eine Resource hinzufügt

In Windows-Form Anwendungen kommt man öfters in die Verlegenheit, irgendwelche Bilder, Icons oder Logos anzuzeigen. Auch in Tabellen oder in einem DataGridView können kleinen Grafiken die Lesbarkeit stark erhöhen. Die Grafiken der eigentlichen Exe beizulegen mag seine Vorteile haben, doch oftmals ist es besser, diese in der ausführbaren Datei zu inkludieren und aus dem Speicher abzurufen. Wie man eine Datei als Resource in Visual Studio bereitstellt, beschreibe ich hier.

Zuerst muss man zum Resourcen-Fenster navigieren. Ein Weg, dorthin zu gelangen, ist ein Rechtsklick im Projektmappen-Explorer auf die Applikation, dann „Eigenschaften“ wählen. Im sich öffnenden Fenster sieht man nun einige Tab-Reiter. Man wählt hier den Tab Reiter „Ressourcen“. Die Schaltfläche „Ressource hinzufügen“ ist eigentlich selbsterklärend. Man wählt die passende Eigenschaft (oder wählt „vorhandene Datei hinzufügen“), vergibt dem Bild bzw. der Ressource einen eindeutigen Namen. Wenn man eine Datei direkt hinzufügt, sollte man je nach Namenskonvention diesen noch anpassen.

Die Grafik Resource in C# ansprechen
Nun haben wir also eine Grafik als Resource hinzugefügt. Nun möchten wir diese auch im Programmcode verwenden. Dies ist in C# nun sehr einfach, denn wir verwenden die statische Klasse „Properties.Resources“. Diese Klasse gewährt uns Zugriff auf die eingebettete Resource.
Im nachfolgenden Beispiel verwenden wir eine PictureBox um eine Grafik anzuzeigen. Eine Grafik „link.jpg“ habe ich als Resource mit dem Namen „link“ hinzugefügt. Der Zugriff auf die Grafik ist nun per „Properties.Resources.link“ möglich:

/// <summary>
/// Wir zeigen eine Grafik in einer PictureBox an
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void Form1_Load(object sender, EventArgs e)
{
            //Eingebette Resource: link.jpg. Resourcen-Name: link
            pictureBox1.Image = Properties.Resources.link;
}
grafik in datagrid
Einbettung eines Image

In Anlehnung an mein Beispiel im Artikel „C# Images in DataGridView anzeigen” greife ich im untenstehenden Beispiel direkt auf die Resource hinzu und spare mir dem Umweg, das Image erst noch explizit zuzuweisen. Wer nicht den direkten Weg über die Properies.Resource gehen möchte, kann den von Microsoft hier beschriebenen Weg über einen Stream gehen.

DataGridViewImageColumn dgv_pic = new DataGridViewImageColumn(false);
DataGridViewColumn dgv_text = new DataGridViewTextBoxColumn();

//Füge die Colums hinzu
dataGridView1.Columns.Add(dgv_pic);
dataGridView1.Columns.Add(dgv_text);

//Hier werden manuell zwei Rows hinzugefügt
dataGridView1.Rows.Add(2);

dataGridView1[0, 0].Value = Properties.Resources.lampe_rot; // (Image)lampe_rot;
dataGridView1[0, 1].Value = Properties.Resources.lampe_gruen; //(Image)lampe_gruen;
dataGridView1[1, 0].Value = "Server A";
dataGridView1[1, 1].Value = "Server B";

Vorteil einer eingebettenen Resource
Der größte Vorteil ist natürlich, dass die Ressource, beispielsweise eine Grafik oder ein Icon nach dem Release in der Exe enthalten ist. Man muss sich keine weitere Gedanken mehr über die Grafik machen und man kann sicher sein, dass die Anwendung auch auf das Bild zugreifen kann. Aber auch der Nachteil liegt klar auf der Hand: Jedes so integrierte Bild vergößert die Exe. Es dürfte also wenig Sinn machen, große Images so zu integrieren. Kleine Status-Bilder oder Icon-Sets können aber so elegant integriert werden.

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]

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.

Amazon S3: Wie wir einen Image-Server realisiert haben

“Cloud” ist in aller Munde. Amazon S3 (Amazon Simple Storage Service) ist im Prinzip eine Cloud. Amazon selbst beschreibt diesen Dienst als “Speicher für das Intranet”. Ich kann also schnell und einfach Dateien dort ablegen und von überall zugreifen. Die Daten werden gespiegelt dort abgelegt, die Datenanbindung ist vorbildlich. Soweit so gut. Nichts wirklich spannendes. Clouds gibt es inzwischen wie Sand am Meer und die meisten Nerds können sich bei der Vorstellung eines neuen Cloud-Anbieters das Gähnen nicht verkneifen. Auch Amazons „Ausführungsanforderungen“ lesen sich wie klassisches Bullshit-Bingo: „Sicher“, „Zuverlässig“, „Skalierbar“, „Geschwindigkeit“, „Günstiger Preis“ und Einfachheit“. Und trotzdem möchte ich den Dienst samt Zugriff per C# vorstellen.

amazon s3 kontoübersichtIm vorliegenden Projekt hatten wir das Problem, eine große Shopping-Seite zu beschleunigen. Alleine die Bilddateien belegten über 50 GB und wurden rege von den Kunden aufgerufen. Schon recht früh erkannte man, dass die Bilder auf einen separaten Server ausgelagert werden mussten, um den Zugriff auf den eigentlichen Webserver zu entlasten. Doch man erkannte, dass ein Server, der die Bilder hostet, nicht mehr ausreicht. Zu groß war die Last, die nur das Ausliefern der Bilder verursachte. Des Weiteren waren die Bilder nicht gespiegelt. Bei einem Servercrash wären die Bilder zwar nicht unwiederbringlich verloren gewesen, doch der Ausfall hätte einiges an Zeit benötigt. Und ein Shopping-Portal ohne Produkt-Bilder ist ein Portal ohne Umsätze. Also überlegte man, wie der/die zukünftigen Server beschaffen sein mussten, damit der Zugriff auf die Bilder schnell erfolgte und diese gleichzeitig zuverlässig gespeichert wurden.
Um alle Anforderungen zu erfüllen mussten also mindestens zwei Server angeschafft werden. Neben den Hardwarekosten, den laufenden Betriebskosten sind die Server-Administrationskosten nicht zu unterschätzen. Dem gegenüber wurden die Kosten für die Amazon S3 gestellt. Für Neueinsteiger bietet Amazon ein AWS Gratisangebot an, was für erste Tests durchaus akzeptabel ist. Dies genutzt, konnte der Dienst ein paar Wochen gestestet werden. Zugriffe und deren Geschwindigkeit wurden getestet, die (produktiv anfallenden) Kosten der Eigenlösung gegenübergestellt.
Aktuell halte ich die Amazon Webserver-Angebote nicht unbedingt für finanziell Vorteilhaft (einige Spezial-Angebote ausgenommen). Auch kleinere Webseiten sind sicherlich besser damit bedient, Teile ihres Webs auf eine andere virtuelle Maschine bei einem Hosten auszulagern, als die Daten in einer S3 zu speichern, denn bei jedem Zugriff eines Users tickt der Amazon-Zähler. Und da kann schon ein „Sümmchen“ zusammenkommen.
Doch bei dem oben beschriebenen Projekt ergab sich ein Kostenvorteil. Zudem überzeugte die Zugriffszeit der S3. Genutzt wird der Standort EU (Irland).

Weiter: Amazon S3: Zugriff auf S3 per C#

Suchmaschinen-Besucher und Search-Keys aus Apache-Logfile ermitteln

Es interessiert einem brennend: Wie viel Besucher habe ich durch Suchmaschinen erhalten und vor allem, nach was haben die Besucher über die Suchmaschine gesucht? Die Antwort gibt das Server-Logfile, welches detailliert die Besucher protokolliert. Die gängigste Methode ist, das Logfile in einem Texteditor zu öffnen und manuell nach den Zugriffen zu schauen. Dies macht man eine Weile, dann hat man keine Lust mehr. Denn man möchte schnell eine Übersicht über die Suchmaschinen-Besucher und ihre Suchbegriffe.
Dann sucht man im Internet nach Logfile-Parser und wird schnell fündig. Neben online-Webstatistiken wie AWStats oder Freeware für das Offline-Parsen findet man eine ganze Reihe an Parsern; die meisten jedoch viel zu kompliziert oder eben nicht speziell für diesen Zweck zugeschnitten, so dass die Handhabung zu umständlich ist.

Logfile-Parser speziell für das Ermitteln von Suchmaschinen-Benutzer
Irgendwann war ich es leid und habe den Sirmark-Suchmaschinen-Pareser geschrieben, den ich hier zum Download anbiete. Das Programm ist Freeware, Ihr könnt es kostenlos downloaden und benutzen. Einen Support biete ich jedoch NICHT!

DOWNLOAD: Logfile-Parser SLP V1.2

Systemvoraussetzungen für den Suchmaschinen-Parser
Der Parser ist für Windows .NET geschrieben. Er läuft ab Windows XP und benötigt mindestens das .NET-Framewerk 2.0. Er besteht aus einer einfachen EXE, benötigt keine Installation oder Registry-Einträge. Also einfach downloaden und starten.

Benutzung von Sirmarks-Suchmaschinen-Parser

Apache Logfile auswerten
Logfile Parser: Apache Logfile öffnen

Die Handhabung des Suchmaschinen-Parsers ist sehr einfach. Nach dem Starten der Anwendung klickt man auf den Button „Logfile laden“. Es öffnet sich ein Dialog, über den man das Logfile auswählt. Dies bedeutet, dass das Logfile also auf der lokalen Windows-Maschine oder per Datenfreigabe über Windows erreichbar sein muss. Also gegebenenfalls das Logfile via FTP vom Webserver herunterladen.
Gezippte Logfiles: Der Logfile-Parser entpackt das Logfile NICHT automatisch. Wenn es sich um ein gezipptes Logfile handelt („.gz“), das Logfile vorher beispielsweise mit 7.zip entpacken!

Logfile nach Suchbegriffen analysieren

Logfile nach Suchbegriffen analysieren
Alle Besucher von Suchmaschinen werden mit dem Suchbegriff ermittelt

Wenn ein Logfile ausgewählt wurde, beginnt der Parser sofort mit seiner Arbeit. Er geht das Logfile zeilenweise durch und zeigt die gefundenen Zeilen in der Box „Zeilen“ zur Laufzeit an. Die Anzahl der Zeilen sowie der Fundstellen wird in einem Label angezeigt.
Wenn das Logfile komplett analysiert wurde, werden die Suchstellen aufbereitet. In der unteren Tabelle erscheinen alle Suchstellen aufbereitet nach Datum, Suchmaschine, Search-Key, Ziel-URL und die IP-Adresse des Besuchers.

Tipps für die Nutzung des Suchmaschinen-Parsers
Beide Tabellen sind durch einen horizontalen Slider miteinander verbunden. Wenn man mit der Maus in den Bereich zwischen der Anzeige „Zeilen“ und der Tabelle fährt, ändert sich der Mauszeiger und man kann die Größe der Elemente ändern.
Die Datenzeilen in der aufbereiteten Tabelle sind markierbar und können so per Copy and Paste schnell in eine Textdatei oder Excel kopiert werden. Um beispielsweise die gesamte aufbereitete Tabelle nach Excel zu kopieren, klickt man am einfachsten (1) in der Tabelle an; die gesamte Tabelle wird markiert. Nun wählt man auf der Tatstatur „Strg“ + „c“ für „Copy“, wechselt dann nach Excel oder einem Texteditor und wählt dort einfügen (oder „Strg“+ „p“ („Paste“).

Change-Log
Version 1.1:

  1. Suchanfragen werden nun per URL Decode umgeschlüsselt.
  2. BUG in Suchanfrage (String-Länge) bereinigt.
  3. Bot, Spider und Crawler-Tab hinzugefügt: Ab dieser Version werden auch alle Crawler-Zugriffe aus dem Logfile extrahiert und aufbereitet in einem eigenen Tab aufgelistet. Durch die Möglichkeit, die Tabelle zu sortieren (Klick in den Tabellen-Spaltenkopf), kann schnell eine Übersicht über die Crawler-Tätigkeit einzelner Bots (Googleboot, Bing etc) erhalten werden.

Version 1.2

  1. Tool kommt nun mit weiteren verschiedenen Apache-Logfiles zurecht (Logfile Aufbau-Erkennung)
  2. Beschleunigung der Verarbeitungsgeschwindigkeit: Durch C#-Optimierung konnte die Abarbeitungsdauer eines Referenz-Logfiles von 1:37 min auf 1:13 min minimiert werden.
  3. Ausführungsdauer wird nun im Fenstertitel mit angezeigt
  4. Um die Verarbeitungsgeschwindigkeit zu beeinflussen, kann nun der Analysemodus (alles, nur Suchanfragen, nur Bots) beeinflusst werden
  5. BUG: Aufgrund der optimierten Abfragegeschwindigkeit kann es vorkommen, dass normale Besuchereinträge fälschlicherweise als Bot erkannt werden.

Alternativer PHP Cache (APC) installieren

Installation Alternativer PHP Cache APC Linux Ubuntu Plesk

Die Installation des „Alternativer PHP Cache“ (APC)  ist eigentlich schnell erledigt. Da jedoch nicht jede im Internet verfügbare Lösung oder jedes HowTo zum Ziel führt, möchte ich hier einige Wege aufzeigen. Gerade unter Ubuntu/Plesk kann die Installation zu Problemen führen. Wie diese Probleme umgangen werden, zeige ich hier.

APC bzw. der Alterative PHP Cache ist ein freies PHP Modul, welches eine robuste Möglichkeit bietet, PHP Zwischencode zu cachen und somit die Zugriffszeiten einer dynamischen PHP-Seite massiv zu optimieren. Gerade Google scheint immer mehr darauf zu achten, dass eine Webseite möglichst schnell geladen wird. Das Stichwort hierzu lautet Page Speed.  Da offensichtlich die Ladezeit einer Webseite sich auch auf den Google-Suchindex auswirken, müssen Webmaster inzwischen auch die Ladezeit im Auge behalten. Im Prinzip auch ein richtiger Ansatz. Kaum ein Leser möchte bei jedem Klick mehrere Sekunden auf den Aufbau der folgenden Seite warten. Im Zeitalter von Breitband-Anschlüssen und DSL verliert der Leser bei langen Wartezeiten schnell das Interesse an einer Seite.

Über die PHP.ini ist schnell ersichtlich, ob APC installiert wurde

Das PHP-Modul APC ist aktuell noch nicht im Basis-Umfang von PHP enthalten. Dies wird wohl – wie man im Internet lesen kann – sich in einer der folgenden Versionen ändern. Wohl aus diesem Grund ist das Interesse an diesem Cache sprunghaft gestiegen, ist es doch ein Garant, dass der Code dieses Caches von den Machern von PHP als valide angesehen wird. Aktuell müssen wir APC noch per Hand nachinstallieren. Prüfen Sie zuerst über eine PHPINFO(), ob das Modul APC auf Ihrem Server installiert ist.

Je nach Linux-Webserver-Installation führen unterschiedliche Wege zur Installation von APC Bytecode Cache. Im einfachsten Fall genügen die folgenden Zeilen:

# sudo aptitude install php-apc
# sudo /etc/init.d/apache2 restart

Leider wird gerade unter Plesk der Versuch wie folgt enden:

root@XXX:/usr/local/src/APC-3.1.8# apt-get install php-apc
Reading package lists... Done
Building dependency tree
Reading state information... Done
E: Couldn't find package php-apc

Wenn APC nicht als Paket vorhanden ist, müssen wir es manuell installieren. Zuerst besuchen wir die Seite http://pecl.php.net/package/APC und ermitteln die aktuellste Version sowie den Download-Link der aktuellen TGZ-Version. Hier in diesem Beispiel ist es die Version „APC-3.1.9.tgz”.

cd /usr/local/src
wget http://pecl.php.net/get/APC-3.1.9.tgz
tar xf APC-3.1.9.tgz
cd APC-3.1.9/
phpize

Fehlermöglichkeit PHP: pecl install liefert „ERROR: ‚phpize‘ failed“
Wenn nach der Eingabe von „phpize“ die Fehlermeldung „ERROR: ‚phpize‘ failed“ erscheint, deutet dies darauf hin, das PECL nicht auf dem Server installiert wurde. Holen Sie dies nach:

apt-get install apache2-threaded-dev php5-dev php-pear make

Wenn der Befehl “phpize” keinen Fehler mehr liefert, können Sie wie folgt weitermachen:

./configure
make
make install

Nun ist APC erfolgreich installiert. Damit PHP dieses Modul auch läd und nutzen kann, muss es in der php.ini auch eingefügt werden. Hierzu können Sie folgenden Befehl nutzen:

echo „extension=apc.so“ >>/etc/php.ini

Status des APC-Cache

Je nach Installation hat PHP auch einen Ordner mit dynamischen Inhalten. Prüfen Sie, ob der Ordner
/etc/php5/apache2/conf.d
vorhanden ist. Wenn ja, kopieren Sie ein vorhandenes Ini-File und ändern den darin vorhandenen Dateipfad wie folgt ab:
extension=apc.so

Nach dieser Änderung müssen Sie den Apache Webserver neu starten:
/etc/init.d/apache2 restart
Plesk-Nutzer sollten den Neustart des Apache Webserver über die Weboberfläche von Plesk erledigen.

Prüfen Sie über eine PHPINFO(), ob PHP APC erfolgreich läd.

Daten unter Linux zippen und schnell übertragen

Wenn man beispielsweise eine MySQL-Tabelle von einem Master Server zum Slave übertragen muss und diese Tabelle schon eine stattliche Größe angenommen hat, macht man sich durchaus Gedanken, wie man den Datenübertrag am sinnvollsten bewerkstelligt.

Datenübertrag per WinSCP
Wer sich zwischen der Windows und der Linux-Welt bewegt, kennst sicherlich das Windows-Tool WinSCP. Mit diesem Tool kann von einem Windows-Rechner per grafischer Benutzeroberfläche auf einen Linux-Server zugegriffen werden. Auch der Datenaustausch zwischen beiden Rechnern ist möglich. Wenn allerdings von einem Linux-Recher zum Anderen die Verbindung erfolgen soll, müsste man die Daten zuerst auf Windows-Rechner zwischengespeichert werden, was in den meisten Fällen weniger sinnvoll ist. Positiv anzumerken ist, dass WinSCP auch ein abgebrochener Upload selbstständig wieder aufnimmt.

Datenaustausch mittels SCP (Secure Copy)
Der direkte Weg zur Datenübertragung zwischen zwei Linux-Rechnern sollte per SCP (Secure Copy) erfolgen. Die Übertragung erfolgt verschlüsselt vom Quell- zum Zielserver. Beginnend vom Quellserver gilt folgende Befehlskette:

scp -r orig_directory USERNAME@machine2:destination_directory

Der Parameter “-r” veranlasst SCP das angegebene Directory rekursiv zu übertragen. Wenn man nur eine einzelne Datei übertragen möchte, kann dieser Parameter weggelassen werden.

Nach Absetzen des Befehles nimmt SCP Kontakt zum Zielserver auf. Danach erfolgt die Abfrage des Passwortes (von USERNAME) und der Übertrag beginnt.
NACHTEIL: Leider kann ein abgebrochener Upload mittels SCP nicht wieder aufgenommen werden. Da gemäß Murphys Gesetz der stundenlange Upload meist bei 99 Prozent abbricht, ist dies ein sehr ärgerlicher Nachteil. Wie man den Abbruch eines Uploads umgehen kann, kann im Artikel „Upload mit SCP abgebrochen, was nun?“ nachgelesen werden.

Dateien unter Linux zippen (komprimieren)
Die bekannteste Komprimierungsform unter Windows ist sicherlich ZIP. Auch unter Linux ist ZIP erhältlich, wenn auch nicht in jeder Distribution vorhanden. In den Linux-Paketen ist der Zipper jedoch meist integriert, so dass die Installation schnell und einfach erledigt ist.

Um ein Directory zu zippen, genügt folgender Befehl:

zip -r archive_name.zip directory_to_compress

Um eine Datei zu entpacken, genügt:

unzip archive_name.zip

Dateien packen mittels TAR.GZ
Verbreiteter als der Zipper ist in der Linux-Welt sicherlich tar.gz. Glaubt man einigen Meinungen im Internet (die ich nicht in Frage stelle), erreicht dieser Zipper wesentlich bessere Komprimierungsraten und schon die CPU beim zippen im Gegensatz zum Zipper.
Um ein Verzeichnis zu zippen, wählt man:
tar -zcvf archive_name.tar.gz directory_to_compress

Zum Entpacken im aktuelle Verzeichnis wählt man
tar -zxvf archive_name.tar.gz

Will man seine Daten in einem bestimmten Verzeichnis entpacken, gibt man das Zielverzeichnis explizit an:
tar -zxvf archive_name.tar.gz -C /tmp/extract_here/