Symfony: Bilder upload und Bearbeitung

Wie versprochen möchte ich über meine weiteren Erfahrungen mit Symfony berichten. Heute soll es darum gehen, wie man Bilder von der lokalen Festplatte auf die Webseite lädt, einen enstsprechenden Eintrag in die Datenbank erzeugt und die Datei auch noch in der Größe ändert und umbenennt.

Beginnen wir mit dem eigentlichen Hochladen der Datei. Auch dort nimmt uns Symfony wieder eine Menge Arbeit ab. Durch das Fomular-Framework ist das einfügen eines entsprechenden Formularfeldes denkbar einfach:
[sourcecode language=’php‘]
$this->setWidget(‚large‘, new sfWidgetFormInputFile());
[/sourcecode]

Aus diesem Code innerhalb der configure-methode des sfForm’s wird dann im Formular automatisch das gewünschte Feld eingefügt. Damit das versenden funktionier muss die Form-Mathod natürlich auf „POST“ stehen. Das Formular selber muss Multipart sein:

[sourcecode language=’php‘]
isMultipart() and print ‚enctype=“multipart/form-data“ ‚ ?>>
[/sourcecode]

Und in der Verarbeitung des Fomulars muss sichergestellt sein, dass auch die Files an die bind()-Methode des Formulars übergeben werden:
[sourcecode language=’php‘]
$form->bind(
$request->getParameter($form->getName()),
$request->getFiles($form->getName())
);
[/sourcecode]

Viel Arbeit nimmt uns dann der Validator ab:

[sourcecode language=’php‘]
$this->validatorSchema[‚large‘] = new sfValidatorFile(array(
‚required‘ => true,
‚path‘ => sfConfig::get(‚app_fz_img_upload_dir‘),
‚mime_types‘ => ‚web_images‘,
));
[/sourcecode]

Das Beispiel zeigt, was der Validator alles mit dem hochgeladenen Bild tut. Nach dem Hochladen wird die Datei automatisch in den Ordner verschoben der unter ‚path‘ angegeben wird (Ich habe diese Option konfigurierbar gehalten, siehe app.yml). Gleichzeitig prüft der Validator noch, ob es sich bei der Datei um ein Bild handelt. Die dabei unterstützten Mime-Typ’s sind:

  • image/jpeg
  • image/pjpeg
  • image/png
  • image/x-png
  • image/gif

Anschließend liegt dann eine Datei mit einem per sha1 erzeugten Namen in dem Pfad, den wir dem Validator gegeben haben. Gleichzeitig wird der Name der Datei aber auch in der neuen Instanz der Klasse übernommen (in meinem Fall im Member ‚large‘).

Da ich persönlich aber nun gerne auch schon am Dateinamen erkennen möchte was im Bild zu finden ist habe ich die Funktionalität noch etwas erweitert. Es gibt die Möglichkeit, die save() Methode der Model-Klasse zu überschreiben (Natürlich nicht in der BaseKlasse!) und dort zusätzliche Dinge zu tun.

