1) exit_with_error('InvalidRequest'); $db = connect(); if (count($path) && $path[0]) { $file_id = intval($path[0]); $file_row = $db->select_first_row('uploaded_files', 'file', array('id' => $file_id)); if (!$file_row) exit_with_404(); $file_path = uploaded_file_path_for_row($file_row); return stream_file_content($file_path, $file_row['file_sha256'], $file_row['file_filename']); } $sha256 = array_get($_GET, 'sha256'); if ($sha256) { $file_row = $db->select_first_row('uploaded_files', 'file', array('sha256' => $sha256, 'deleted_at' => null)); if (!$file_row) exit_with_error('NotFound'); exit_with_success(array('uploadedFile' => format_uploaded_file($file_row))); } exit_with_error('InvalidArguments'); } define('STREAM_CHUNK_SIZE', 64 * 1024); function stream_file_content($uploaded_file, $etag, $disposition_name) { if (!file_exists($uploaded_file)) exit_with_404(); $file_size = filesize($uploaded_file); $last_modified = gmdate('D, d M Y H:i:s', filemtime($uploaded_file)) . ' GMT'; $file_handle = fopen($uploaded_file, "rb"); if (!$file_handle) exit_with_404(); $headers = getallheaders(); // We don't support multi-part range request. e.g. bytes=1-3,4-5 $range = parse_range_header(array_get($headers, 'Range'), $file_size); if ($range && (!array_key_exists('If-Range', $headers) || $headers['If-Range'] == $last_modified || $headers['If-Range'] == $etag)) { assert($range['start'] >= 0); if ($range['start'] > $range['end'] || $range['end'] >= $file_size) { header('HTTP/1.1 416 Range Not Satisfiable'); header("Content-Range: bytes */$file_size"); exit(416); } $start = $range['start']; $end = $range['end']; $content_length = $end - $start + 1; header('HTTP/1.1 206 Partial Content'); header("Content-Range: bytes $start-$end/$file_size"); fseek($file_handle, $start); } else { $content_length = $file_size; header("Accept-Ranges: bytes"); header("ETag: $etag"); } $output_buffer = fopen('php://output', 'wb'); $encoded_filename = urlencode($disposition_name); header('Content-Type: application/octet-stream'); header('Content-Length: ' . $content_length); header("Content-Disposition: attachment; filename*=utf-8''$encoded_filename"); header("Last-Modified: $last_modified"); set_time_limit(0); while (!feof($file_handle) && $content_length) { $is_end = $content_length < STREAM_CHUNK_SIZE; $chunk_size = $is_end ? $content_length : STREAM_CHUNK_SIZE; $chunk = fread($file_handle, $chunk_size); $content_length -= $chunk_size; fwrite($output_buffer, $chunk, $chunk_size); flush(); } exit(0); } function parse_range_header($range_header, $file_size) { // We don't support multi-part range request. e.g. bytes=1-3,4-5 $matches = array(); $end_byte = $file_size; if (!$range_header || !preg_match('/^\s*bytes\s*=\s*((\d+)-(\d*)|-(\d+))\s*$/', $range_header, $matches)) return NULL; $end_byte = $file_size - 1; if ($matches[2]) { $start_byte = intval($matches[2]); if ($matches[3]) $end_byte = intval($matches[3]); else $end_byte = $file_size - 1; } else { $suffix_length = intval($matches[4]); if ($file_size < $suffix_length) $start_byte = 0; else $start_byte = $file_size - $suffix_length; } return array('start' => $start_byte, 'end' => $end_byte); } function exit_with_404() { header($_SERVER['SERVER_PROTOCOL'] . ' 404 Not Found'); exit_with_error('NotFound'); } main(array_key_exists('PATH_INFO', $_SERVER) ? explode('/', trim($_SERVER['PATH_INFO'], '/')) : array()); ?>