MDL-15919 unzipping support

This commit is contained in:
skodak
2008-08-02 18:33:11 +00:00
parent 3570711ad5
commit 17d9269f59
3 changed files with 296 additions and 7 deletions
+253
View File
@@ -1,5 +1,258 @@
<?php //$Id$
/**
* Utility class - handles all zipping and unzipping operations.
*/
class file_packer {
public function zip_files_to_pathname($files, $zipfile) {
error('todo');
}
public function zip_files_to_storage($files, $contextid, $filearea, $itemid, $filepath, $filename) {
error('todo');
}
/**
* 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
* @return mixed list of processed files; false if error
*/
public function unzip_files_to_pathname($zipfile, $pathname) {
global $CFG;
if (!is_string($zipfile)) {
return $zipfile->unzip_files_to_pathname($pathname);
}
$processed = array();
$pathname = rtrim($pathname, '/');
if (!is_readable($zipfile)) {
return false;
}
$zip = new ZipArchive();
if (!$zip->open($zipfile, ZIPARCHIVE::FL_NOCASE)) {
return false;
}
for ($i=0; $i<$zip->numFiles; $i++) {
$index = $zip->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;
}
if ($size === 0 and $name[strlen($name)-1] === '/') {
$newdir = "$pathname/$name";
// directory
if (is_file($newdir) and !unlink($newdir)) {
$processed[$name] = 'Can not create directory, file already exists'; // TODO: localise
continue;
}
if (is_dir($newdir)) {
//dir already there
$processed[$name] = true;
} else {
if (mkdir($newdir, $CFG->directorypermissions, true)) {
$processed[$name] = true;
} else {
$processed[$name] = 'Can not create directory'; // TODO: localise
}
}
continue;
}
$parts = explode('/', trim($name, '/'));
$filename = array_pop($parts);
$newdir = rtrim($pathname.'/'.implode('/', $parts), '/');
if (!is_dir($newdir)) {
if (!mkdir($newdir, $CFG->directorypermissions, true)) {
$processed[$name] = 'Can not create directory'; // TODO: localise
continue;
}
}
$newfile = "$newdir/$filename";
if (!$fp = fopen($newfile, 'wb')) {
$processed[$name] = 'Can not write target file'; // TODO: localise
continue;
}
if (!$fz = $zip->getStream($index['name'])) {
$processed[$name] = 'Can not read file from zip archive'; // TODO: localise
fclose($fp);
continue;
}
while (!feof($fz)) {
$content = fread($fz, 262143);
fwrite($fp, $content);
}
fclose($fz);
fclose($fp);
if (filesize($newfile) !== $size) {
$processed[$name] = 'Unknown error during zip extraction'; // TODO: localise
// something went wrong :-(
@unlink($newfile);
continue;
}
$processed[$name] = true;
}
$zip->close();
return $processed;
}
/**
* 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 int $contextid
* @param string $filearea
* @param int $itemid
* @param string $filepath
* @return mixed list of processed files; false if error
*/
public function unzip_files_to_storage($zipfile, $contextid, $filearea, $itemid, $pathbase, $userid=null) {
global $CFG;
if (!is_string($zipfile)) {
return $zipfile->unzip_files_to_pathname($contextid, $filearea, $itemid, $pathbase, $userid);
}
check_dir_exists($CFG->dataroot.'/temp/unzip', true, true);
$pathbase = trim($pathbase, '/');
$pathbase = ($pathbase === '') ? '/' : '/'.$pathbase.'/';
$fs = get_file_storage();
$processed = array();
$zip = new ZipArchive();
if (!$zip->open($zipfile, ZIPARCHIVE::FL_NOCASE)) {
return false;
}
for ($i=0; $i<$zip->numFiles; $i++) {
$index = $zip->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;
}
if ($size === 0 and $name[strlen($name)-1] === '/') {
$newfilepath = $pathbase.$name.'/';
$fs->create_directory($contextid, $filearea, $itemid, $newfilepath, $userid);
$processed[$name] = true;
continue;
}
$parts = explode('/', trim($name, '/'));
$filename = array_pop($parts);
$filepath = $pathbase;
if ($parts) {
$filepath .= implode('/', $parts).'/';
}
if ($size < 2097151) {
// small file
if (!$fz = $zip->getStream($index['name'])) {
$processed[$name] = 'Can not read file from zip archive'; // TODO: localise
continue;
}
$content = '';
while (!feof($fz)) {
$content .= fread($fz, 262143);
}
fclose($fz);
if (strlen($content) !== $size) {
$processed[$name] = 'Unknown error during zip extraction'; // TODO: localise
// something went wrong :-(
unset($content);
continue;
}
if ($file = $fs->get_file($contextid, $filearea, $itemid, $filepath, $filename)) {
if (!$file->delete()) {
$processed[$name] = 'Can not delete existing file'; // TODO: localise
continue;
}
}
$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;
if ($fs->create_file_from_string($file_record, $content)) {
$processed[$name] = true;
} else {
$processed[$name] = 'Unknown error during zip extraction'; // TODO: localise
}
unset($content);
continue;
} else {
// large file, would not fit into memory :-(
$tmpfile = tempnam($CFG->dataroot.'/temp/unzip');
if (!$fp = fopen($tmpfile, 'wb')) {
$processed[$name] = 'Can not write temp file'; // TODO: localise
continue;
}
if (!$fz = $zip->getStream($index['name'])) {
$processed[$name] = 'Can not read file from zip archive'; // TODO: localise
continue;
}
while (!feof($fz)) {
$content = fread($fz, 262143);
fwrite($fp, $content);
}
fclose($fz);
fclose($fp);
if (strlen($tmpfile) !== $size) {
$processed[$name] = 'Unknown error during zip extraction'; // TODO: localise
// something went wrong :-(
@unlink($tmpfile);
continue;
}
if ($file = $fs->get_file($contextid, $filearea, $itemid, $filepath, $filename)) {
if (!$file->delete()) {
$processed[$name] = 'Can not delete existing file'; // TODO: localise
continue;
}
}
$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;
if ($fs->create_file_from_pathname($file_record, $tmpfile)) {
$processed[$name] = true;
} else {
$processed[$name] = 'Unknown error during zip extraction'; // TODO: localise
}
@unlink($tmpfile);
continue;
}
}
return $processed;
}
}
+3 -3
View File
@@ -23,7 +23,7 @@ class file_storage {
throw new file_exception('localfilecannotcreatefiledirs'); // permission trouble
}
// place warning file in file pool root
file_put_contents($this->filedir.'/warning.txt',
file_put_contents($this->filedir.'/warning.txt',
'This directory contains the content of uploaded files and is controlled by Moodle code. Do not manually move, change or rename any of the files and subdirectories here.');
}
}
@@ -570,12 +570,12 @@ class file_storage {
/**
* Return path to file with given hash
*
* DO NOT USE - should be protected, but protected is dumb in PHP
* NOTE: must not be public, files in pool must not be modified
*
* @param string $contenthash
* @return string expected file location
*/
public function path_from_hash($contenthash) {
protected function path_from_hash($contenthash) {
$l1 = $contenthash[0].$contenthash[1];
$l2 = $contenthash[2].$contenthash[3];
$l3 = $contenthash[4].$contenthash[5];
+40 -4
View File
@@ -37,11 +37,21 @@ class stored_file {
/**
* Protected - developers must not gain direct access to this function
* NOTE: do not make this public, we must not modify or delete the pool files directly! ;-)
* @return ful path to pool file with file content
**/
protected function get_content_file_location() {
// NOTE: do not make this public, we must not modify or delete the pool files directly! ;-)
$hashpath = $this->fs->path_from_hash($this->file_record->contenthash);
return $hashpath.'/'.$this->file_record->contenthash;
global $CFG;
if (isset($CFG->filedir)) {
$filedir = $CFG->filedir;
} else {
$filedir = $CFG->dataroot.'/filedir';
}
$contenthash = $this->file_record->contenthash;
$l1 = $contenthash[0].$contenthash[1];
$l2 = $contenthash[2].$contenthash[3];
$l3 = $contenthash[4].$contenthash[5];
return "$filedir/$l1/$l2/$l3/$contenthash";
}
/**
@@ -82,7 +92,7 @@ class stored_file {
/**
* Copy content of file to give npathname
* @param string $pathnema rela path to new file
* @param string $pathnema rela path to new file
* @return bool success
*/
public function copy_content_to($pathname) {
@@ -93,6 +103,32 @@ class stored_file {
return copy($path, $pathname);
}
/**
* Unzip file to given file path (real OS filesystem), existing files are overwrited
* @param string $path target directory
* @return mixed list of processed files; false if error
*/
public function unzip_files_to_pathname($path) {
$packer = get_file_packer();
$zipfile = $this->get_content_file_location();
return $packer->unzip_files_to_pathname($path, $path);
}
/**
* Unzip file to given file path (real OS filesystem), existing files are overwrited
* @param int $contextid
* @param string $filearea
* @param int $itemid
* @param string $pathbase
* @param int $userid
* @return mixed list of processed files; false if error
*/
public function unzip_files_to_storage($contextid, $filearea, $itemid, $pathbase, $userid=null) {
$packer = get_file_packer();
$zipfile = $this->get_content_file_location();
return $packer->unzip_files_to_storage($zipfile, $contextid, $filearea, $itemid, $pathbase);
}
public function get_contextid() {
return $this->file_record->contextid;
}