Das habe ich getan und innerhalb der Methode nicht nur die Datei umbenannt sondern ebenfalls in der Größe angepasst und ein Thumpnail für die Vorschau erzeugt.
[sourcecode language=’php‘]
public function save(Doctrine_Connection $conn = null){

$this[’small‘] = ‚unset‘;
$ret = parent::save($conn);

$max_height = sfConfig::get(‚app_fz_img_max_height‘);
$max_width = sfConfig::get(‚app_fz_img_max_width‘);

$max_thump_height = sfConfig::get(‚app_fz_img_thump_max_height‘);
$max_thump_width = sfConfig::get(‚app_fz_img_thump_max_width‘);

$thumnailDir = sfConfig::get(‚app_fz_img_upload_dir_thumpnails‘);
$uploadDir = sfConfig::get(‚app_fz_img_upload_dir‘);

$newFileName = $this->generateNewFileName();
$newFullSizeFile = $uploadDir.’/‘.$newFileName;
$newThumpFile = $thumnailDir.’/‘.$newFileName;

//Do the FileHandling Stuff
//Rename/resize the Original file

$originalFile = $uploadDir.’/‘.$this[‚large‘];
sfContext::getInstance()->getLogger()->log(‚Processing File: ‚. $originalFile);

//Create Full-Size file
$this->modifyFile($max_width, $max_height, $originalFile, $newFullSizeFile);

//Create a thumpnail of the original file
$this->modifyFile($max_thump_width, $max_thump_height, $originalFile, $newThumpFile);

//Store the new Filenames
$this[‚large‘] = $newFileName;
$this[’small‘] = $newFileName;

$ret = parent::save($conn);

//Delete the original file
unlink($originalFile);

return $ret;
}
[/sourcecode]
Zunächst einmal hole ich mir ein paar konfigurierbare Parameter aus der app.yml. Anschließend baue ich mir den kompletten Pfad zu der neu hochgeladenen Datei zusammen und speichere diesen in $originalFile. Die Methode modifyFile(…)

[sourcecode language=’php‘]
private function modifyFile($width, $height, $original, $newFile){
sfContext::getInstance()->getLogger()->log(‚Creating File…‘);
$thumpMaker = new sfThumbnail($width, $height, true, true, 100);
$thumpMaker->loadFile($original);
$thumpMaker->save($newFile);
sfContext::getInstance()->getLogger()->log(‚ -> New File: ‚. $newFile);
}
[/sourcecode]
Nutzt das sfThumpnailPlugin um die Bildgröße entsprechend der Übergabewerte zu ändern. Gleichzeitig wird die neue Datei auch gespeichert.

Nach dem Aufruf der modify-Methode muss der neue Dateiname (ohne Pfad) in den Membern übernommen werden. Damit man in der save-Methode auch schon auf die id (Primary-Key) zugreifen kann muss vor dem Bearbeiten der Dateien die Instanz schon einmal in der Datenbank gespeichert worden sein. Damit das klappt müssen alle „not null“ Felder einen Wert haben. Aus diesem Grund wird „small“ mit ‚unset‘ vorbelegt und anschließend „parent::save($conn);“ aufgerufen.

Der selbe Aufruf erfolgt am Schluss der Methode noch einmal um die Änderungen zu übernehmen. Mit dem abschließenden unlink wird die Original-Datei gelöscht.

Ich hoffe diese kleine Anleitung hilft dem einen oder der anderen weiter. Fragen und Anmerkungen bitte in den Kommentaren.

4 Kommentare zu “Symfony: Bilder upload und Bearbeitung”

  1. Jenny

    Hallo Kai,

    dieser Beitrag ist ja wirklich spitze! Dafür möchte ich mich natürlich ganz herzlich bedanken. Ich kann tatsächlich alles, was darin steht auch in meinem Projektzusammenhang nutzen. Besonders auch die automatische Größenanpassung hilft mir sehr!

    Vielen Dank und liebe Grüße!
    Jenny

  2. David

    Hey,
    danke für den ausführlichen Artikel!

    Ich hab noch kurze Anmerkungen:

    Thumbnail schreibt man mit „b“ 😉
    Die Methode „generateNewFilename“ fehlt leider 😉 In meinem Fall benutze ich gleichzeitig das Plugin „sfPropelActAsSluggableBehavior“ und nehme einfach den generierten Slug als Dateinamen.

  3. kai

    Darfst den Rechtschreibfehler behalten!

    Es ist Absicht, dass die Methode „generateNewFilename“ nicht mit aufgeführt ist, ich denke einen eindeutigen String zu Erzeugen sollte jeder Entwickler auch ohne einen Vorschlag hinbekommen. Das von Dir genannte Plugin kann ich nicht nutzen, da ich Doctrine als OR-Mapper einsetze. Selbst wenn ich es könnte, ich finde die Idee mit der Methode besser, weil flexibler.

  4. bilder hochladen

    Super. Vielen Dank für die Info. Werds gleich mal versuchen.