Compress on the fly a directory in tar.gz format u

2019-04-29 21:54发布

问题:

I want to compress (on the fly) a directory in tar.gz format using PHP. I know it can be done with exec or using this code (using ZIP format) but my host doesn't support exec and hasn't installed the zip extension of PHP.

After searching the internet I came across this PHP code:

<?php
// Computes the unsigned Checksum of a file’s header
// to try to ensure valid file
// PRIVATE ACCESS FUNCTION
function __computeUnsignedChecksum($bytestring) {
  for($i=0; $i<512; $i++)
    $unsigned_chksum += ord($bytestring[$i]);
  for($i=0; $i<8; $i++)
    $unsigned_chksum -= ord($bytestring[148 + $i]);
  $unsigned_chksum += ord(" ") * 8;

  return $unsigned_chksum;
}

// Generates a TAR file from the processed data
// PRIVATE ACCESS FUNCTION
function tarSection($Name, $Data, $information=NULL) {
  // Generate the TAR header for this file

  $header .= str_pad($Name,100,chr(0));
  $header .= str_pad("777",7,"0",STR_PAD_LEFT) . chr(0);
  $header .= str_pad(decoct($information["user_id"]),7,"0",STR_PAD_LEFT) . chr(0);
  $header .= str_pad(decoct($information["group_id"]),7,"0",STR_PAD_LEFT) . chr(0);
  $header .= str_pad(decoct(strlen($Data)),11,"0",STR_PAD_LEFT) . chr(0);
  $header .= str_pad(decoct(time(0)),11,"0",STR_PAD_LEFT) . chr(0);
  $header .= str_repeat(" ",8);
  $header .= "0";
  $header .= str_repeat(chr(0),100);
  $header .= str_pad("ustar",6,chr(32));
  $header .= chr(32) . chr(0);
  $header .= str_pad($information["user_name"],32,chr(0));
  $header .= str_pad($information["group_name"],32,chr(0));
  $header .= str_repeat(chr(0),8);
  $header .= str_repeat(chr(0),8);
  $header .= str_repeat(chr(0),155);
  $header .= str_repeat(chr(0),12);

  // Compute header checksum
  $checksum = str_pad(decoct(__computeUnsignedChecksum($header)),6,"0",STR_PAD_LEFT);
  for($i=0; $i<6; $i++) {
    $header[(148 + $i)] = substr($checksum,$i,1);
  }
  $header[154] = chr(0);
  $header[155] = chr(32);

  // Pad file contents to byte count divisible by 512
  $file_contents = str_pad($Data,(ceil(strlen($Data) / 512) * 512),chr(0));

  // Add new tar formatted data to tar file contents
  $tar_file = $header . $file_contents;

  return $tar_file;
}

function targz($Name, $Data) {
  return gzencode(tarSection($Name,$Data),9);
}

header("Content-Disposition: attachment; filename=backup.tar.gz");
header("Content-type: application/x-gzip");

$getfile = file_get_contents("test.txt");

echo targz('test.txt', $getfile);
?>

This does work for multiple files using this:

header("Content-Disposition: attachment; filename=backup.tar.gz");
header("Content-type: application/x-gzip");

$getfile = file_get_contents("test.txt");

echo targz('test.txt', $getfile);

$getfile2 = file_get_contents("test2.txt");

echo targz('test2.txt', $getfile2);

In the backup.tar.gz are now 2 files (test.txt and test2.txt).

But how can I use this to download a directory (and the sub directories)?

My directory is something like this:

home/
    file1.html
    file2.html
Another_Dir/
    file8.html
    Sub_Dir/
        file19.html

回答1:

You need too list all files and directory inside the directory you want to compress. There's a recursive directory listing function here. I think combining your code with his code with do the work. something like this:

header("Content-Disposition: attachment; filename=backup.tar.gz");
header("Content-type: application/x-gzip");

// Your other tar.gz functions...

function compress( $path = '.', $level = 0 ){ 
    $ignore = array( 'cgi-bin', '.', '..' ); 
    $dh = @opendir( $path );

    while( false !== ( $file = readdir( $dh ) ) ){                                   
        if( !in_array( $file, $ignore ) ){
            if( is_dir( "$path/$file" ) ){
                // Go to the subdirs to compress files inside the subdirs
                compress( "$path/$file", ($level+1) );                                           
            } else {
                $getfile = file_get_contents($path. '/' .$file);
                // get the last dir in the $path
                $dirs = array_filter( explode('/', $path) );
                $dir = array_pop($dirs);

                // if we're not in a sub dir just compress the file in root otherwise add it on the subdir...
                if ($level < 1) {
                    echo targz($file, $getfile);
                } else {
                    // add all top level subdirs when $level is > 2
                    for ($i = 0; $i < $level - 1; $i++) {
                        $temp = array_pop($dirs);
                        $dir = $temp.'/'.$dir;
                    }
                    echo targz($dir. '/' .$file, $getfile);
                }
            }                                        
        }                                    
    } 

    closedir( $dh ); 
}

$dir    = 'dir/to/compress';
if (is_file($dir)) {
    // If it's a file path just compress it & push it to output
    $getfile = file_get_contents($dir);
    echo targz(basename($dir), $getfile);
} elseif(is_dir($dir)) {
    return compress($dir);
}

It works fine for me. It's my first contribution, hope it helps..