BigBasti's Blog About Me & my Digital Lifestyle

30Aug/112

Probleme mit Plesk Admin und MySQL

Es gibt Tage da zickt Plesk herum und man könnte verzweifeln weil plötzlich die Adminoberfläche, die gestern noch funktionierte, nun nicht mehr laden will und Plesk sich mit der Fehlermeldung verabschiedet:

Error: Test connection to the database server has failed because of network problems:
Failed to connect to database: Access denied for user 'admin'@'localhost' (using password: YES)

Lösung:

Zum Glück lässt sich das Problem ganz einfach aus der Welt schaffen, dazu muss man das Admin Passwort für MySQL neu setzen.

  1. Loggt euch auf dem Server über RemoteDesktop (oder SSH) ein
  2. Öffnet die my.ini aus eurem Plesk Verzeichnis ( %plesk_dir%databases\mysql\data )
  3. fügt den Parameter "add the parameter skip-grant-tables=1" hinzu und speichert.
  4. Startet den MySQL Service neu
  5. Verbindet euch zu der MySQL Datenbank ( "%plesk_dir%\mysql\bin\mysql.exe" mysql )
  6. Führt folgenden Befehl aus:
  7. mysql> update user set password=password('euer_passwort') where user='admin'; Wobei ihr hier euer Admin-Passwort eintragen solltet dass Plesk nutzt um sich anzumelden
  8. Entfernt den Parameter aus der my.ini den ihr im Schritt 3. hinzugefügt habt
  9. Startet dem MySQL Dienst erneu neu

Das Problem sollte sich nun erledigt haben.

8Jul/101

Microsoft SQL Server Compact Edition – Eine Einfuehrung

Oft müssen Clientprogramme große Datenmangen speichern, und in den meisten Fällen wird dazu eine Datenbank eingesetzt. Wenn man eine Internetverbindung voraussetzt kann man einen mächtigen Datenbank Server anbinden und die Daten in der Cloud sichern. Doch oft ist eine Offline Lösung viel einfacher und effektiver.

Inzwischen gibt es auch viele verschiedene lokale Datenbanksysteme wie zB. SQLite, DB4O oder auch MS SQL Server Compact Edition. Alle diese Serversysteme haben auch den Vorteil, dass diese auf den mobilen Systemen problemlos eingesetzt werden können, haben aber auch den Nachteil, dass sie meist nicht den Vollen Feature Umfang eines echten SQL Servers bieten können.

Vorbereitung

In diesem Artikel soll es um den SQL Server Compact Edition von Microsoft gehen, diesen wollte ich schon immer mal testen und nun hatte ich mal die Zeit dazu.

Sofern ihr es noch nicht habt könnt ihr den SQL CE Server kostenlos bei Microsoft herunterladen. Nach der Installation arbeiten wir wie gewohnt im Visual Studio.

Implementierung

Die ganze Implementierung der Datenbank, Erzeugung der Tabellen sowie die Anbindung erfolgt aus Visual Studio heraus. Die Funktionen die man dort hat sind aber auch  stark von dem Provider abhängig, so bieten verschiedene Provider mehr oder weniger Funktionalität.

Um eine Datenbank zu einem Projekt hinzuzufügen öffnet einfach den Projektmappen-Explorer (Ansicht->Projektmappen-Explorer) und Klickt mit der rechten Maustaste auf euer Projekt und dann auf "Hinzufügen"->"Komponente..." und wählt eine Lokale Datenbank aus. In dem Datenbank Assistent Fenster klickt nun 2 Mal auf "Zurück" um auf die erste Seite des Assistenten zu gelangen.

Klickt nun auf "Neue Verbindung". In dem folgendem Fenster sollte man darauf achten, dass als Datenquelle "Microsoft SQL Server Compact 3.5" ausgewählt ist.

Bild 1: Datenbank Konfiguration

Klickt nun auf "Erstellen..." nun solltet ihr das Fenster aus Bild 1 sehen. Wählt oben den Speicherort eurer Datenbank und legt wenn nötig ein Passwort fest um die Datenbank zu verschlüsseln. Bestätigt das Ganze noch zwei Mal mit OK. Visual Studio sollte euch dann darauf hinweisen, dass ihr vorhabt eine Lokale Datenbank zu verwenden und schlägt euch vor diese in euer Projekt zu importieren - das nehmen wir gerne an.

Visual Studio fügt die neu erzeugte Datenbank Datei dem Projekt hinzu und setzt auch die nötigen Verweise auf System.Data und auf System.Data.SqlServerCe.

Anlegen der Tabellen

Beim Anlegen der Tabellen in unserer Datenbank ist uns Visual Studio natürlich ebenfalls sehr behilflich. Öffnet einfach den Server-Explorer (Ansicht->Server-Explorer) und erweitert eure frisch angelegte Datenbank. (Eventuell müsst ihr auch ein Passwort angeben wenn ihr die Datenbank verschlüsselt habt)

