0) { ob_end_clean(); } if ($piece === false) { status_header(416); header('Content-Range: bytes */' . $size); exit; } header('Content-Type: application/octet-stream'); header('Content-Disposition: attachment; filename="' . str_replace(['"', "\r", "\n"], '', $filename) . '"'); header('X-Content-Type-Options: nosniff'); if ($piece === null) { header('Content-Length: ' . (string) $size); Fs::send($path); exit; } status_header(206); header('Content-Range: bytes ' . $piece[0] . '-' . $piece[1] . '/' . $size); header('Content-Length: ' . (string) ($piece[1] - $piece[0] + 1)); Fs::send_range($path, $piece[0], $piece[1] - $piece[0] + 1); exit; } /** * The bytes a Range header asks for, as [first, last]: null for the whole file (no header, or one this doesn't * answer, such as several ranges), false when nothing of the file is in it. * * @return array{0: int, 1: int}|false|null */ public static function range(string $header, int $size) { if (!preg_match('/^bytes=(\d*)-(\d*)$/', trim($header), $m) || ($m[1] === '' && $m[2] === '')) { return null; } if ($m[1] === '') { // the last N bytes $n = (int) $m[2]; return $n === 0 || $size === 0 ? false : [max(0, $size - $n), $size - 1]; } $first = (int) $m[1]; if ($m[2] !== '' && (int) $m[2] < $first) { return null; // not a valid range, so the header is ignored } if ($first >= $size) { return false; } return [$first, $m[2] === '' ? $size - 1 : min((int) $m[2], $size - 1)]; } /** A download's name: letters, digits, dots and dashes. (sanitize_file_name() would turn "example.co.za" into "example.co_.za".) */ public static function file_name(string $name): string { return trim((string) preg_replace('/[^A-Za-z0-9._-]+/', '-', $name), '-.'); } }