mirror of
https://github.com/moodle/moodle.git
synced 2025-01-18 22:08:20 +01:00
MDL-15920 initial zipping support
This commit is contained in:
parent
f6b923eaba
commit
b1897a6d68
@ -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;
|
||||
}
|
||||
}
|
@ -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
11
lib/file/zip_archive.php
Normal 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
|
||||
|
||||
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user