Klickt nun mit der rechten Maustaste auf Tabellen und wählt "Tabelle erstellen" um den Tabellen Assistenten zu starten.

Bild 2: Tabellen Assistent

Hier könnt ihr eure Tabelle anlegen und konfigurieren. Wenn ihr die nun fertige Tabelle im Server-Explorer rechts anklickt und dann "Tabelleneigenschaften" wählt bekommt ihr noch mehr Konfigurationsmöglichkeiten wie etwa Beziehungen.

Bereits beim Erstellen der Tabellen ist es euch vielleicht schon aufgefallen, dass es nicht so viele Datentypen zur Auswahl gibt wie in einem richtigen SQL Server aber das sind nicht die einzigen Einschränkungen...

Arbeiten mit der Datenbank

Das Arbeiten mit der Datenbank gestaltet sich nicht anders als mit anderen Datenbanken. Die Objekte die man dafür benötigt heißen sogar fast gleich. Alle Klasse die man nutzt haben noch den Zusatz "Ce", also statt "SqlCommand" heißt es nun "SqlCeCommand".

Und auch die Nutzung unterscheidet sich nur geringfügig:

Datenbank Verbindung herstellen:


        private SqlCeConnection con;
        private string dbLocation = Environment.CurrentDirectory + "\\clipboard.sdf";
        private string conString;

        private void connectDB(){
            conString = "Data Source=\"" + dbLocation + "\"; Password=\"bigbasti.com\"; Encrypt=True";
            con = new SqlCeConnection(conString);
            con.Open();
        }

Eine Select Abfrage durchführen:


        ///
        /// Führt einen Select Befehl auf der Datenbank aus und gibt das ergebnis in einer DataTable zurück
        ///
        ///Der auszuführende Befehl
        /// Return: Ein DataTable mit den ausgelesenen Daten
        public DataTable executeSelect(string query) {
            connectDB();
            DataTable dt = new DataTable();
            SqlCeCommand com = new SqlCeCommand(query, con);
            SqlCeDataAdapter sad = new SqlCeDataAdapter(com);

            sad.Fill(dt);
            con.Close();
            return dt;
        }

        public int checkDouble(string link, string date) {
            int retVal;
            using (SqlCeConnection con = new SqlCeConnection(conString)) {
                con.Open();

                //Doppelte Einträge verhindern
                DataTable dt = executeSelect("SELECT * FROM cp_link WHERE link LIKE '" + link + "'");
                if (dt.Rows.Count > 0) {
                    executeNonQuery("DELETE FROM cp_link WHERE link LIKE '" + link + "'");
                }
            }
            return retVal;
        }

UPDATE, DELETE und INSERT:


        ///
        /// Executes an SQL statement against the SqlCeConnection and returns
        /// the number of rows affected.You also can use ExecuteNonQuery to
        /// change the data in a database without using a DataSet by executing
        /// UPDATE, INSERT, or DELETE statements.
        ///
        ///Der Befehl der ausgeführt werden soll
        /// The number of rows affected.
        public int executeNonQuery(string q) {
            int retVal;
            using (SqlCeConnection con = new SqlCeConnection(conString)) {
                con.Open();
                using (SqlCeCommand com = new SqlCeCommand(q, con)) {
                    retVal = com.ExecuteNonQuery();
                }
            }
            return retVal;
        }
        public int doSomeInsert(string link, string date) {
            int retVal;
            using (SqlCeConnection con = new SqlCeConnection(conString)) {
                con.Open();
                using (SqlCeCommand com = new SqlCeCommand("INSERT INTO cp_link  (link, date) VALUES (@link, @date )", con)) {
                    com.Parameters.AddWithValue("@link", link);
                    com.Parameters.AddWithValue("@date", date);
                    retVal = com.ExecuteNonQuery();
                }
            }
            return retVal;
        }

Hier gibts eigentlich nichts besonderes. Leider hat man hier, da es eine Compact Edition ist nicht so viele Befehle zur Verfügung wie im Original. So fehlt zB. auch der TRUNCAT Befehl wie ich erstaunt feststellen musste. Ich habe da nicht noch weiter recherchiert was noch alles fehlt, da ich für das was ich gemacht habe keine besonderen Aktionen benötigte.

Deployment

