From b1897a6d68b11c46916b0d5bcd33d6685d1ad748 Mon Sep 17 00:00:00 2001 From: skodak Date: Sun, 3 Aug 2008 21:37:50 +0000 Subject: [PATCH] MDL-15920 initial zipping support --- lib/file/file_packer.php | 158 +++++++++++++++++++++++++++++++++------ lib/file/stored_file.php | 18 +++++ lib/file/zip_archive.php | 11 +++ 3 files changed, 166 insertions(+), 21 deletions(-) create mode 100644 lib/file/zip_archive.php diff --git a/lib/file/file_packer.php b/lib/file/file_packer.php index f5ef80df5f..7007b8b082 100644 --- a/lib/file/file_packer.php +++ b/lib/file/file_packer.php @@ -1,22 +1,140 @@ libdir/file/zip_archive.php"); + /** * Utility class - handles all zipping and unzipping operations. */ class file_packer { + /** + * 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; + } + + /** + * 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) { - error('todo'); + 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(); } - public function zip_files_to_storage($files, $contextid, $filearea, $itemid, $filepath, $filename) { - error('todo'); + 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; } } \ No newline at end of file diff --git a/lib/file/stored_file.php b/lib/file/stored_file.php index 8e536247cd..b7bf489fae 100644 --- a/lib/file/stored_file.php +++ b/lib/file/stored_file.php @@ -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; } diff --git a/lib/file/zip_archive.php b/lib/file/zip_archive.php new file mode 100644 index 0000000000..d8b78679ec --- /dev/null +++ b/lib/file/zip_archive.php @@ -0,0 +1,11 @@ +