177 lines
5.4 KiB
PHP
177 lines
5.4 KiB
PHP
<?php
|
|
|
|
namespace App\Services;
|
|
|
|
use App\Services\Storage\FileStorageInterface;
|
|
use App\Services\Storage\LocalStorageDriver;
|
|
use App\Services\Storage\S3StorageDriver;
|
|
use CodeIgniter\HTTP\Files\UploadedFile;
|
|
use CodeIgniter\HTTP\ResponseInterface;
|
|
use Config\Storage;
|
|
|
|
class FileStorageService
|
|
{
|
|
private FileStorageInterface $driver;
|
|
|
|
private Storage $config;
|
|
|
|
public function __construct(?Storage $config = null)
|
|
{
|
|
$this->config = $config ?? config('Storage');
|
|
$this->driver = $this->createDriver($this->config->driver);
|
|
}
|
|
|
|
public function driver(): FileStorageInterface
|
|
{
|
|
return $this->driver;
|
|
}
|
|
|
|
public function isS3(): bool
|
|
{
|
|
return $this->config->driver === 's3';
|
|
}
|
|
|
|
public function resolveKey(string $module, string $subFolder, string $fileName): string
|
|
{
|
|
$module = trim($module, '/');
|
|
$subFolder = trim($subFolder, '/');
|
|
$fileName = ltrim(str_replace('\\', '/', $fileName), '/');
|
|
|
|
if ($subFolder === '') {
|
|
return "uploads/{$module}/{$fileName}";
|
|
}
|
|
|
|
return "uploads/{$module}/{$subFolder}/{$fileName}";
|
|
}
|
|
|
|
public function upload(UploadedFile|string $source, string $key, array $options = []): string
|
|
{
|
|
$storedKey = $this->driver->upload($source, $key, $options);
|
|
|
|
if ($this->config->retainLocal && $this->isS3()) {
|
|
(new LocalStorageDriver($this->config))->upload($source, $key, $options);
|
|
}
|
|
|
|
return $storedKey;
|
|
}
|
|
|
|
public function uploadModuleFile(
|
|
UploadedFile $file,
|
|
string $module,
|
|
string $subFolder = '',
|
|
?string $fileName = null
|
|
): string {
|
|
$fileName = $fileName ?: (time() . '_' . $file->getRandomName());
|
|
|
|
$this->upload($file, $this->resolveKey($module, $subFolder, $fileName));
|
|
|
|
return $fileName;
|
|
}
|
|
|
|
public function put(string $key, string $contents, array $options = []): string
|
|
{
|
|
$storedKey = $this->driver->put($key, $contents, $options);
|
|
|
|
if ($this->config->retainLocal && $this->isS3()) {
|
|
(new LocalStorageDriver($this->config))->put($key, $contents, $options);
|
|
}
|
|
|
|
return $storedKey;
|
|
}
|
|
|
|
public function exists(string $module, string $subFolder, string $fileName): bool
|
|
{
|
|
return $this->driver->exists($this->resolveKey($module, $subFolder, $fileName));
|
|
}
|
|
|
|
public function existsKey(string $key): bool
|
|
{
|
|
return $this->driver->exists($key);
|
|
}
|
|
|
|
public function delete(string $module, string $subFolder, string $fileName): bool
|
|
{
|
|
return $this->driver->delete($this->resolveKey($module, $subFolder, $fileName));
|
|
}
|
|
|
|
public function deleteKey(string $key): bool
|
|
{
|
|
return $this->driver->delete($key);
|
|
}
|
|
|
|
public function read(string $module, string $subFolder, string $fileName): string
|
|
{
|
|
return $this->driver->read($this->resolveKey($module, $subFolder, $fileName));
|
|
}
|
|
|
|
public function readKey(string $key): string
|
|
{
|
|
return $this->driver->read($key);
|
|
}
|
|
|
|
public function download(string $module, string $subFolder, string $fileName): ResponseInterface
|
|
{
|
|
return $this->driver->streamDownload(
|
|
$this->resolveKey($module, $subFolder, $fileName),
|
|
$fileName
|
|
);
|
|
}
|
|
|
|
public function downloadKey(string $key, ?string $downloadName = null): ResponseInterface
|
|
{
|
|
return $this->driver->streamDownload($key, $downloadName ?: basename($key));
|
|
}
|
|
|
|
/**
|
|
* Returns a local filesystem path suitable for pdftotext, Gemini, RAG, etc.
|
|
* For S3, this downloads to a temp file first.
|
|
*/
|
|
public function getLocalPathForProcessing(string $module, string $subFolder, string $fileName): string
|
|
{
|
|
return $this->getLocalPathForKey($this->resolveKey($module, $subFolder, $fileName));
|
|
}
|
|
|
|
public function getLocalPathForKey(string $key): string
|
|
{
|
|
if ($this->config->driver === 'local') {
|
|
return rtrim($this->config->localRoot, '/\\') . DIRECTORY_SEPARATOR . ltrim(str_replace('/', DIRECTORY_SEPARATOR, $key), '/\\');
|
|
}
|
|
|
|
return $this->driver->downloadToTemp($key);
|
|
}
|
|
|
|
public function getTemporaryUrl(string $module, string $subFolder, string $fileName, ?int $ttlSeconds = null): string
|
|
{
|
|
return $this->getTemporaryUrlForKey(
|
|
$this->resolveKey($module, $subFolder, $fileName),
|
|
$ttlSeconds
|
|
);
|
|
}
|
|
|
|
public function getTemporaryUrlForKey(string $key, ?int $ttlSeconds = null): string
|
|
{
|
|
return $this->driver->temporaryUrl($key, $ttlSeconds ?? $this->config->presignedTtl);
|
|
}
|
|
|
|
public function getUrl(string $module, string $subFolder, string $fileName): string
|
|
{
|
|
return $this->driver->url($this->resolveKey($module, $subFolder, $fileName));
|
|
}
|
|
|
|
public function copy(string $fromModule, string $fromSubFolder, string $fromFileName, string $toModule, string $toSubFolder, string $toFileName): bool
|
|
{
|
|
return $this->driver->copy(
|
|
$this->resolveKey($fromModule, $fromSubFolder, $fromFileName),
|
|
$this->resolveKey($toModule, $toSubFolder, $toFileName)
|
|
);
|
|
}
|
|
|
|
private function createDriver(string $driver): FileStorageInterface
|
|
{
|
|
return match ($driver) {
|
|
's3' => new S3StorageDriver($this->config),
|
|
default => new LocalStorageDriver($this->config),
|
|
};
|
|
}
|
|
}
|