Wenn ihr das Projekt über ein Setup-Projekt deployen wollt dann sollte da eigentlich nichts schief gehen. Ihr könnt aber natürlich auch ohne ein extra Setup-Projekt deployen, dazu solltet ihr folgende Schritte machen:

  1. Öffnet die Eigenschaften eures Projekts
  2. Öffnet dort den "Veröffentlichen" (Publish) Tab
  3. Klickt auf den Knopf "Erforderliche Komponenten"
  4. Sucht den Eintrag "SQL Server Compact Edition 3.5" und entfernt das Häkchen und schließt die Eigenschaften
  5. Nun benötigen wir die erforderlichen DLL Dateien, diese liegen in "C:\Programe\Microsoft SQL Server Compact Edition\"
  6. Es sind 7 oder auch mehr Dateien, abhängig davon wie viele Sprachen installiert sind (siehe Bild links)
  7. Markiert diese DLLs und zieht diese direkt in den Projektmappen-Explorer, da mit diese DLLs dem Projekt hinzugefügt werden
  8. Markiert die neuen DLLs in dem Projektmappen-Explorer -> klickt mit der rechten Maustaste darauf und wählt "Eigenschaften"
  9. Setzt die Eigenschaft "In Ausgabeverzeichnis kopieren" auf "Kopieren, wenn neuer" damit ihr immer die Aktuelle Version im Build Verzeichnis habt
  10. Klickt im Projektmappen-Explorer mit der rechten Maustaste auf System.Data.SqlServerCe und wählt Eigenschaften
  11. Setzt hier die Eigenschaft "Lokale Kopie" auf True. Dieser Schritt ist nötig damit das Programm lokal nach der DLL sucht und nicht versucht sich diese aus dem GAC (Global Assembly Cache) zu holen.

Und das war es eigentlich schon. Nun könnt ihr das Projekt problemlos deployen.

12Jan/100

Java: DB4O Anbindung in einer Client / Server umgebung

Da ich mich zur Zeit selbst mit diesem Thema auseinander setze, dachte ich, es könnte nicht schaden ein kleines Tutorial zu schreiben. Hier möchte ich die ersten Schritte erklären und an ein paar Beispielen demonstrieren.

DB4O steht für Database for Objects und ist, wie der Name es schon sagt eine Datenbank für Objekte. Im Unterschied zu einer SQL Datenbank (wie MySQL) werden hier keine Tabellen erstellt, in denen die Daten gesichert werden, sondern die Objekte "als Ganzes" in die Datenbank geschrieben.

Das bringt einen gewaltigen Vorteil, denn wenn man ein Objekt in einer herkömmlchen relationalen Datenbank sichern möchte muss man es in einzelne Teile zerlegen und diese Teile dann in die Tabellen schreiben. Beim Auslesen wieder das ganze Spielchen umgekehrt, man sucht sich alle Werte des Objekts wieder aus der Datenbank zusammen um dann eine neue Instanz davon zu erstellen und mit den alten Werten zu füllen.

All das ist mit DB4O nicht mehr nötig, da hier die Datenbank diese Aufgaben für uns übernimmt, somit ist es sehr einfach für uns auch sehr komplexe Objekte schnell und bequem zu sichern und zu laden.

Fangen wir aber ersteinmal mit der Installation an

Die Installation, wenn man diese so nennen kann ist bei DB4O sehr einfach, und besteht nur darin, die nötigen Librarys in das Projektverzeichnis zu kopieren. Somit ist keine Installation beim Client mehr nötig, das Programm bringt dann alles was nötig ist selbst mit!

Als erstes benötigt ihr die Librarys von der DB4O - Homepage

Nach dem ihr diese heruntergeladen habt erstellt ihr ein "lib" Verzeichnis in eurem Projektordner.

In diesen neu erstellten Ordner entpackt ihr die *.jar Dateien aus dem heruntergeladenen Archiv aus dem gleichnamigen Ordner.

Nun wechselt ihr in Eclipse (bzw. eure IDE) und aktualisiert mal die Ansicht, ihr solltet nun den neuen Ordner mit den ganzen *.jar Dateien sehen.

Der letzte Schritt besteht darin, die Librarys zu dem Klassenpfad hinzuzufügen. Dazu klickt ihr mit der Rechten Maustaste auf euer Projekt und wählt "Properties", dann wählt ihr rechts "Java Build path", und nun den Tab "Libraries".

Nun klickt ihr auf "Add JARs..." und wechselt in euren lib Ordner. hier wählt ihr alle *.jar Dateien aus und klickt 2x auf "OK". - Das wars auch schon!

Wenn das alles erledigt ist, sollte euer Projektbaum ca so auusehen wie auf dem Bild links.

Die Praxis

Wenn ihr die Installation schon einfach fandet, dann werdet ihr die Implementation lieben! Denn das ist genauso schnell erledigt, denn alle große Arbeiten übernehmen für uns die Libraries, die wir im letzten Schritt importiert haben!

Als erstes benätigen wir eine Klasse deren Objektinstanzen wir dann in die Datenbank schreiben können. Dazu habeich eine einfache kleine Klasse "Car" geschrieben:


import java.io.*;
import java.util.*;

