Auto-formatted by Laravel Pint (default Laravel preset): string concatenation spacing, anonymous class brace placement, constructor body shorthand, import ordering, and assertion indentation.
274 lines
8.6 KiB
PHP
274 lines
8.6 KiB
PHP
<?php
|
|
|
|
namespace App\Services;
|
|
|
|
use App\Jobs\ConvertPowerPointJob;
|
|
use Illuminate\Http\UploadedFile;
|
|
use Illuminate\Support\Facades\Storage;
|
|
use Illuminate\Support\Str;
|
|
use InvalidArgumentException;
|
|
use RecursiveDirectoryIterator;
|
|
use RecursiveIteratorIterator;
|
|
use SplFileInfo;
|
|
use ZipArchive;
|
|
|
|
class FileConversionService
|
|
{
|
|
private const MAX_FILE_SIZE_BYTES = 52428800;
|
|
|
|
private const SUPPORTED_EXTENSIONS = ['png', 'jpg', 'jpeg', 'ppt', 'pptx', 'zip'];
|
|
|
|
private const IMAGE_EXTENSIONS = ['png', 'jpg', 'jpeg'];
|
|
|
|
private const POWERPOINT_EXTENSIONS = ['ppt', 'pptx'];
|
|
|
|
public function convertImage(UploadedFile|string|SplFileInfo $file): array
|
|
{
|
|
$sourcePath = $this->resolvePath($file);
|
|
$extension = $this->resolveExtension($file, $sourcePath);
|
|
$this->assertSupported($extension);
|
|
|
|
if (! in_array($extension, self::IMAGE_EXTENSIONS, true)) {
|
|
throw new InvalidArgumentException('Nur Bilddateien koennen mit convertImage verarbeitet werden.');
|
|
}
|
|
|
|
$this->assertSize($file, $sourcePath);
|
|
|
|
$filename = Str::uuid()->toString().'.jpg';
|
|
$relativePath = 'slides/'.$filename;
|
|
$targetPath = Storage::disk('public')->path($relativePath);
|
|
Storage::disk('public')->makeDirectory('slides');
|
|
$this->ensureDirectory(dirname($targetPath));
|
|
|
|
$manager = $this->createImageManager();
|
|
$canvas = $manager->create(1920, 1080)->fill('000000');
|
|
$image = $manager->read($sourcePath);
|
|
$image->scaleDown(width: 1920, height: 1080);
|
|
$canvas->place($image, 'center');
|
|
$canvas->save($targetPath, quality: 90);
|
|
|
|
$thumbnailPath = $this->generateThumbnail($relativePath);
|
|
|
|
return [
|
|
'filename' => $relativePath,
|
|
'thumbnail' => $thumbnailPath,
|
|
];
|
|
}
|
|
|
|
public function convertPowerPoint(UploadedFile|string|SplFileInfo $file): string
|
|
{
|
|
$sourcePath = $this->resolvePath($file);
|
|
$extension = $this->resolveExtension($file, $sourcePath);
|
|
$this->assertSupported($extension);
|
|
|
|
if (! in_array($extension, self::POWERPOINT_EXTENSIONS, true)) {
|
|
throw new InvalidArgumentException('Nur PPT/PPTX-Dateien sind hier erlaubt.');
|
|
}
|
|
|
|
$this->assertSize($file, $sourcePath);
|
|
|
|
$jobId = Str::uuid()->toString();
|
|
ConvertPowerPointJob::dispatch($sourcePath, $jobId);
|
|
|
|
return $jobId;
|
|
}
|
|
|
|
public function processZip(UploadedFile|string|SplFileInfo $file): array
|
|
{
|
|
$sourcePath = $this->resolvePath($file);
|
|
$extension = $this->resolveExtension($file, $sourcePath);
|
|
$this->assertSupported($extension);
|
|
|
|
if ($extension !== 'zip') {
|
|
throw new InvalidArgumentException('processZip erwartet eine ZIP-Datei.');
|
|
}
|
|
|
|
$this->assertSize($file, $sourcePath);
|
|
|
|
$zip = new ZipArchive;
|
|
if ($zip->open($sourcePath) !== true) {
|
|
throw new InvalidArgumentException('ZIP-Datei konnte nicht geoeffnet werden.');
|
|
}
|
|
|
|
$extractDir = storage_path('app/temp/zip-'.Str::uuid()->toString());
|
|
if (! is_dir($extractDir) && ! mkdir($extractDir, 0775, true) && ! is_dir($extractDir)) {
|
|
throw new InvalidArgumentException('Temporaires ZIP-Verzeichnis konnte nicht erstellt werden.');
|
|
}
|
|
|
|
$zip->extractTo($extractDir);
|
|
$zip->close();
|
|
|
|
$results = [];
|
|
|
|
$iterator = new RecursiveIteratorIterator(
|
|
new RecursiveDirectoryIterator($extractDir, RecursiveDirectoryIterator::SKIP_DOTS)
|
|
);
|
|
|
|
foreach ($iterator as $entry) {
|
|
if (! $entry instanceof SplFileInfo || ! $entry->isFile()) {
|
|
continue;
|
|
}
|
|
|
|
$entryPath = $entry->getRealPath();
|
|
if ($entryPath === false) {
|
|
continue;
|
|
}
|
|
|
|
$entryExtension = $this->extensionFromPath($entryPath);
|
|
if (! in_array($entryExtension, self::SUPPORTED_EXTENSIONS, true)) {
|
|
continue;
|
|
}
|
|
|
|
if (in_array($entryExtension, self::IMAGE_EXTENSIONS, true)) {
|
|
$results[] = $this->convertImage($entryPath);
|
|
|
|
continue;
|
|
}
|
|
|
|
if (in_array($entryExtension, self::POWERPOINT_EXTENSIONS, true)) {
|
|
$results[] = ['job_id' => $this->convertPowerPoint($entryPath)];
|
|
|
|
continue;
|
|
}
|
|
|
|
$results = [...$results, ...$this->processZip($entryPath)];
|
|
}
|
|
|
|
$this->deleteDirectory($extractDir);
|
|
|
|
return $results;
|
|
}
|
|
|
|
public function generateThumbnail(string $path): string
|
|
{
|
|
$absolutePath = str_starts_with($path, DIRECTORY_SEPARATOR)
|
|
? $path
|
|
: Storage::disk('public')->path($path);
|
|
|
|
if (! is_file($absolutePath)) {
|
|
throw new InvalidArgumentException('Datei fuer Thumbnail nicht gefunden.');
|
|
}
|
|
|
|
$filename = pathinfo($absolutePath, PATHINFO_FILENAME).'.jpg';
|
|
$thumbnailRelativePath = 'slides/thumbnails/'.$filename;
|
|
$thumbnailAbsolutePath = Storage::disk('public')->path($thumbnailRelativePath);
|
|
Storage::disk('public')->makeDirectory('slides/thumbnails');
|
|
$this->ensureDirectory(dirname($thumbnailAbsolutePath));
|
|
|
|
$manager = $this->createImageManager();
|
|
$canvas = $manager->create(320, 180)->fill('000000');
|
|
$image = $manager->read($absolutePath);
|
|
$image->scaleDown(width: 320, height: 180);
|
|
$canvas->place($image, 'center');
|
|
$canvas->save($thumbnailAbsolutePath, quality: 85);
|
|
|
|
return $thumbnailRelativePath;
|
|
}
|
|
|
|
private function resolvePath(UploadedFile|string|SplFileInfo $file): string
|
|
{
|
|
if ($file instanceof UploadedFile) {
|
|
$path = $file->getRealPath();
|
|
if ($path === false || ! is_file($path)) {
|
|
throw new InvalidArgumentException('Upload-Datei ist ungueltig.');
|
|
}
|
|
|
|
return $path;
|
|
}
|
|
|
|
if ($file instanceof SplFileInfo) {
|
|
$path = $file->getRealPath();
|
|
if ($path === false) {
|
|
throw new InvalidArgumentException('Dateipfad ist ungueltig.');
|
|
}
|
|
|
|
return $path;
|
|
}
|
|
|
|
if (! is_file($file)) {
|
|
throw new InvalidArgumentException('Datei wurde nicht gefunden.');
|
|
}
|
|
|
|
return $file;
|
|
}
|
|
|
|
private function assertSupported(string $extension): void
|
|
{
|
|
if (! in_array($extension, self::SUPPORTED_EXTENSIONS, true)) {
|
|
throw new InvalidArgumentException('Dateityp wird nicht unterstuetzt.');
|
|
}
|
|
}
|
|
|
|
private function extensionFromPath(string $path): string
|
|
{
|
|
return strtolower((string) pathinfo($path, PATHINFO_EXTENSION));
|
|
}
|
|
|
|
private function resolveExtension(UploadedFile|string|SplFileInfo $file, string $sourcePath): string
|
|
{
|
|
if ($file instanceof UploadedFile) {
|
|
return strtolower($file->getClientOriginalExtension());
|
|
}
|
|
|
|
return $this->extensionFromPath($sourcePath);
|
|
}
|
|
|
|
private function assertSize(UploadedFile|string|SplFileInfo $file, string $resolvedPath): void
|
|
{
|
|
$size = $file instanceof UploadedFile
|
|
? ($file->getSize() ?? 0)
|
|
: (filesize($resolvedPath) ?: 0);
|
|
|
|
if ($size > self::MAX_FILE_SIZE_BYTES) {
|
|
throw new InvalidArgumentException('Datei ist groesser als 50MB.');
|
|
}
|
|
}
|
|
|
|
private function deleteDirectory(string $directory): void
|
|
{
|
|
if (! is_dir($directory)) {
|
|
return;
|
|
}
|
|
|
|
$iterator = new RecursiveIteratorIterator(
|
|
new RecursiveDirectoryIterator($directory, RecursiveDirectoryIterator::SKIP_DOTS),
|
|
RecursiveIteratorIterator::CHILD_FIRST
|
|
);
|
|
|
|
foreach ($iterator as $item) {
|
|
if (! $item instanceof SplFileInfo) {
|
|
continue;
|
|
}
|
|
|
|
if ($item->isDir()) {
|
|
@rmdir($item->getPathname());
|
|
|
|
continue;
|
|
}
|
|
|
|
@unlink($item->getPathname());
|
|
}
|
|
|
|
@rmdir($directory);
|
|
}
|
|
|
|
private function ensureDirectory(string $directory): void
|
|
{
|
|
if (is_dir($directory)) {
|
|
return;
|
|
}
|
|
|
|
if (! mkdir($directory, 0775, true) && ! is_dir($directory)) {
|
|
throw new InvalidArgumentException('Zielverzeichnis konnte nicht erstellt werden.');
|
|
}
|
|
}
|
|
|
|
private function createImageManager(): mixed
|
|
{
|
|
$managerClass = implode('\\', ['Intervention', 'Image', 'ImageManager']);
|
|
$driverClass = implode('\\', ['Intervention', 'Image', 'Drivers', 'Gd', 'Driver']);
|
|
|
|
return new $managerClass(new $driverClass);
|
|
}
|
|
}
|