|
|
|
|
|
<?php
|
|
|
|
|
|
|
|
|
|
|
|
namespace App\Support;
|
|
|
|
|
|
|
|
|
|
|
|
use Illuminate\Http\UploadedFile;
|
|
|
|
|
|
use Illuminate\Support\Facades\Storage;
|
|
|
|
|
|
use Illuminate\Support\Str;
|
|
|
|
|
|
use Illuminate\Validation\ValidationException;
|
|
|
|
|
|
use RuntimeException;
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 导入类文件:只进 local 私有盘,禁止写入 public,调用方用完须删除。
|
|
|
|
|
|
*/
|
|
|
|
|
|
final class SecurePrivateImportUpload
|
|
|
|
|
|
{
|
|
|
|
|
|
/**
|
|
|
|
|
|
* @param list<string> $allowedExtensions
|
|
|
|
|
|
*/
|
|
|
|
|
|
public static function storeTemporary(
|
|
|
|
|
|
UploadedFile $file,
|
|
|
|
|
|
string $directory,
|
|
|
|
|
|
array $allowedExtensions,
|
|
|
|
|
|
int $maxKilobytes
|
|
|
|
|
|
): string {
|
|
|
|
|
|
if (! $file->isValid()) {
|
|
|
|
|
|
throw ValidationException::withMessages([
|
|
|
|
|
|
'file' => ['上传未通过校验:'.$file->getErrorMessage()],
|
|
|
|
|
|
]);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
$size = (int) $file->getSize();
|
|
|
|
|
|
if ($size <= 0 || $size > $maxKilobytes * 1024) {
|
|
|
|
|
|
throw ValidationException::withMessages([
|
|
|
|
|
|
'file' => ['文件过大'],
|
|
|
|
|
|
]);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
$extension = UploadFilenameGuard::lastAllowedExtension(
|
|
|
|
|
|
(string) $file->getClientOriginalName(),
|
|
|
|
|
|
$allowedExtensions
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
$relativeDir = trim(str_replace('\\', '/', $directory), '/');
|
|
|
|
|
|
if ($relativeDir === '' || str_contains($relativeDir, '..')) {
|
|
|
|
|
|
throw new RuntimeException('invalid private import directory');
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
$name = Str::uuid()->toString().'.'.$extension;
|
|
|
|
|
|
$stored = Storage::disk('local')->putFileAs($relativeDir, $file, $name);
|
|
|
|
|
|
if ($stored === false) {
|
|
|
|
|
|
throw new RuntimeException('local disk putFileAs returned false');
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return storage_path('app/'.ltrim(str_replace('\\', '/', $stored), '/'));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 仅校验、不落盘(调用方使用 PHP 临时文件,如 Excel 预览)。
|
|
|
|
|
|
*
|
|
|
|
|
|
* @param list<string> $allowedExtensions
|
|
|
|
|
|
*/
|
|
|
|
|
|
public static function assertAcceptable(
|
|
|
|
|
|
UploadedFile $file,
|
|
|
|
|
|
array $allowedExtensions,
|
|
|
|
|
|
int $maxKilobytes
|
|
|
|
|
|
): string {
|
|
|
|
|
|
if (! $file->isValid()) {
|
|
|
|
|
|
throw ValidationException::withMessages([
|
|
|
|
|
|
'file' => ['上传未通过校验:'.$file->getErrorMessage()],
|
|
|
|
|
|
]);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
$size = (int) $file->getSize();
|
|
|
|
|
|
if ($size <= 0 || $size > $maxKilobytes * 1024) {
|
|
|
|
|
|
throw ValidationException::withMessages([
|
|
|
|
|
|
'file' => ['文件过大'],
|
|
|
|
|
|
]);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return UploadFilenameGuard::lastAllowedExtension(
|
|
|
|
|
|
(string) $file->getClientOriginalName(),
|
|
|
|
|
|
$allowedExtensions
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|