public class Car implements Serializable{
	private static final long serialVersionUID = 1L;
	private String marke;
	private int ps;

	private LinkedList teile = new LinkedList();

	// Konstruktoren

	public Car(){}

	public Car (int ps){
		this.ps = ps;
	}
	public Car(String marke){
		this.marke = marke;
	}
	public Car(String marke, int ps){
		this.marke = marke;
		this.ps = ps;
	}
	public void addTeil(String teil){
		teile.add(teil);
	}

	// Getter & Setter

	public LinkedList getTeile(){
		return teile;
	}
	public void setMarke(String marke){
		this.marke = marke;
	}
	public void setPs(int ps){
		this.ps = ps;
	}
	public String getMarke(){
		return this.marke;
	}
	public int getPs(){
		return ps;
	}
}

Wichtig ist bei dieser kleinen Klasse ist, dass sie das Interface java.io.Serializable implementiert, damit es auch selialisiert werden kann!

Ok nun haben wir ein Objekt, wie krigen wir dieses in die Datenbank? Das geht sehr einfach, wie man an diesem Codeschnippsel sehen kann:


    ObjectContainer db = Db4o.openFile
        ("C:/test.yap");
    try{
        db.set(new Car("VW", 124));
    }
    catch(DatabaseFileLockedException e){
        e.printStackTrace();
    }
    finally{
        db.close();
    }

Erst wird ein ObjectContainer erstellt, in den wir die Datenbankdatei einlesen, die bei DB4O die Endung yap haben. Wenn diese Datei noch nicht vorhanden ist wird diese automatisch angelegt. Hier gibt es zu beachten, dass die Pfadangabe nicht mit dem Windowstypischen "\" (Backslash) angegebenwird, sondern mit einem normal Slash.

Im folgenden Try-Catch Block wird die Methode Set aufgerufen, der wir auch eine Instanz unseres Car-Objekts übergeben. Das wars auch schon, nun ist diese instanz der Car Klasse schon in der Datenbank gespeichert!

Genauso einfach geht es auch einene Wert aus der Datenbank wieder auszulesen:


bjectContainer db = Db4o.openFile
        ("C:/test.yap");
    try{
        ObjectSet result = db.get(new Car(null));
    }
    catch(Exception e){
        e.printStackTrace();
    }
    finally{
        db.close();
    }

Dazu wird die Methode Get benutzt, die als Rückgabewert ein ObjectSet mit allen Autos in der Datenbank liefert. Als Parameter wird der Get Methode eine leere Car Klasse übergeben, zu dem Warum komme ich nun.

Abfragesprachen / Abfragemethoden

Wie ihr an dem Letzten Codebeispiel gesehen habt wird hier keine SQL Sprache genutzt um die nötigen Werte aus der Datenbank zu ziehen, sondern es wird mit Objekten gearbeitet. DB4O bietet drei verschiedene Wege um an die gespeicherten Objekte zu kommen:

QBE - Query by Example
SODA - Simple Object Database Access
NQ - Native Queries

Ich werde hier aber nur auf die ersten zwei zusprechen kommen.

QBE bietet die einfachste Möglichkeit an unsere gespeicherten Objekte zu kommen, diese Methode habe ich auch bei dem Beispiel oben genutzt.

Bei dieser methode wird vom Benutzer ein Objekt erzeugt, nach dem dann in der Datenbank gesucht wird. Wenn wir den Fall von eben aufgreifen, wo nur eine leere Car Klasse übergeben wurde, werden wir sehen, dass der Wert null mit allen Werten übereinstimmt. Das heißt, will ich alle Werte aus der Datenbank haben, übergebe ich ihr eine "leere" Klasse.

Wenn ich die Get Abfrage etwas ändere und statt null, "VW" übergebe, würde uns die Datenbank alle Autos ausspucken, die als marke VW haben. Genauso können wir auch nach der PS Zahl suchen, indem wir statt null eine Zahl übergeben. Wir können aber auch, wenn wir genau wissen wonach wir suchen, auch beide Werte übergeben. db.get("VW",120);

Achting, wir können diese methoden nur anwenden, weil wir in der Car Klasse die einzelnen Konstruktoren definiert haben. So muss für jede Suchanfrage auch der passende Konstruktor vorhanden sein. Oder man erstellt einen Konstruktor, mit dem man alles abdecken kann. So könnte man in unserem Beispiel hier auch so vorgehen um alle VW Autos gelistet zu bekommen: db.get("VW", null);

Leider hat diese Vorgehensweise so simpel sie auch ist entscheidende Nachteile. So dürfen in den Konstruktoren keine Werte initialisiert werden, denn wenn ich im Konstruktor die Variable marke standardmäßig als marke = "VW"; initialisieren würde, würde mir die Datenbank bei jeder Suche nur Autos der marke VW ausspucken.

