MDL-15919 unzipping support
This commit is contained in:
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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];
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user