Dieses Mal moechte ich mich etwas um das entpacken verschiedener Archive kuemmern.
Und zwar um folgende:- .tar
- .gz
- .bz2
- .tar.gz
- .tgz
- .tar.bz2
Funktionen fuer bZip2- und gZip-Kompression/Dekompression sind ja bereits in PHP enthalten.
Jedoch muss man fuer Tar-Archive entweder auf Shell-Kommandos zugreifen oder eine PEAR-Extension installieren.
Um diesem aus dem Weg zu gehen hab ich mich entschieden ein Script zu schreiben welches diese Archive entpacken kann.
Nachdem das Script zum Entpacken der Tar-Archive fertig war war es ein Leichtes die Dekompression von bZip2 oder gZip hinzuzufuegen, daher fange ich auch bei diesem Tutorial mit der Funktion zum entpacken der Tar-Archive an.
PHP-Code:
function untar($tarfile)
{
$tarfile=fopen($tarfile,"r");
$datainfo="";
$data="";
while (!feof($tarfile))
{
$readdata=fread($tarfile,512);
if (substr($readdata,257,5)=="ustar")
{
if (!empty($datainfo))
{
$poscount=0;
$name="";
while (substr($datainfo,$poscount,1)!=chr(0))
{
$name.=substr($datainfo,$poscount,1);
$poscount++;
}
if (!empty($name))
{
if (substr($name,-1)=="/")
{
echo "Creating ".$name."<br>";
mkdir($name);
}
else
{
echo "Extracting ".$name."<br>";
$datasize=strlen($data)-1;
while ((substr($data,$datasize,1)==chr(0)) && ($datasize>-1))
{
$datasize--;
}
$datasize++;
$filedata="";
for ($datacount=0;$datacount<$datasize;$datacount++)
{
$filedata.=substr($data,$datacount,1);
}
$file=fopen($name,"w");
fwrite($file,$filedata);
fclose($file);
}
}
$datainfo=$readdata;
$data="";
}
else
{
$datainfo=$readdata;
}
}
else
{
$data.=$readdata;
}
}
if (!empty($datainfo))
{
$poscount=0;
$name="";
while (substr($datainfo,$poscount,1)!=chr(0))
{
$name.=substr($datainfo,$poscount,1);
$poscount++;
}
if (!empty($name))
{
if (substr($name,-1)=="/")
{
echo "Creating ".$name."<br>";
mkdir($name);
}
else
{
echo "Extracting ".$name."<br>";
$datasize=strlen($data)-1;
while ((substr($data,$datasize,1)==chr(0)) && ($datasize>-1))
{
$datasize--;
}
$datasize++;
$filedata="";
for ($datacount=0;$datacount<$datasize;$datacount++)
{
$filedata.=substr($data,$datacount,1);
}
$file=fopen($name,"w");
fwrite($file,$filedata);
fclose($file);
}
}
$datainfo=$readdata;
$data="";
}
fclose($tarfile);
}
Zum besseren Verstaendnis der Funktion ein wenig zur Theorie der Tar-Archive:
Ein Tar-Archiv ist immer x * 512 Byte gross, egal wie gross die enthaltenen Daten sind.
Daher kann man das Archiv in 512-Byte-Bloecken auslesen und diese auswerten.
Vor den jeweiligen Daten wird in Block mit Informationen gespeichert.
Diese enthalten unter anderem den Datei-/Verzeichnisnamen, die Rechte, User- und GroupID.
Sowohl die Datei-Rechte als auch User- und GroupID werden von diesem Script nicht wiederhergestellt.
Den Info-Block kann man ziemlich einfach identifizieren, er enthaelt ab Byte 258 (im Script steht 257, da ja bei substr() bei 0 mit dem Zaehlen begonnen wird) den String ustar.
Am Anfang dieses Blocks steht der Datei-/Verzeichnisname, die restlichen Informationen sind fuer dieses Script nicht notwendig.
Wie gesagt: Rechte, UserID und GroupID werden hier nicht wiederhergestellt.
Weiterhin folgen nach einem Info-Block fuer ein Verzeichnis keine Daten, sondern gleich der Info-Block fuer den naechsten Eintrag.
Das Script liest also das Tar-Archiv in 512-Byte-Bloecken ein. Wird ein Block als Info-Block identifiziert wird mit der Auswertung angefangen.
Der erste Info-Block wird nicht gleich verarbeitet, da ja bei Dateien die Daten erst folgen.
Beim naechsten Info-Block wird dann gehandelt:
Wurde vom letzten Info-Block ein Verzeichnis beschrieben wird dieses angelegt.
Ansonsten werden die gelesenen Daten in die im Info-Block angegebene Datei geschrieben.
Da ein Tar-Archiv immer x * 512 Byte gross ist kann man sich leicht vorstellen, dass Daten die nicht exakt x * 512 Byte gross sind aufgefuellt werden. Dies geschieht mit Zeichen deren Wert 0 ist (also chr(0)).
Vom Ende des Datenblockes wird solange rueckwaerts gezaehlt bis nicht mehr chr(0) gefunden wird. So erhaelt man die eigentliche groesse der Daten.
Mit dieser Information kann man nun die Daten in die Datei schreiben.
Da am Ende aller Daten kein weiterer Info-Block steht endet die WHILE-Schleife aufgrund eof().
Daher muss nach der Schleife noch der letzte Block ausgewertet werden.
Dies geschieht auf die gleiche Art und Weise wie in der Schleife.
Nun soll das ganze auch Tar-Archive entpacken koennen welche sich in gZip- oder bZip2-Archive befinden.
In der Regel wird dazu an die Endung .tar noch die Endung .gz bzw. .bz2 angehaengt.
Anstelle von .tar.gz findet man aber auch schonmal .tgz als Endung.
Da der Dateiname des Tar-Archives mehrfach benoetigt wird hab ich die folgende Funktion geschrieben:
PHP-Code:
function getuncompressedfilename($compressedfilename)
{
$compressedfilenameparts=explode(".",$compressedfilename);
for ($count=0;$count<count($compressedfilenameparts)-1;$count++)
{
$uncompressedfilenameparts[]=$compressedfilenameparts[$count];
}
if ($compressedfilenameparts[count($compressedfilenameparts)-1]=="tgz")
{
$uncompressedfilenameparts[]="tar";
}
$uncompressedfilename=implode(".",$uncompressedfilenameparts);
return $uncompressedfilename;
}
Dabei wird der Dateiname lediglich an den Punkten getrennt und ohne die letzte Endung wieder zusammengesetzt.
Es wird aber auch ueberprueft ob die letzte Endung tgz war. In diesem Fall wird tar angehaengt.
Da die Funktion getuncompressedfilename() es jetzt einfach macht an den "entpackten" Dateinamen zu kommen koennen nun die Funktionen zum entpacken von gZip- und bZip2-Archiven hinzugefuegt werden.
PHP-Code:
function unbzip2($bz2file)
{
$content="";
$file=bzopen($bz2file,"r");
while (!feof($file))
{
$content.=bzread($file,1);
}
bzclose($file);
$file=fopen(getuncompressedfilename($bz2file),"w");
fwrite($file,$content);
fclose($file);
}
PHP-Code:
function ungzip($gzfile)
{
$content="";
$file=gzopen($gzfile,"r");
while (!gzeof($file))
{
$content.=gzread($file,1);
}
gzclose($file);
$file=fopen(getuncompressedfilename($gzfile),"w");
fwrite($file,$content);
fclose($file);
}
Da die beiden Funktionen nahezu identisch sind beschraenke ich mich darauf die es einmal allgemein zu erklaeren.
Die Daten werden Byte-fuer-Byte ausgelesen und dabei dekomprimiert.
Anschliessend wird der "entpackte" Dateiname mittels getuncompressedfilename() geholt und die Datei mit diesem Namen erstellt.
Dort werden die entpackten Datein hineingeschrieben. Fertig.
Zu erkennen um welchen Archiv-Typ (gZip, bZip2 oder Tar) es sich denn nun handelt ist Aufgabe der folgenden Funktion:
PHP-Code:
function getarchivetype($filename)
{
$archivetype="unknown";
$filenameparts=explode(".",$filename);
if (($filenameparts[count($filenameparts)-1]=="gz") || ($filenameparts[count($filenameparts)-1]=="tgz"))
{
$archivetype="gzip";
}
if ($filenameparts[count($filenameparts)-1]=="bz2")
{
$archivetype="bzip2";
}
if ($filenameparts[count($filenameparts)-1]=="tar")
{
$archivetype="tar";
}
return $archivetype;
}
Hierbei wird aehnlich vorgegangen wie in der Funktion getuncompressedfilename().
Der Dateiname wird an den Punkten getrennt und anschliessend wird die letzte Endung untersucht.
Bei gz oder tgz handelt sich um ein gZip-Archiv.
Bei bz2 um ein bZip2-Archiv.
Und bei tar um ein Tar-Archiv.
Falls die Endung keine der 4 angegebenen Endungen ist wird unknown zurueckgegeben.
Nun sind alle Funktionen soweit fertig, nun muss noch der eigentlich Hauptteil des Scripts her, welches den Dateinamen entgegennimmt und dann an die entsprechenden Funktionen weitergibt.
PHP-Code:
if (isset($_GET['file']))
{
$archive=$_GET['file'];
$type=getarchivetype($archive);
if ($type!="unknown")
{
if ($type!="tar")
{
echo "Decompressing ".$archive."<br>";
if ($type=="gzip")
{
ungzip($archive);
}
if ($type=="bzip2")
{
unbzip2($archive);
}
$uncompressedname=getuncompressedfilename($archive);
if (getarchivetype($uncompressedname)=="tar")
{
untar($uncompressedname);
unlink($uncompressedname);
}
}
else
{
untar($archive);
}
}
}
Wenn der Parameter file im URL uebergeben wird springt das Script an. Ansonsten macht es rein garnichts.
Der Archiv-Typ wird mittels getarchivetype() bestimmt.
Wenn der Typ erfolgreich erkannt wurde geht's weiter.
Handelt es sich nicht um ein Tar-Archiv wird es entweder mittels ungzip() oder unbzip2() entpacket, je nach Typ.
Mittels getuncompressedfilename() wird ueberprueft ob sich in dem gZip- oder bZip2-Archiv ein Tar-Archiv befand. Ist dies der Fall wird dieses entpackt und geloescht.
Handelt es sich bei dem im URL angegebenen Archiv um ein Tar-Archiv wird dieses entpackt.
Es findet keine Pruefung auf Existenz der Datei statt. Diese zu implementieren sollte aber kein Problem darstellen.
Viel Spass und Viel Erfolg
reptiler
Lesezeichen