diff --git a/lib/filelib.php b/lib/filelib.php index 2260e4af3cd..3d4dd833b3d 100644 --- a/lib/filelib.php +++ b/lib/filelib.php @@ -1959,6 +1959,43 @@ function send_header_404() { } } +/** + * The readfile function can fail when files are larger than 2GB (even on 64-bit + * platforms). This wrapper uses readfile for small files and custom code for + * large ones. + * + * @param string $path Path to file + * @param int $filesize Size of file (if left out, will get it automatically) + * @return int|bool Size read (will always be $filesize) or false if failed + */ +function readfile_allow_large($path, $filesize = -1) { + // Automatically get size if not specified. + if ($filesize === -1) { + $filesize = filesize($path); + } + if ($filesize <= 2147483647) { + // If the file is up to 2^31 - 1, send it normally using readfile. + return readfile($path); + } else { + // For large files, read and output in 64KB chunks. + $handle = fopen($path, 'r'); + if ($handle === false) { + return false; + } + $left = $filesize; + while ($left > 0) { + $size = min($left, 65536); + $buffer = fread($handle, $size); + if ($buffer === false) { + return false; + } + echo $buffer; + $left -= $size; + } + return $filesize; + } +} + /** * Enhanced readfile() with optional acceleration. * @param string|stored_file $file @@ -2081,7 +2118,7 @@ function readfile_accel($file, $mimetype, $accelerate) { if (is_object($file)) { $file->readfile(); } else { - readfile($file); + readfile_allow_large($file, $filesize); } } diff --git a/lib/filestorage/stored_file.php b/lib/filestorage/stored_file.php index d56aa524045..f1e6c248825 100644 --- a/lib/filestorage/stored_file.php +++ b/lib/filestorage/stored_file.php @@ -409,7 +409,7 @@ class stored_file { throw new file_exception('storedfilecannotread', '', $path); } } - readfile($path); + readfile_allow_large($path, $this->get_filesize()); } /** diff --git a/lib/upgrade.txt b/lib/upgrade.txt index 550f27120ea..0450eb2296c 100644 --- a/lib/upgrade.txt +++ b/lib/upgrade.txt @@ -32,6 +32,7 @@ information provided here is intended especially for developers. get_fast_modinfo(). Purging all caches and every core upgrade purges course modinfo cache as well. If function get_fast_modinfo() is called for multiple courses make sure to include field cacherev in course object. +* New function readfile_allow_large() in filelib.php for use when very large files may need sending to user. DEPRECATIONS: Various previously deprecated functions have now been altered to throw DEBUG_DEVELOPER debugging notices