TechBlog: File List erweitern

Für das Erstellen von Downloadseiten habe ich die Extension File List (dr_blob) gefunden. Mit dem hier vorgestellen Skript kann ein Verzeichnis automatisch (rekursiv) durchsucht werden, sodass die Ordner und Dateien nicht mehr manuell in Typo3 hinzugefügt werden müssen. Außerdem wird die Extension für große Dateien fit gemacht.

Dateisystem automatisch einlesen

Zunächst einmal unterstützt die Extension sowohl das Speichern der Dateien in der Datenbank als auch ganz normal im Dateisystem, wobei ich nur letzteres verwende. Oft lade ich Dateien in ein Verzeichnis und möchte, dass diese Dateien auf einer bestimmten Seite als Downloadlinks erscheinen. Die Extension organisiert die Ordner, die zum Download stehen, in Seiten vom Typ Systemordner im Seitenbaum von Typo3, während die Dateien selbst spezielle Records sind. Diese müssen normalerweise im Backend erstellt werden; dies kann man jedoch auch automatisieren. Das erledigt das folgende php-Skript, das bei mir als "FileListAutoUpdate.php" im fileadmin-Verzeichnis liegt:

  1. <?php
  2. define("MAGIC", "/etc/magic.mime");
  3.  
  4. class user_FileListAutoUpdate {
  5.   function update($content, $conf) {
  6.     if (substr($conf['dir'], strlen($conf['dir'])-1) === '/') {
  7.       $conf['dir'] = substr($conf['dir'], 0, strlen($conf['dir'])-1);
  8.     }
  9.     return $this->updateFolder($conf['dir'], $conf['siteUID'], 1);
  10.   }
  11.   private function updateFolder($dir, $siteUID, $recursive) {
  12.     $errors = "";
  13.     $filerecords = $GLOBALS['TYPO3_DB']->exec_SELECTquery (
  14.       'uid,blob_data,type',
  15.       'tx_drblob_content',
  16.       'pid=' . $siteUID . ' AND deleted=0',
  17.       '', '', '');
  18.     $includedfiles = array();
  19.     while($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($filerecords)) {
  20.       if ($row['type'] == 2) {
  21.         $includedfiles[$row['uid']] = $row['blob_data'];
  22.       }
  23.     }
  24.  
  25.     $sites = $GLOBALS['TYPO3_DB']->exec_SELECTquery (
  26.       'uid,title',
  27.       'pages',
  28.       'pid=' . $siteUID . ' AND deleted=0',
  29.       '', '', '');
  30.     $includedfolders = array();
  31.     while($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($sites)) {
  32.       $includedfolders[$row['uid']] = $row['title'];
  33.     }
  34.     $handle = opendir($dir);
  35.     while (false !== ($filename = readdir($handle))) {
  36.       if (substr($filename, 0, 1) != '.') {
  37.         $filepath = $dir.'/'.$filename;
  38.         if (is_file($filepath)) {
  39.           $blobpath = substr($filepath, strlen('fileadmin/'));
  40.           if (!in_array($blobpath, $includedfiles)) {
  41.             $finfo = finfo_open(FILEINFO_MIME);
  42.             $filetype = finfo_file($finfo, $filepath);
  43.             finfo_close($finfo);
  44.             $filesize = filesize($filepath);
  45.             $filechecksum = md5_file($filepath);
  46.             $GLOBALS['TYPO3_DB']->exec_INSERTquery('tx_drblob_content', array(
  47.               'pid' => $siteUID,
  48.               'tstamp' => time(),
  49.               'crdate' => time(),
  50.               'title' => $filename,
  51.               'type' => 2,
  52.               'blob_name' => $filename,
  53.               'blob_size' => $filesize,
  54.               'blob_data' => $blobpath,
  55.               'blob_type' => $filetype,
  56.               'blob_checksum' => $filechecksum,
  57.             ));
  58.           }
  59.         }
  60.         else {
  61.           $uid = array_search($filename, $includedfolders);
  62.           if ($uid === false) {
  63.             $GLOBALS['TYPO3_DB']->exec_INSERTquery('pages', array(
  64.               'pid' => $siteUID,
  65.               'tstamp' => time(),
  66.               'crdate' => time(),
  67.               'title' => $filename,
  68.               'doktype' => 254
  69.             ));
  70.             $uid = $GLOBALS['TYPO3_DB']->sql_insert_id();
  71.           }
  72.           if ($recursive) {
  73.             $this->updateFolder($filepath, $uid, 1);
  74.           }
  75.         }
  76.       }
  77.     }
  78.     closedir($handle);
  79.   }
  80. }
  81. ?>

