Система управления файлами PHP
-
21-08-2019 - |
Вопрос
Нам нужно создать интерфейс управления файлами, который интегрируется с текущим веб-сайтом клиента, построенным на Cakephp.
Файловый менеджер должен позволять пользователям видеть только те файлы, к которым у них есть разрешения.
Пользователь сможет загружать файлы (любого размера) и предлагать другим пользователям загружать эти файлы (если позволяют разрешения).
Это не проблема для многих систем управления файлами, которые мы можем приобрести, но ни одна из тех, что я могу найти, не интегрируется с их текущей системой входа в систему.Клиент хочет, чтобы пользователи входили в систему только один раз, чтобы получить доступ к своему пользовательскому CP и своим файлам.Итак, насколько я понимаю, наш единственный вариант — это создать интерфейс управления файлами самостоятельно.
Какие параметры мы можем разрешить загрузку с помощью PHP?Я понимаю, что нам нужно увеличить лимит загрузки php, но есть ли потолок, который позволяет php/apache?
Размер файлов, скорее всего, будет ограничен примерно 150 МБ, если это актуально, однако могут быть ситуации, когда размеры файлов будут больше.
Также, что можно и чего нельзя делать при загрузке/управлении файлами на сервере Linux?
Полагаю, у меня нет настоящих «конкретных» вопросов, но хотелось бы получить совет о том, с чего начать и о некоторых типичных ловушках, с которыми мы столкнемся.
Решение
На самом деле управление файлами довольно простое.Вот несколько советов, которые могут указать вам правильное направление.
Прежде всего, если это ситуация с веб-сервером со сбалансированной нагрузкой, вам нужно будет немного повысить сложность, чтобы разместить файлы в одном общем месте.Если это так, свяжитесь со мной, и я буду рад выслать вам наш сверхлегкий файловый сервер/клиент, который мы используем для той же ситуации.
Есть несколько переменных, на которые вы захотите повлиять, чтобы разрешить загрузку большего размера.Я рекомендую использовать директивы Apache, чтобы ограничить эти изменения конкретным файлом:
<Directory /home/deploy/project/uploader>
php_value max_upload_size "200M"
php_value post_max_size "200M"
php_value max_input_time "1800"
# this one depends on how much processing you are doing to the file
php_value memory_limit "32M"
</Directory>
Архитектура:
Создайте таблицу базы данных, в которой будет храниться информация о каждом файле.
CREATE TABLE `File` (
`File_MNID` int(10) unsigned NOT NULL AUTO_INCREMENT,
`Owner_Field` enum('User.User_ID', 'Resource.Resource_ID') NOT NULL,
`Owner_Key` int(10) unsigned NOT NULL,
`ContentType` varchar(64) NOT NULL,
`Size` int(10) NOT NULL,
`Hash` varchar(40) NOT NULL,
`Name` varchar(128) NOT NULL,
PRIMARY KEY (`File_MNID`),
KEY `Owner` (`Owner_Field`,`Owner_Key`)
) ENGINE=InnoDB
Что Owner_Field
и Owner_Key
?Простой способ сказать, какой «объект» владеет файлом.В данном конкретном случае загружалось несколько типов файлов.В вашем случае простой User_ID
поле может быть достаточным.
Цель сохранения владельца состоит в том, чтобы вы могли ограничить круг лиц, которые могут загружать и удалять файл.Это будет иметь решающее значение для защиты загрузок.
Вот пример класса который можно использовать для принятия загрузок файлов из браузера.Конечно, вам придется изменить его под свои нужды.
В следующем коде следует отметить несколько моментов.Поскольку он используется с сервером приложений и файловым сервером, есть несколько вещей, которые нужно «заменить».
- Любые случаи
App::CallAPI(...)
необходимо будет заменить запросом или набором запросов, которые делают «то же самое». - Любые случаи
App::$FS->...
необходимо будет заменить правильными функциями обработки файлов в PHP, такими какmove_uploaded_file
,readfile
, и т. д...
Вот.Имейте в виду, что здесь есть функции, которые позволяют вам видеть файлы, принадлежащие данному пользователю, удалять файлы и так далее и тому подобное.Подробное объяснение внизу...
<?php
class FileClient
{
public static $DENY = '/\.ade$|\.adp$|\.asp$|\.bas$|\.bat$|\.chm$|\.cmd$|\.com$|\.cpl$|\.crt$|\.exe$|\.hlp$|\.hta$|\.inf$|\.ins$|\.isp$|\.its$| \.js$|\.jse$|\.lnk$|\.mda$|\.mdb$|\.mde$|\.mdt,\. mdw$|\.mdz$|\.msc$|\.msi$|\.msp$|\.mst$|\.pcd$|\.pif$|\.reg$|\.scr$|\.sct$|\.shs$|\.tmp$|\.url$|\.vb$|\.vbe$|\.vbs$|vsmacros$|\.vss$|\.vst$|\.vsw$|\.ws$|\.wsc$|\.wsf$|\.wsh$/i';
public static $MAX_SIZE = 5000000;
public static function SelectList($Owner_Field, $Owner_Key)
{
$tmp = App::CallAPI
(
'File.List',
array
(
'Owner_Field' => $Owner_Field,
'Owner_Key' => $Owner_Key,
)
);
return $tmp['Result'];
}
public static function HandleUpload($Owner_Field, $Owner_Key, $FieldName)
{
$aError = array();
if(! isset($_FILES[$FieldName]))
return false;
elseif(! is_array($_FILES[$FieldName]))
return false;
elseif(! $_FILES[$FieldName]['tmp_name'])
return false;
elseif($_FILES[$FieldName]['error'])
return array('An unknown upload error has occured.');
$sPath = $_FILES[$FieldName]['tmp_name'];
$sHash = sha1_file($sPath);
$sType = $_FILES[$FieldName]['type'];
$nSize = (int) $_FILES[$FieldName]['size'];
$sName = $_FILES[$FieldName]['name'];
if(preg_match(self::$DENY, $sName))
{
$aError[] = "File type not allowed for security reasons. If this file must be attached, please add it to a .zip file first...";
}
if($nSize > self::$MAX_SIZE)
{
$aError[] = 'File too large at $nSize bytes.';
}
// Any errors? Bail out.
if($aError)
{
return $aError;
}
$File = App::CallAPI
(
'File.Insert',
array
(
'Owner_Field' => $Owner_Field,
'Owner_Key' => $Owner_Key,
'ContentType' => $sType,
'Size' => $nSize,
'Hash' => $sHash,
'Name' => $sName,
)
);
App::InitFS();
App::$FS->PutFile("File_" . $File['File_MNID'], $sPath);
return $File['File_MNID'];
}
public static function Serve($Owner_Field, $Owner_Key, $File_MNID)
{
//Also returns the name, content-type, and ledger_MNID
$File = App::CallAPI
(
'File.Select',
array
(
'Owner_Field' => $Owner_Field,
'Owner_Key' => $Owner_Key,
'File_MNID' => $File_MNID
)
);
$Name = 'File_' . $File['File_MNID'] ;
//Content Header for that given file
header('Content-disposition: attachment; filename="' . $File['Name'] . '"');
header("Content-type:'" . $File['ContentType'] . "'");
App::InitFS();
#TODO
echo App::$FS->GetString($Name);
}
public static function Delete($Owner_Field, $Owner_Key, $File_MNID)
{
$tmp = App::CallAPI
(
'File.Delete',
array
(
'Owner_Field' => $Owner_Field,
'Owner_Key' => $Owner_Key,
'File_MNID' => $File_MNID,
)
);
App::InitFS();
App::$FS->DelFile("File_" . $File_MNID);
}
public static function DeleteAll($Owner_Field, $Owner_Key)
{
foreach(self::SelectList($Owner_Field, $Owner_Key) as $aRow)
{
self::Delete($Owner_Field, $Owner_Key, $aRow['File_MNID']);
}
}
}
Примечания:
Имейте в виду, что этот класс НЕ реализует безопасность.Предполагается, что перед вызовом у вызывающего абонента есть аутентифицированные Owner_Field и Owner_Key. FileClient::Serve(...)
и т. д...
Уже немного поздно, поэтому, если что-то из этого не имеет смысла, просто оставьте комментарий.Хорошего вечера, и я надеюсь, что это кому-то поможет.
ПС.Пользовательский интерфейс может представлять собой простые таблицы, поля для загрузки файлов и т. д.Или вы можете проявить фантазию и использовать флеш-загрузчик...
Другие советы
Я рекомендую взглянуть на следующие примеры кода CakePHP, предоставленные сообществом:
вот хороший выбор.Для этого требуется установить клиент ПК и однофайловый php-сервер.Но он работает быстро!