Desweiteren kann man keine Bedingungen benutzen, denn was ist wenn ich zB. alle Autos sehen will, die mehr als 100 PS haben?

Genau hier setzt SODA an. Hier sollte man wissen, dass alle Abfragen in DB4O unter der Haube in SODA laufen, so wird jede Anfrage automatisch konvertiert.

SODA bietet uns hier viele weitere Werkzeuge die man auch aus zB. SQL kennt. So können wir nun auch komplexere Abfragen erstellen. Setzen wir nun mal unser Problem von eben um und fragen alle Autos ab, die mehr als 100 PS haben:


		Query q = aClient.query();
		q.constrain((Car.class));
		q.descend("ps").constrain(100).greater();
		listResult(q.execute());

Wie man sieht ist diese Abfragemethode etwas komplexer. Aber auf den zweiten Blick ist es aber doch ganz logisch. Zu erst wird ein Query Objekt erzeugt, dem wir dann ein "constrain" (zu Deut. Beschränkung, Begrenzung) übergeben. Diesem übergeben wir "Car.class", somit sagen wir dem Query, dass er nur Objekte vom Typ Car aus der Datenbank holt.

Die nächste Zeile ist eigentlich auch selbsterklärend, dort setzen wir fest, dass die Ergebnisse absteigend (descend) sortiert werden, undzwar nach der Eigenschaft "ps" und durch eine Weitere Beschränkung nur Werte über 100 akzeptiert.

Die letzte Zeile führt den Befehl aus.

Ähnlich kann man auch einen Maximalwert setzen, sodass nur Autos angezeigt werden die zwischen 100 und 130 PS haben:


		Query q = aClient.query();
		q.constrain((Car.class));
		Constraint c1 = q.descend("ps").constrain(100).greater();
		q.descend("ps").constrain(130).smaller().and(c1);
		listResult(q.execute());

Natürlich kann man auch Autos abrufen, die einen bestimmten Wert enthalten. ZB alle Autos die die den Buchstaben w im Namen haben:


		Query q = aClient.query();
		q.constrain((Car.class));
		q.descend("marke").constrain("w").like();
		listResult(q.execute());

Client / Server umgebung

So, nach der Kurzen Einführung wollen wir nun das eigentlich Ziel umsetzen, nämlich die Datenbank in ein Client Server System integrieren. Zum Glück ist das ebenfalls nicht allzu kompleziert!

Malen wir uns erstmal ein kleines Diagramm um zu verbiltlichen, wie der Aufbau der Programme sein soll:

Bild 2: Der Aufbau

Das Tolle ist, dass die DB4O Libraries und die Arbeit abnehmen eine Verbindugn aufzubauen, die Sockets zu verwalten und die einzelnen Threads anzulegen. Somit müssen wir uns nur auf die Implementierung konzentrieren.

Unsere Server Applikation ist dementsprechend sehr einfach gestrickt:


import com.db4o.*;

public class Server {
	public static void main (String [] args){

		//Server starten mit lokaler Datei
		System.out.println("Starte Server...");
		ObjectServer server = Db4o.openServer("C:/test.yap",54321);

		server.grantAccess("test","pass");
		server.grantAccess("andererUser","pass2");
		try {
			while(true){
				//Leerlauf damit der Server anbleibt
				System.out.println("SERVER: [DB4O] läuft...");
				Thread.sleep(10000);
			}
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		finally {
			server.close();
		}
	}
}

Wie man sieht sieht man nicht viel. Was auffällt, ist dass man nun statt openFile openServer benutzt wird, und als zweiten Parameter nun einen Port mitgibt, auf dem der Server auf Verbindungen wartet.

Danach werden noch die Benutzer beschrieben, denen der Zugriff auf die Datenbank gewährt werden soll. Und eigentlich wars das auch schon! Die anschließende Schleife sorgt nur dafür, dass das Programm unendlich läuft (zumindest bis der Benutzer es terminiert) und Anfragen von Clients akzeptiert!

Gut nun schauen wir uns mal den Client an:


import com.db4o.*;

public class ClientAdd {

	static ObjectContainer aClient = null;

