MDL-15920 initial zipping support

This commit is contained in:
skodak 2008-08-03 21:37:50 +00:00
parent f6b923eaba
commit b1897a6d68
3 changed files with 167 additions and 22 deletions

View File

@ -1,22 +1,140 @@
<?php //$Id$
require_once("$CFG->libdir/file/zip_archive.php");
/**
* Utility class - handles all zipping and unzipping operations.
*/
class file_packer {
public function zip_files_to_pathname($files, $zipfile) {
error('todo');
/**
* Zip files and store the result in file storage
* @param array $archivepath=>$pathanme or stored file instance
* @param int $contextid
* @param string $filearea
* @param int $itemid
* @param string $filepath
* @param string $filename
* @return mixed false if error stored file instance if ok
*/
public function zip_files_to_storage($files, $contextid, $filearea, $itemid, $filepath, $filename, $userid=null) {
global $CFG;
$fs = get_file_storage();
check_dir_exists($CFG->dataroot.'/temp/zip', true, true);
$tmpfile = tempnam($CFG->dataroot.'/temp/zip', 'zipstor');
if ($result = $this->zip_files_to_pathname($files, $tmpfile)) {
if ($file = $fs->get_file($contextid, $filearea, $itemid, $filepath, $filename)) {
if (!$file->delete()) {
@unlink($tmpfile);
return false;
}
}
$file_record = new object();
$file_record->contextid = $contextid;
$file_record->filearea = $filearea;
$file_record->itemid = $itemid;
$file_record->filepath = $filepath;
$file_record->filename = $filename;
$file_record->userid = $userid;
$result = $fs->create_file_from_pathname($file_record, $tmpfile);
}
@unlink($tmpfile);
return $result;
}
public function zip_files_to_storage($files, $contextid, $filearea, $itemid, $filepath, $filename) {
error('todo');
/**
* Zip files and store the result in os file
* @param array $archivepath=>$pathanme or stored file instance
* @param string $zipfile
* @return bool success
*/
public function zip_files_to_pathname($files, $zipfile) {
if (!is_array($files)) {
return false;
}
$ziparch = new zip_archive();
if (!$ziparch->open($zipfile, ZIPARCHIVE::OVERWRITE)) {
return false;
}
foreach ($files as $archivepath => $file) {
$archivepath = trim($archivepath, '/');
if (is_null($file)) {
// empty directories have null as content
$ziparch->addEmptyDir($archivepath.'/');
} else if (is_string($file)) {
$this->add_os_file_to_zip($ziparch, $archivepath, $file);
} else {
$this->add_stored_file_to_zip($ziparch, $archivepath, $file);
}
}
return $ziparch->close();
}
protected function add_stored_file_to_zip($ziparch, $archivepath, $file) {
$file->add_to_ziparchive($ziparch, $archivepath);
if (!$file->is_directory()) {
return;
}
$baselength = strlen($file->get_filepath());
$fs = get_file_storage();
$files = $fs->get_directory_files($file->get_contextid(), $file->get_filearea(), $file->get_itemid(),
$file->get_filepath(), true, true);
foreach ($files as $file) {
$path = $file->get_filepath();
$path = substr($path, $baselength);
$path = $archivepath.'/'.$path;
if (!$file->is_directory()) {
$path = $path.$file->get_filename();
}
$file->add_to_ziparchive($ziparch, $path);
}
}
protected function add_os_file_to_zip( $ziparch, $archivepath, $file) {
if (!file_exists($file)) {
return;
}
if (is_file($file)) {
if (!is_readable($file)) {
return;
}
$ziparch->addFile($file, $archivepath);
return;
}
if (is_dir($file)) {
if ($archivepath !== '') {
$archivepath = $archivepath.'/';
$ziparch->addEmptyDir($archivepath);
}
$files = new DirectoryIterator($file);
foreach ($files as $file) {
if ($file->isDot()) {
continue;
}
$newpath = $archivepath.$file->getFilename();
$this->add_os_file_to_zip($ziparch, $newpath, $file->getPathname());
}
unset($files); //release file handles
return;
}
}
/**
* Unzip file to given file path (real OS filesystem), existing files are overwrited
* @param mixed $zipfile full pathname of zip file or stored_file instance
* @param string $path target directory
* @param string $pathname target directory
* @return mixed list of processed files; false if error
*/
public function unzip_files_to_pathname($zipfile, $pathname) {
@ -32,20 +150,18 @@ class file_packer {
if (!is_readable($zipfile)) {
return false;
}
$zip = new ZipArchive();
if (!$zip->open($zipfile, ZIPARCHIVE::FL_NOCASE)) {
$ziparch = new zip_archive();
if (!$ziparch->open($zipfile, ZIPARCHIVE::FL_NOCASE)) {
return false;
}
for ($i=0; $i<$zip->numFiles; $i++) {
$index = $zip->statIndex($i);
for ($i=0; $i<$ziparch->numFiles; $i++) {
$index = $ziparch->statIndex($i);
$size = clean_param($index['size'], PARAM_INT);
$name = clean_param($index['name'], PARAM_PATH);
$name = ltrim($name, '/');
//TODO: add $name encoding conversions magic here
if ($name === '' or array_key_exists($name, $processed)) {
//probably filename collisions caused by filename cleaning/conversion
continue;
@ -87,7 +203,7 @@ class file_packer {
$processed[$name] = 'Can not write target file'; // TODO: localise
continue;
}
if (!$fz = $zip->getStream($index['name'])) {
if (!$fz = $ziparch->getStream($index['name'])) {
$processed[$name] = 'Can not read file from zip archive'; // TODO: localise
fclose($fp);
continue;
@ -107,7 +223,7 @@ class file_packer {
}
$processed[$name] = true;
}
$zip->close();
$ziparch->close();
return $processed;
}
@ -127,7 +243,7 @@ class file_packer {
return $zipfile->unzip_files_to_pathname($contextid, $filearea, $itemid, $pathbase, $userid);
}
check_dir_exists($CFG->dataroot.'/temp/unzip', true, true);
check_dir_exists($CFG->dataroot.'/temp/zip', true, true);
$pathbase = trim($pathbase, '/');
$pathbase = ($pathbase === '') ? '/' : '/'.$pathbase.'/';
@ -135,19 +251,18 @@ class file_packer {
$processed = array();
$zip = new ZipArchive();
if (!$zip->open($zipfile, ZIPARCHIVE::FL_NOCASE)) {
$ziparch = new zip_archive();
if (!$ziparch->open($zipfile, ZIPARCHIVE::FL_NOCASE)) {
return false;
}
for ($i=0; $i<$zip->numFiles; $i++) {
$index = $zip->statIndex($i);
for ($i=0; $i<$ziparch->numFiles; $i++) {
$index = $ziparch->statIndex($i);
$size = clean_param($index['size'], PARAM_INT);
$name = clean_param($index['name'], PARAM_PATH);
$name = ltrim($name, '/');
//TODO: add $name encoding conversions magic here
if ($name === '' or array_key_exists($name, $processed)) {
//probably filename collisions caused by filename cleaning/conversion
@ -170,7 +285,7 @@ class file_packer {
if ($size < 2097151) {
// small file
if (!$fz = $zip->getStream($index['name'])) {
if (!$fz = $ziparch->getStream($index['name'])) {
$processed[$name] = 'Can not read file from zip archive'; // TODO: localise
continue;
}
@ -209,13 +324,13 @@ class file_packer {
} else {
// large file, would not fit into memory :-(
$tmpfile = tempnam($CFG->dataroot.'/temp/unzip', 'largefile');
$tmpfile = tempnam($CFG->dataroot.'/temp/zip', 'unzip');
if (!$fp = fopen($tmpfile, 'wb')) {
@unlink($tmpfile);
$processed[$name] = 'Can not write temp file'; // TODO: localise
continue;
}
if (!$fz = $zip->getStream($index['name'])) {
if (!$fz = $ziparch->getStream($index['name'])) {
@unlink($tmpfile);
$processed[$name] = 'Can not read file from zip archive'; // TODO: localise
continue;
@ -256,6 +371,7 @@ class file_packer {
continue;
}
}
$ziparch->close();
return $processed;
}
}

View File

@ -129,6 +129,24 @@ class stored_file {
return $packer->unzip_files_to_storage($zipfile, $contextid, $filearea, $itemid, $pathbase);
}
/**
* Add file/directory into zip archive
* @param object $ziparchive
* @param string $archivepath pathname in zip archive
* @return bool success
*/
public function add_to_ziparchive(zip_archive $ziparch, $archivepath) {
if ($this->is_directory()) {
return $ziparch->addEmptyDir($archivepath);
} else {
$path = $this->get_content_file_location();
if (!is_readable($path)) {
return false;
}
return $ziparch->addFile($path, $archivepath);
}
}
public function get_contextid() {
return $this->file_record->contextid;
}

11
lib/file/zip_archive.php Normal file
View File

@ -0,0 +1,11 @@
<?php //$Id$
class zip_archive extends ZipArchive {
//TODO: limit number of open file handles by fetching small files into memory and closing/reopening archive for large files
//TODO: add file name encoding conversions
//TODO: prevent adding of target zip into archive
}