123 lines
4.0 KiB
PHP
123 lines
4.0 KiB
PHP
<?php
|
|
|
|
require('../include/json-header.php');
|
|
require('../include/uploaded-file-helpers.php');
|
|
|
|
function main($path)
|
|
{
|
|
if (count($path) > 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());
|
|
|
|
?>
|