	public static void main(String [] args){
		aClient = Db4o.openClient("localhost", 54321, "test", "pass");

                //------------------------------------
		Car myCar1 = new Car("Mercedes", 98);
		Car myCar2 = new Car("VW", 158);

		myCar1.addTeil("Dieselmotor");
		myCar1.addTeil("Klimaanlage");
		myCar1.addTeil("ABS");

		myCar2.addTeil("ERP");
		myCar2.addTeil("Schiebedach");
		myCar2.addTeil("Automatik");

		//Sende Objekte
		System.out.println("Sende Objekte...");
		aClient.set(myCar1);
		aClient.set(myCar2);

		aClient.commit();
		System.out.println("Fertig");

		aClient.close();
	}
}

Wie man sieht ist die Verbindung zum Server noch einfacher gestrickt als der Server. Es wird wiedermal ein ObjectContainer instanziert und kann nun genauso benutzt werden wie in den Beispielen von oben.

Ich denke mal das dürfte als Einstieg reichen. Wenn ihr Probleme oder Fragen habt könnt ihr diese hier gern posten, vielleciht kommt dann irgendwann ein zweiter Teil.

Ein Lauffähiges Beispiel gibts hier: Download [Eclipse Project]

Wer mehr erfahren will guckt hier.
Eine wetere gute Anleitung gibts hier.

14Nov/093

Mod_Rewrite unter Microsoft IIS nutzbar machen

iisEin Umstieg von Unix & Apache auf Windows & IIS kann ganz schön stressig sein. Da ich letzte Woche selber mit dem Blog von UNIX auf eine Windows Plattform umgestiegen bin musste ich das an meinem eigenen Leib erfahren.

Das was einem am IIS wohl am meisten fehlt ist wohl die mod_rewrite funktion, die man über die .htaccess Datei konfiguriert. Diese wird dann dementsprechend unbrauchbar.

Microsoft bietet zwar auch sowas ähnliches an, aber mich überkommt immer ein ungutes Gefühl sobald ich mit mod_rewrite auseinandersetzen muss, und da hat man natürlich noch weniger Lust das ganze umzuschreiben.

Die beste Lösung wäre da natürlich, wenn man die alten Definitionen benutzen könnte. Und das geht auch tatsächlich!

Nach etwas googeln habe ich eine schöne, kostenlose Software gefunden, die und die dafür benötigte ISAPI Erweiterung für den IIS liefert! Diese findet ihr hier:

Wenn ihr die Installation durchlaufen habt, könnt ihr schnell mal checken ob der Dienst läuft in dem Ihr auf die Eigenschaften von "Websites" im IIS klickt und dann ISAPI Filters wählt. Gegebenenfalls den IIS neustarten.

modrew1Bild 1: Aktive ISAPI Filter

Nun könnt ihr eure Definitionen über die mitgelieferte (und wie ich finde etwas eingestaubte) Software erledigen:

modrew2Bild 2: Das Tool von Helicon zum verwalten der Rewrite-Regeln

Hier (Bild 2) sehr ihr eine Globale Einstellung, die sich auf alle Sites auswirkt. Es können aber auch einzelne Regeln erstellt werden, so wie man es auch gewohnt ist! Alternativ kann man auch in dem Webseiteneigenschaften (siehe Bild 1) Direkt den Tab "ISAPI_Rewrite" wählen.

Die Software ist kostenlos und erfüllt hervorragend ihren Zweck! Ich kanns nur weiterempfehlen!

17Sep/092

VB.NET: Bilder Hochladen mit der TwitPic API Teil 3

In dem dritten Teil der API-Reihe möchte ich euch zeigen wie man mit Hilfe der TwitPic API Bilder hochladen kann.

Diesesmal schreibt die API vor, dass man POST-Requests verwendet um der Seite Daten zu senden. Das macht die Sache etwas komplizierter, und dazu kommt noch, dass wir auch noch ein Bild verschicken müssen, also müssen auchnoch binäre Daten verschickt werden. Aber langsam, erstmal die Theorie!

Ihr kennt doch sicher die normalen Eingabemasken wie zB. die von imageshack.us. Diese besteht aus einem Dateiauswahlfeld, mehreren Checkboxen und einem Textfeld. Wer schonmal HTML gemacht hat wird wissen dass dieses Gebilde aus so genannten Interaktiven Elementen ein Formular darstellt welches nach dem Betätigen des Senden-Buttons an eine vorher definierte Adresse gesendet wird!

Dabei sendet der Browser einen HTTP POST-Request an den Server. Ein solcher Request besteht für gewöhnlich aus einem Kopf, den Daten und einem Fuß. So könnte zB. ein HTTP POST-Request aussehen:


POST /wiki/Spezial:Search HTTP/1.1
Host: de.wikipedia.org
Content-Type: application/x-www-form-urlencoded
Content-Length: 24
search=Katzen&go=Artikel

Der Server verwertet diese Daten dann und schickt eine Antwort. Je nachdem wie wir angefragt haben bekommen wir als Antwort eine XML-Struktur (Wie in den Letzten Beispielen) oder eine HTML Struktur. Die Kommunikation geschieht dabei auf dem HTTP Port 80 und läuft über das TCP Protokoll!

In den letzten beiden Teilen haben wir jeweils so genannte GET-Requests erstellt, bei denen nur der URL-Strang mit den Variablen versandt wurde.

Da wir aber nun ein Bild übertragen wollen, müssen wir versuchen ein POST-Request-Objekt zu erstellen, das wir dann an den TwitPic Server schicken können.

Unser POST-Objekt muss dabei folgende Daten übermitteln:

