fix(media): copy and delete temp file when saving instead of moving it for FS FileManager

+ throw exception instead silently failing file save

closes #338
This commit is contained in:
Yassine Doghri 2023-08-06 12:14:05 +00:00
parent 0775add678
commit 9346e787bd
10 changed files with 59 additions and 57 deletions

View File

@ -13,6 +13,7 @@ namespace Modules\Media\Entities;
use CodeIgniter\Entity\Entity;
use CodeIgniter\Files\File;
use Modules\Media\Models\MediaModel;
use RuntimeException;
/**
* @property int $id
@ -97,15 +98,13 @@ class BaseMedia extends Entity
return $this;
}
public function saveFile(): bool
public function saveFile(): void
{
if (! $this->attributes['file'] || ! $this->file_key) {
return false;
throw new RuntimeException("'file' and 'file_key' attributes must be set before saving a file.");
}
$this->attributes['file_key'] = service('file_manager')->save($this->attributes['file'], $this->file_key);
return true;
}
public function deleteFile(): bool
@ -128,6 +127,7 @@ class BaseMedia extends Entity
if (! service('file_manager')->rename($this->file_key, $newFileKey)) {
$db->transRollback();
return false;
}

View File

@ -94,14 +94,14 @@ class Image extends BaseMedia
return $this;
}
public function saveFile(): bool
public function saveFile(): void
{
if ($this->attributes['sizes'] !== []) {
$this->initFileProperties();
$this->saveSizes();
}
return parent::saveFile();
parent::saveFile();
}
public function deleteFile(): bool

View File

@ -11,6 +11,7 @@ declare(strict_types=1);
namespace Modules\Media\Entities;
use CodeIgniter\Files\File;
use Exception;
use Modules\Media\TranscriptParser;
class Transcript extends BaseMedia
@ -53,11 +54,11 @@ class Transcript extends BaseMedia
return $this;
}
public function saveFile(): bool
public function saveFile(): void
{
$this->saveJsonTranscript();
return parent::saveFile();
parent::saveFile();
}
public function deleteFile(): bool
@ -73,19 +74,18 @@ class Transcript extends BaseMedia
return true;
}
private function saveJsonTranscript(): bool
private function saveJsonTranscript(): void
{
$srtContent = file_get_contents($this->file->getRealPath());
$transcriptParser = new TranscriptParser();
if ($srtContent === false) {
return false;
throw new Exception('Could not read transcript file at ' . $this->file->getRealPath());
}
if (! $transcriptJson = $transcriptParser->loadString($srtContent)->parseSrt()) {
return false;
}
$transcriptJson = $transcriptParser->loadString($srtContent)
->parseSrt();
$tempFilePath = WRITEPATH . 'uploads/' . $this->file->getRandomName();
file_put_contents($tempFilePath, $transcriptJson);
@ -94,7 +94,5 @@ class Transcript extends BaseMedia
service('file_manager')
->save($newTranscriptJson, $this->json_key);
return true;
}
}

View File

@ -20,32 +20,32 @@ class FS implements FileManagerInterface
/**
* Saves a file to the corresponding folder in `public/media`
*/
public function save(File $file, string $path): string | false
public function save(File $file, string $key): string
{
helper('media');
if ((pathinfo($path, PATHINFO_EXTENSION) === '') && (($extension = $file->getExtension()) !== '')) {
$path = $path . '.' . $extension;
}
$mediaRoot = $this->media_path_absolute();
$path = $mediaRoot . '/' . $key;
if (! file_exists(dirname($mediaRoot . '/' . $path))) {
mkdir(dirname($mediaRoot . '/' . $path), 0777, true);
if (! file_exists(dirname($path))) {
mkdir(dirname($path), 0777, true);
}
if (! file_exists(dirname($mediaRoot . '/' . $path) . '/index.html')) {
touch(dirname($mediaRoot . '/' . $path) . '/index.html');
if (! file_exists(dirname($path) . '/index.html')) {
touch(dirname($path) . '/index.html');
}
try {
// move to media folder, overwrite file if already existing
$file->move($mediaRoot . '/', $path, true);
} catch (Exception) {
return false;
// copy to media folder, overwrite file if already existing
$isCopySuccessful = copy($file->getRealPath(), $path);
if (! $isCopySuccessful) {
throw new Exception("Could not save file {$key} to {$path}");
}
return $path;
// delete temporary file after copy
unlink($file->getRealPath());
return $key;
}
public function delete(string $key): bool

View File

@ -9,7 +9,7 @@ use CodeIgniter\HTTP\Response;
interface FileManagerInterface
{
public function save(File $file, string $key): string | false;
public function save(File $file, string $key): string;
public function delete(string $key): bool;

View File

@ -30,19 +30,15 @@ class S3 implements FileManagerInterface
]);
}
public function save(File $file, string $key): string|false
public function save(File $file, string $key): string
{
try {
$this->s3->putObject([
'Bucket' => $this->config->s3['bucket'],
'Key' => $this->prefixKey($key),
'SourceFile' => $file,
'ContentType' => $file->getMimeType(),
'CacheControl' => 'max-age=' . YEAR,
]);
} catch (Exception) {
return false;
}
$this->s3->putObject([
'Bucket' => $this->config->s3['bucket'],
'Key' => $this->prefixKey($key),
'SourceFile' => $file,
'ContentType' => $file->getMimeType(),
'CacheControl' => 'max-age=' . YEAR,
]);
// delete file after storage in s3
unlink($file->getRealPath());

View File

@ -117,12 +117,13 @@ class MediaModel extends Model
public function saveMedia(object $media): int | false
{
// save file first
if (! $media->saveFile()) {
return false;
}
$media->saveFile();
// insert record in database
if (! $mediaId = $this->insert($media, true)) {
/** @var int|false $mediaId */
$mediaId = $this->insert($media, true);
if (! $mediaId) {
$this->db->transRollback();
return false;
@ -137,10 +138,10 @@ class MediaModel extends Model
public function updateMedia(object $media): bool
{
// save file first
if (! $media->saveFile()) {
return false;
}
// FIXME: what if file is not set?
$media->saveFile();
// update record in database
return $this->update($media->id, $media);
}

View File

@ -12,6 +12,7 @@ declare(strict_types=1);
namespace Modules\Media;
use Exception;
use stdClass;
class TranscriptParser
@ -27,8 +28,10 @@ class TranscriptParser
/**
* Adapted from: https://stackoverflow.com/a/11659306
*
* @return string Returns the json encoded string
*/
public function parseSrt(): string | false
public function parseSrt(): string
{
if (! defined('SRT_STATE_SUBNUMBER')) {
define('SRT_STATE_SUBNUMBER', 0);
@ -98,7 +101,13 @@ class TranscriptParser
$subs[] = $sub;
}
return json_encode($subs, JSON_PRETTY_PRINT);
$jsonString = json_encode($subs, JSON_PRETTY_PRINT);
if (! $jsonString) {
throw new Exception('Failed to parse SRT to JSON.');
}
return $jsonString;
}
private function getSecondsFromTimeString(string $timeString): float

View File

@ -46,8 +46,6 @@ class PodcastImport extends BaseCommand
public function init(): void
{
CLI::clearScreen();
helper('podcast_import');
$importQueue = get_import_tasks();
@ -97,9 +95,9 @@ class PodcastImport extends BaseCommand
public function run(array $params): void
{
try {
$this->init();
$this->init();
try {
CLI::write('All good! Feed was parsed successfully!');
CLI::write(

View File

@ -69,7 +69,7 @@ class Publish extends BaseCommand
$request->post($hub, $requestOptions);
} catch (Exception $exception) {
log_message(
'critical',
'warning',
"COULD NOT PUBLISH @{$podcast->handle} ON {$hub}" . PHP_EOL . $exception->getMessage()
);
}