Implement chunked file uploads on the server side

This commit is contained in:
Kijin Sung 2016-12-17 20:50:43 +09:00
parent ef27f8c0c4
commit e2895912e5

View file

@ -29,21 +29,98 @@ class fileController extends file
$file_info = $_FILES['Filedata'];
// An error appears if not a normally uploaded file
if(!is_uploaded_file($file_info['tmp_name'])) exit();
if(!$file_info || !is_uploaded_file($file_info['tmp_name'])) exit();
// Basic variables setting
$oFileModel = getModel('file');
$editor_sequence = Context::get('editor_sequence');
$upload_target_srl = intval(Context::get('uploadTargetSrl'));
if(!$upload_target_srl) $upload_target_srl = intval(Context::get('upload_target_srl'));
$module_srl = $this->module_srl;
// Exit a session if there is neither upload permission nor information
if(!$_SESSION['upload_info'][$editor_sequence]->enabled) exit();
// Extract from session information if upload_target_srl is not specified
if(!$upload_target_srl) $upload_target_srl = $_SESSION['upload_info'][$editor_sequence]->upload_target_srl;
// Create if upload_target_srl is not defined in the session information
if(!$upload_target_srl) $_SESSION['upload_info'][$editor_sequence]->upload_target_srl = $upload_target_srl = getNextSequence();
if(!$_SESSION['upload_info'][$editor_sequence]->enabled)
{
return new Object(-1, 'msg_not_permitted');
}
// Get upload_target_srl
$upload_target_srl = intval(Context::get('uploadTargetSrl')) ?: intval(Context::get('upload_target_srl'));
if (!$upload_target_srl)
{
$upload_target_srl = $_SESSION['upload_info'][$editor_sequence]->upload_target_srl;
}
if (!$upload_target_srl)
{
$upload_target_srl = getNextSequence();
$_SESSION['upload_info'][$editor_sequence]->upload_target_srl = $upload_target_srl;
}
// Handle chunking
if (preg_match('!^bytes (\d+)-(\d+)/(\d+)$!', $_SERVER['HTTP_CONTENT_RANGE'], $matches))
{
// Check basic sanity
$chunk_start = $matches[1];
$chunk_size = ($matches[2] - $matches[1]) + 1;
$total_size = $matches[3];
if ($chunk_start < 0 || $chunk_size < 0 || $total_size < 0 || $chunk_start + $chunk_size > $total_size || $chunk_size != $file_info['size'])
{
return new Object(-1, 'msg_upload_invalid_chunk');
}
// Check existing chunks
$temp_key = hash_hmac('sha1', sprintf('%d:%d:%d:%s', $editor_sequence, $upload_target_srl, $module_srl, $file_info['name']), config('crypto.authentication_key'));
$temp_filename = RX_BASEDIR . 'files/attach/chunks/' . $temp_key;
if ($chunk_start == 0 && Rhymix\Framework\Storage::isFile($temp_filename))
{
Rhymix\Framework\Storage::delete($temp_filename);
return new Object(-1, 'msg_upload_invalid_chunk');
}
if ($chunk_start != 0 && (!Rhymix\Framework\Storage::isFile($temp_filename) || Rhymix\Framework\Storage::getSize($temp_filename) != $chunk_start))
{
Rhymix\Framework\Storage::delete($temp_filename);
return new Object(-1, 'msg_upload_invalid_chunk');
}
// Check size limit
$is_admin = (Context::get('logged_info')->is_admin === 'Y');
if (!$is_admin)
{
$module_config = getModel('file')->getFileConfig($module_srl);
$allowed_attach_size = $module_config->allowed_attach_size * 1024 * 1024;
$allowed_filesize = $module_config->allowed_filesize * 1024 * 1024;
if ($total_size > $allowed_filesize)
{
return new Object(-1, 'msg_exceeds_limit_size');
}
$output = executeQuery('file.getAttachedFileSize', (object)array('upload_target_srl' => $upload_target_srl));
if (intval($output->data->attached_size) + $total_size > $allowed_attach_size)
{
return new Object(-1, 'msg_exceeds_limit_size');
}
}
// Append to chunk
$fp = fopen($file_info['tmp_name'], 'r');
$success = Rhymix\Framework\Storage::write($temp_filename, $fp, 'a');
if ($success && Rhymix\Framework\Storage::getSize($temp_filename) == $chunk_start + $chunk_size)
{
if ($chunk_start + $chunk_size == $total_size)
{
$file_info['tmp_name'] = $temp_filename;
$file_info['size'] = Rhymix\Framework\Storage::getSize($temp_filename);
}
else
{
return new Object();
}
}
else
{
Rhymix\Framework\Storage::delete($temp_filename);
return new Object(-1, 'msg_upload_chunk_append_failed');
}
}
// Save the file
$output = $this->insertFile($file_info, $module_srl, $upload_target_srl);
Context::setResponseMethod('JSON');
@ -752,7 +829,7 @@ class fileController extends file
}
// Move the file
if($manual_insert)
if($manual_insert || starts_with(RX_BASEDIR . 'files/attach/chunks/', $file_info['tmp_name']))
{
@copy($file_info['tmp_name'], $filename);
if(!file_exists($filename))