  • Unseren Twitter Benutzernamen
  • Unser Twitter Passwort
  • Das Bild das wir einstellen wollen

Wenn wir diese Daten nun in unser Objekt verfrachten, müssen wir dafür sorgen, dass der Server später weis, was was ist und die Daten von einander trennen kann. Deswegen müssen wir für jeden Eintrag einen eigenen Head und Body erzeugen.

Das Bild muss dann auch in das binäre Format umgewandelt werden und dann mit den anderen Daten durch ein Strom an den Server gesendet werden! - Hört sich kompliziert an, ich weis aber es hält sich in Grenzen!

Hier erstmal der Code für das Hochladen des Objekts mit allen Daten und der Abruf der Antwort. Tipp: Ihr könnt den Code besser lesen, wenn ihr auf den Knopf oben rechts in der Code Darstellung klickt!


    Public Function uploadPic(ByVal pic As Byte(), ByVal filename As String, ByVal user As String, ByVal pass As String)
        Dim encoding As String = "iso-8859-1"

        'Erzeugen einer einzigartigen identifikation
        Dim gui As String = Guid.NewGuid().ToString()

        'Diese wird für den Header und den footer der Daten benötigt
        Dim head As String = String.Format("--{0}", gui)
        Dim foot As String = String.Format("--{0}--", gui)

        'Einen Stringbuilder erstellen, in dem wir nun bequem die
        'benötigten POST Daten speichern können
        Dim contents As StringBuilder = New StringBuilder()

        'Benutzerdaten schreiben (benutzername)
        contents.AppendLine(head)
        contents.AppendLine(String.Format("Content-Disposition: form-data; name=""{0}""", "username"))
        contents.AppendLine()
        contents.AppendLine(user)

        'Benutzerdaten schreiben (passwort)
        contents.AppendLine(head)
        contents.AppendLine(String.Format("Content-Disposition: form-data; name=""{0}""", "password"))
        contents.AppendLine()
        contents.AppendLine(pass)

        'Header schreiben
        contents.AppendLine(head)
        'Bildinformationen schreiben, Bildkopf und die Binärdaten
        Dim fileHeader As String = String.Format("Content-Disposition: file; name=""{0}""; filename=""{1}""", "media", filename)
        Dim fileData As String = System.Text.Encoding.GetEncoding(encoding).GetString(pic)

        'Informationen zu dem Übergebenen Dateityp schreiben
        contents.AppendLine(fileHeader)
        contents.AppendLine(String.Format("Content-Type: {0}", "image/jpeg"))
        contents.AppendLine()
        contents.AppendLine(fileData)

        'Durch schreiben des footers signalisieren dass keine Daten mehr kommen
        contents.AppendLine(foot)

        MessageBox.Show(contents.ToString)

        'Stream Reader zum lesen der Antwort von Twitpic
        Dim reader As StreamReader
        Dim result As String
        Dim response As HttpWebResponse

        'Einen Webrequest zu der TwitPic API erstellen
        Dim request As HttpWebRequest = WebRequest.Create("http://twitpic.com/api/upload")

        'Die Auflagen die in der API beschreiben sind erfüllen:
        'API-Zitat:
        '---
        'Fields to post in:
        '(post data should be formatted as multipart/form-data)
        '---
        request.ContentType = String.Format("multipart/form-data; boundary={0}", gui)
        request.Method = "POST"

        'Die Daten die noch im Stringbuilder als String vorliegen
        'in das byte (Binär)-Format umwandeln, damit die API diese annimt
        Dim bytes As Byte() = System.Text.Encoding.GetEncoding(encoding).GetBytes(contents.ToString())
        request.ContentLength = bytes.Length

        'Einen Stream aus dem WebRequest erstellen
        Dim writer As Stream = request.GetRequestStream()

        'Die Binären Daten in den Strom schreiben
        writer.Write(bytes, 0, bytes.Length)
        response = request.GetResponse()

        'Antwort von Twitpic empfangen
        reader = New StreamReader(response.GetResponseStream)
        result = reader.ReadToEnd

        reader.Close()

