TrueAsync\UploadedFile
(PHP 8.6+, true_async_server 0.1+)
Представлення одного завантаженого файлу з multipart/form-data. PSR-7-сумісний.
Отримується через HttpRequest::getFile() / HttpRequest::getFiles().
namespace TrueAsync;
final class UploadedFile
{
public function getStream(): mixed;
public function moveTo(string $targetPath, int $mode = 0644): void;
public function getSize(): ?int;
public function getError(): int;
public function getClientFilename(): ?string;
public function getClientMediaType(): ?string;
public function getClientCharset(): ?string;
public function isReady(): bool;
public function isValid(): bool;
}Методи
getStream
public UploadedFile::getStream(): mixedStream-resource для читання файлу. Можна читати частково завантажений файл.
| return | resource або null, якщо недоступний |
| throws | \RuntimeException, якщо файл уже було переміщено moveTo() |
moveTo
public UploadedFile::moveTo(string $targetPath, int $mode = 0644): voidПереміщує завантажений файл.
- Підтримує абсолютні та відносні шляхи.
- Автоматично створює parent-директорії за відсутності.
- Крос-FS: автоматичний fallback на
copy() + unlink().
| throws | \RuntimeException, якщо вже переміщено або помилка запису |
getSize
public UploadedFile::getSize(): ?intРозмір файлу в байтах. null, якщо невідомий (наприклад, при streaming до отримання tail).
getError
public UploadedFile::getError(): intКод помилки у форматі PHP UPLOAD_ERR_*.
getClientFilename
public UploadedFile::getClientFilename(): ?stringОригінальне ім'я від клієнта, як прийшло (без модифікацій). Ліміт 4 KB.
Не довіряйте. Ім'я може містити будь-які байти (включно з path-separators). Перед використанням або саніруйте, або генеруйте ім'я на сервері.
getClientMediaType
public UploadedFile::getClientMediaType(): ?stringMIME-тип від клієнта (взятий як є з браузерного Content-Type частини). Не верифікується сервером.
getClientCharset
public UploadedFile::getClientCharset(): ?stringCharset із заголовка Content-Type частини (якщо вказаний).
isReady
public UploadedFile::isReady(): booltrue після повного завантаження і закриття тимчасового дескриптора.
isValid
public UploadedFile::isValid(): boolЕквівалент getError() === UPLOAD_ERR_OK.
Приклад
$server->addHttpHandler(function ($req, $res) {
if ($req->getMethod() !== 'POST') {
$res->setStatusCode(405); return;
}
$avatar = $req->getFile('avatar');
if ($avatar === null) {
$res->setStatusCode(400)->json(['error' => 'no avatar field']); return;
}
if (!$avatar->isValid()) {
$res->setStatusCode(400)->json([
'error' => 'upload error',
'code' => $avatar->getError(),
]);
return;
}
if ($avatar->getSize() > 5 * 1024 * 1024) {
$res->setStatusCode(413)->json(['error' => 'too big']); return;
}
// Перевіримо, що клієнт надіслав картинку (довіряємо лише в першому наближенні)
$declared = $avatar->getClientMediaType() ?? '';
if (!str_starts_with($declared, 'image/')) {
$res->setStatusCode(415)->json(['error' => 'not an image']); return;
}
// Ім'я генеруємо самі, не довіряючи клієнту
$name = bin2hex(random_bytes(16)) . '.bin';
$avatar->moveTo("/var/storage/avatars/$name", 0644);
$res->json([
'saved' => $name,
'original_name' => $avatar->getClientFilename(),
'declared_mime' => $declared,
'size' => $avatar->getSize(),
]);
});Декілька файлів в одному полі
HTML-форма:
<input type="file" name="photos[]" multiple>Отримання:
$files = $req->getFiles();
// $files['photos'] === [UploadedFile, UploadedFile, ...]
foreach ($files['photos'] as $file) {
if (!$file->isValid()) continue;
$file->moveTo('/var/storage/' . bin2hex(random_bytes(8)));
}getFile('photos') у цьому випадку поверне перший файл з масиву — для doc-першого-файлу достатньо; для всіх — getFiles().