Bindet man es über ein USER-Objekt wie folgt in die Seite ein, so wird das angegebene Verzeichnis nach jedem Lösen des FE-Caches eingelesen. Ignoriert werden dabei alle Dateien, die mit "." anfangen. Diese Abfrage sollte man nicht ohne bedacht herausnehmen, zumindest wenn Typo3 unter Linux läuft: Auf jeden Fall müssen nämlich die Dateien "." und ".." abgefangen werden, denn diese stehen für das aktuelle bzw. das übergeordnete Verzeichnis. Hier besteht also die Gefahr einer Unendlichschleife.

  1. page.1000 = USER
  2. page.1000 {
  3.   userFunc = user_FileListAutoUpdate->update
  4.   dir = fileadmin/storage/
  5.   siteUID = 26
  6. }

Die "siteUID" gibt an, in welcher Seite die Daten gespeichert werden sollen (selbe Einstellung wie in der Extension). Das Skript erstellt für neue gefundene Ordner einen Systemordner sowie für Dateien jeweils einen Record. Spezielle Einstellungen können nachträglich im Backend vorgenommen werden, so als hätte man die jeweilige Datei wie eigentlich vorgesehen vorher dort hochgeladen.

Große Dateien zum Download stellen

Beim Hoch- und Herunterladen kann es bei der Verwendung der Extension zu dem Fehler

Fatal error: Allowed memory size of XXXXXXXX bytes exhausted (tried to allocate YYYYYYYY bytes) in ...

kommen. Dies liegt daran, dass dr_blob an zwei Stellen die komplette Datei in den Arbeitsspeicher holt. Das ist ungünstig, da der verfügbare Speicher oft kleiner ist als die Datei. Da ich die Dateien nicht über das Backend hochlade sondern per FTP und anschließend das Skript oben ausführe, tritt der Fehler bei mir natürlich nur beim Download auf. Auch nur hierfür bietet die folgende Änderung Abhilfe (sorry, siehe aber weiter unten).

Es sind zwei Änderungen nötig, die allerdings nur helfen, wenn die Datei auf dem Filesystem (und nicht in der Datenbank) gespeichert wird. In der Datei "typo3conf/ext/dr_blob/class.tx_drblob_tcemain.php" in Zeile 70:

  1. $filePointer = fopen( $fileName, 'r' );
  2. $data = fread( $filePointer, filesize( $fileName ) );
  3. $md5_checksum = md5( $data );
  4. #$data = addslashes( $data );
  5. fclose( $filePointer );

Und in Zeile 93 wird das "if" um ein "else" erweitert:

  1. else {
  2.   $data = addslashes( $data );
  3. }

Der addslashes()-Aufruf ist nur für das Speichern in der DB nötig, weshalb er jetzt auch nur dort (im else-Block) ausgeführt wird. Andernfalls hätten im Skript oben die Dateien ebenso manipuliert werden müssen, was aber keinen Vorteil bringt.

Einschub: Um den Upload von großten Dateien zu ermöglichen, müsste man einen Weg finden, auf das "fread" in Zeile 69 sowie auf "t3lib_div::writeFile" weiter unten in der Datei zu verzichten und stattdessen die Datei einfach zu verschieben. Außerdem muss dann in Zeile 70 der Aufruf "$md5_checksum = md5( $data );" in "$md5_checksum = md5_file($fileName);" geändert werden, da "$data" dann ja nicht mehr zu Verfügung steht. Das nur als Idee, ich selbst habe es nicht umgesetzt, da ich es wie oben bereits gesagt nicht benötige.

Während die vorige Änderung den Dateiupload betraf, geht es jetzt um den Download. In der Datei "typo3conf/ext/dr_blob/pi1/class.tx_drblob_pi1.php" in Zeile 731 müssen folgende Änderungen vorgenommen werden:

  1. #echo $blob['blob_data'];
  2. //Load Data
  3. if( $this->getFieldContent( 'type' ) == 1 ) {
  4.   $blob['blob_data'] = $this->getFieldContent( 'blob_data' );
  5.   $blob['blob_data'] = stripslashes( $blob['blob_data'] );
  6.   echo $blob['blob_data'];
  7. } else {
  8.   $extConf = unserialize( $GLOBALS['TYPO3_CONF_VARS']['EXT']['extConf']['dr_blob'] );
  9.   $file = t3lib_div::dirname( $extConf['fileStorageFolder'] ) . '/' . $this->getFieldContent( 'blob_data' );
  10.   $fp = fopen( $file, 'r' );
  11.   while (!feof($fp)) {
  12.     echo fread($fp, 8192);
  13.   }
  14.   fclose( $fp );
  15. }

Die Datei wird beim Download dann schrittweise eingelesen und an den Browser gesendet.

  •  
  • Trackbacks
  •  
Casino 1250227750
16.08.2009
Casino 1250227750

Casino 1250227750...

Casino 1250517724
18.08.2009
Casino 1250517724

Casino 1250517724...

  •  
  • Kommentar schreiben
  •  
Ich möchte über jeden weiteren Kommentar in diesem Post benachrichtigt werden.