Use zip32 only if there are less than 65536 files
A zip32 file can contain, at most, 65535 files (and folders), so take that constraint into account. Signed-off-by: Daniel Calviño Sánchez <danxuliu@gmail.com>
This commit is contained in:
parent
4a73f645e5
commit
90fdf83ca7
|
@ -40,14 +40,34 @@ class Streamer {
|
|||
*
|
||||
* @param IRequest $request
|
||||
* @param int $size The size of the files in bytes
|
||||
* @param int $numberOfFiles The number of files (and directories) that will
|
||||
* be included in the streamed file
|
||||
*/
|
||||
public function __construct(IRequest $request, int $size){
|
||||
public function __construct(IRequest $request, int $size, int $numberOfFiles){
|
||||
|
||||
/**
|
||||
* If the size if below 4GB always use zip32
|
||||
* Use 4*1000*1000*1000 so we have a buffer for all the extra zip data
|
||||
* zip32 constraints for a basic (without compression, volumes nor
|
||||
* encryption) zip file according to the Zip specification:
|
||||
* - No file size is larger than 4 bytes (file size < 4294967296); see
|
||||
* 4.4.9 uncompressed size
|
||||
* - The size of all files plus their local headers is not larger than
|
||||
* 4 bytes; see 4.4.16 relative offset of local header and 4.4.24
|
||||
* offset of start of central directory with respect to the starting
|
||||
* disk number
|
||||
* - The total number of entries (files and directories) in the zip file
|
||||
* is not larger than 2 bytes (number of entries < 65536); see 4.4.22
|
||||
* total number of entries in the central dir
|
||||
* - The size of the central directory is not larger than 4 bytes; see
|
||||
* 4.4.23 size of the central directory
|
||||
*
|
||||
* Due to all that, zip32 is used if the size is below 4GB and there are
|
||||
* less than 65536 files; the margin between 4*1000^3 and 4*1024^3
|
||||
* should give enough room for the extra zip metadata. Technically, it
|
||||
* would still be possible to create an invalid zip32 file (for example,
|
||||
* a zip file from files smaller than 4GB with a central directory
|
||||
* larger than 4GiB), but it should not happen in the real world.
|
||||
*/
|
||||
if ($size < 4 * 1000 * 1000 * 1000) {
|
||||
if ($size < 4 * 1000 * 1000 * 1000 && $numberOfFiles < 65536) {
|
||||
$this->streamerInstance = new ZipStreamer(['zip64' => false]);
|
||||
} else if ($request->isUserAgent($this->preferTarFor)) {
|
||||
$this->streamerInstance = new TarStreamer();
|
||||
|
|
|
@ -146,17 +146,23 @@ class OC_Files {
|
|||
|
||||
self::lockFiles($view, $dir, $files);
|
||||
|
||||
/* Calculate filesize */
|
||||
/* Calculate filesize and number of files */
|
||||
if ($getType === self::ZIP_FILES) {
|
||||
$fileInfos = array();
|
||||
$fileSize = 0;
|
||||
foreach ($files as $file) {
|
||||
$fileSize += \OC\Files\Filesystem::getFileInfo($dir . '/' . $file)->getSize();
|
||||
$fileInfo = \OC\Files\Filesystem::getFileInfo($dir . '/' . $file);
|
||||
$fileSize += $fileInfo->getSize();
|
||||
$fileInfos[] = $fileInfo;
|
||||
}
|
||||
$numberOfFiles = self::getNumberOfFiles($fileInfos);
|
||||
} elseif ($getType === self::ZIP_DIR) {
|
||||
$fileSize = \OC\Files\Filesystem::getFileInfo($dir . '/' . $files)->getSize();
|
||||
$fileInfo = \OC\Files\Filesystem::getFileInfo($dir . '/' . $files);
|
||||
$fileSize = $fileInfo->getSize();
|
||||
$numberOfFiles = self::getNumberOfFiles(array($fileInfo));
|
||||
}
|
||||
|
||||
$streamer = new Streamer(\OC::$server->getRequest(), $fileSize);
|
||||
$streamer = new Streamer(\OC::$server->getRequest(), $fileSize, $numberOfFiles);
|
||||
OC_Util::obEnd();
|
||||
|
||||
$streamer->sendHeaders($name);
|
||||
|
@ -324,6 +330,29 @@ class OC_Files {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the total (recursive) number of files and folders in the given
|
||||
* FileInfos.
|
||||
*
|
||||
* @param \OCP\Files\FileInfo[] $fileInfos the FileInfos to count
|
||||
* @return int the total number of files and folders
|
||||
*/
|
||||
private static function getNumberOfFiles($fileInfos) {
|
||||
$numberOfFiles = 0;
|
||||
|
||||
$view = new View();
|
||||
|
||||
while ($fileInfo = array_pop($fileInfos)) {
|
||||
$numberOfFiles++;
|
||||
|
||||
if ($fileInfo->getType() === \OCP\Files\FileInfo::TYPE_FOLDER) {
|
||||
$fileInfos = array_merge($fileInfos, $view->getDirectoryContent($fileInfo->getPath()));
|
||||
}
|
||||
}
|
||||
|
||||
return $numberOfFiles;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param View $view
|
||||
* @param string $dir
|
||||
|
|
Loading…
Reference in New Issue