        Return result
    End Function

bild1Wenn ihr diesen Code ausführt, wird ein POST-Objekt erzeugt und an den TwitPic Server gesendet. Dabei werden diese Informationen übermittelt. (Bild 1)

Man sieht ganz klar die Aufteilung. Der Kopf der aus der UID und den Meta Daten besteht und der Body, der nur den Wert beinhaltet.

Das Bild das ich hier sende hat einen etwas größeren Kopf, scheint dafür aber einen sehr kleinen Body zu haben. Ich kann euch beruhigen, das ist nur so, weil Windows, oder das MessageBox Objekt keine Binäre Darstellung von Zeichen beherrscht.

Eigentlich kommt dadrunter noch ein Footer (Fuß), aber dieser wird wegen der binären Daten abgeschnitten!

bild2Natürlich bekommt man auch eine Antwort, in diesem Fall eine im XML-Format. Diese beinhaltet entweder die Erfolgsmeldung oder eine Fehlermeldung mit einer Kurzen Beschreibung, woran es lag. An Bild 2 könnt ihr erkennen wie so ein Fehler aussehen könnte!

Wenn kein Fehler kommt steht dort dementsprechend die URL zum Bild statt der Fehlermeldung!

Wenn ihr nicht direkt den Code versteht ist das nicht schlimm. Man sollte ersteinmal wissen was der Browser da bei jedem Klick eigentlich hin- und herschickt. Dazu kann ich den Wikipedia-Artikel empfehlen. Es schadet auch nicht, weitere Beispiele anzuschauen bei denen mit HTTPWebRequest gearbeitet wird. ZB Hier oder Hier. (leider alles in C#)

Zum Schluss sei hier noch gesagt, dass die TwitPic API noch weitere Möglichkeiten bietet, wie zB. das Anfertigen von Vorschaubildern oder das Anhängen von Tweets an das Bild sodass automatisch auch euer Twitter Status mit dem neuen Bild aktualisiert wird!

bild3Bild 3: Das Demoprojekt in Aktion

Wie immer könnt ihr euch auch das Demoprojekt herunterladen: Download

Ich hoffe das Beispiel hat euch gefallen und freue mich auf euer Feedback!

18Apr/092

Vorsicht: Billiganbieter

Vor kurzer Zeit habe ich mir zu Testzwecken einen vServer gemietet, welcher mit Windows lief. Natürlich habe ich mich vorher erkundigt welche Anbieter wohl empfehlenswert sind und die die Preis/Leistung bei denen ist.

Zu meinem Erstaunen musste ich feststellen, dass es eine enorme Preisspanne zwischen den Ganzen Anbietern gibt, sogar mit Windows Server als Betriebssystem. Es gibt Anbieter die fangen schon bei 8 Eur an und welche die erst ab 20 Euro bereit sind einen vServer mit Windows ab zudrücken. (Bei ähnlicher Hardware versteht sich!)

http://www.united-hoster.com/windows-vserver.htmlDa ich in den Bereich erst einstieg entschied ich mich für einen günstigen Anbieter. Dieser Anbieter lockte vorallem damit, dass man monatlich kündigen konnte.

Nach der Bestellung dauerte es nur knapp 2 Tage bis ich meine Zugangsdaten und die Server IP hatte. Alles verlief zu meiner Überraschung problemlos und sauber. - Auch die Administrations Oberfläche von Parallels machte einen sehr guten Eindruck und der vServer war auch problemlos über die Windows Remote Verbindung erreichbar und bedienbar.

Die Anbindung war auch mehr als gut mit einem Downstream von knapp 12 MB/s und einem Upstream von 4 MB/s.

Nach mehreren Wochen habe ich die Konfiguration größtenteils fertig gestellt, sodass alle Dienste, FTP-Zugänge und Benutzerkonten stabil liefen - als dann plötzlich eine Überraschung kam.

Von einer Minute auf die Andere war der Server aus und ich konnte weder über die Parallels Administration als auch über die Windows Remote Desktop Verbindung einen Kontakt zum Server Herstellen.

Sofort schrieb ich eine Mail an den Support, der mir dann auch innerhalb von 2 Stunden!? antwortete, dass meine Rechnung nicht bezahlt sei. Ich dachte mir platzt der Kragen, denn ich war mir zu 100% sicher, dass ich bei der Registrierung die Bezahlung per Lastschriftverfahren eingestellt habe, was mir ein Blick in meine Unterlagen auch bestätigte.

Nach einer erneuten Mail in der ich die Lage Beschrieben hatte und den Zugriff auf den Server wieder einforderte habe ich umsonst 2 Tage gewartet, denn nichts kam. - Kurz darauf habe ich mein Abo wieder gekündigt und noch eine Beschwerde Mail vonmir gegeben - welche natürlich (welche Überraschung) auch unbeantwortet blieb!

Das Geld für diesen und auch für den nächsten Monat!! wurde dann 1 Monat später von meinem Konto gebucht, obwohl ich nur einen Monat lang drauf Zugriff hatte. Eine weitere Beschwerdemail blieb wieder unbeantwortet.

Nach diesem Langen Hin und Her habe ich mir versprochen beim nächsten Mal lieber 5 Euro mehr zu investieren, dafür dann aber vernünftigen Support zu erhalten. Aber dennoch hat sich das ganze gelohnt, denn nur aus Fehlern kann man ja bekanntlich lernen!

   
Get Adobe Flash playerPlugin by wpburn.com wordpress themes