diff --git a/app/Console/Commands/MigrateTiledImages.php b/app/Console/Commands/MigrateTiledImages.php index 72ff13dac..d4a6f3e92 100644 --- a/app/Console/Commands/MigrateTiledImages.php +++ b/app/Console/Commands/MigrateTiledImages.php @@ -40,7 +40,8 @@ public function handle() $query->eachById(function (Image $image) use ($dryRun, $bar, $disk) { if (!$dryRun) { - Queue::push(new MigrateTiledImage($image, $disk)); + $targetPath = fragment_uuid_path($image->uuid); + Queue::push(new MigrateTiledImage($image, $disk, $targetPath)); } $bar->advance(); }); diff --git a/app/Jobs/MigrateTiledImage.php b/app/Jobs/MigrateTiledImage.php index 2d8600bb6..7edf2d270 100644 --- a/app/Jobs/MigrateTiledImage.php +++ b/app/Jobs/MigrateTiledImage.php @@ -24,9 +24,9 @@ class MigrateTiledImage extends TileSingleImage * * @return void */ - public function __construct(Image $image, $disk) + public function __construct(Image $image, string $disk, string $targetPath) { - parent::__construct($image); + parent::__construct($image, $disk, $targetPath); $this->disk = $disk; } @@ -38,9 +38,8 @@ public function __construct(Image $image, $disk) public function handle() { try { - $fragment = fragment_uuid_path($this->image->uuid); $tmpResource = tmpfile(); - $zipResource = Storage::disk($this->disk)->readStream($fragment); + $zipResource = Storage::disk($this->disk)->readStream($this->targetPath); stream_copy_to_stream($zipResource, $tmpResource); $zip = new ZipArchive; $zip->open(stream_get_meta_data($tmpResource)['uri']); diff --git a/app/Jobs/ProcessNewImage.php b/app/Jobs/ProcessNewImage.php index 7b141e1f9..f5821d3ad 100644 --- a/app/Jobs/ProcessNewImage.php +++ b/app/Jobs/ProcessNewImage.php @@ -374,7 +374,8 @@ protected function submitTileJob(Image $image) $image->tiled = true; $image->tilingInProgress = true; $image->save(); - TileSingleImage::dispatch($image); + $targetPath = fragment_uuid_path($image->uuid); + TileSingleImage::dispatch($image, config('image.tiles.disk'), $targetPath); } /** diff --git a/app/Jobs/TileSingleImage.php b/app/Jobs/TileSingleImage.php index 70b16070e..0f03ed8d9 100644 --- a/app/Jobs/TileSingleImage.php +++ b/app/Jobs/TileSingleImage.php @@ -3,54 +3,32 @@ namespace Biigle\Jobs; use Biigle\Image; -use Exception; -use File; +use File as FileFacade; use FileCache; -use FilesystemIterator; -use Illuminate\Contracts\Queue\ShouldQueue; -use Illuminate\Queue\InteractsWithQueue; -use Illuminate\Queue\SerializesModels; -use Illuminate\Support\Facades\Storage; -use Jcupitt\Vips\Image as VipsImage; -use RecursiveDirectoryIterator; -use RecursiveIteratorIterator; -class TileSingleImage extends Job implements ShouldQueue +class TileSingleImage extends TileSingleObject { - use InteractsWithQueue, SerializesModels; - /** * The image to generate tiles for. * * @var Image */ - public $image; - - /** - * Path to the temporary storage file for the tiles. - * - * @var string - */ - public $tempPath; - - /** - * Ignore this job if the image does not exist any more. - * - * @var bool - */ - protected $deleteWhenMissingModels = true; + public $file; /** * Create a new job instance. * - * @param Image $image The image to generate tiles for. + * @param Image $file The image to generate tiles for. + * @param string $storage The path to storage-disk where the tiles should be stored + * @param string $targetPath The path to the tiles within the permanent storage-disk * * @return void */ - public function __construct(Image $image) + public function __construct(Image $file, string $storage, string $targetPath) { - $this->image = $image; - $this->tempPath = config('image.tiles.tmp_dir')."/{$image->uuid}"; + parent::__construct($storage, $targetPath); + $this->file = $file; + $this->tempPath = config('image.tiles.tmp_dir')."/{$file->uuid}"; $this->queue = config('image.tiles.queue'); } @@ -62,78 +40,12 @@ public function __construct(Image $image) public function handle() { try { - FileCache::getOnce($this->image, [$this, 'generateTiles']); + FileCache::getOnce($this->file, [$this, 'generateTiles']); $this->uploadToStorage(); - $this->image->tilingInProgress = false; - $this->image->save(); + $this->file->tilingInProgress = false; + $this->file->save(); } finally { - File::deleteDirectory($this->tempPath); - } - } - - /** - * Generate tiles for the image and put them to temporary storage. - * - * @param Image $image - * @param string $path Path to the cached image file. - */ - public function generateTiles(Image $image, $path) - { - $this->getVipsImage($path)->dzsave($this->tempPath, [ - 'layout' => 'zoomify', - 'container' => 'fs', - 'strip' => true, - ]); - } - - /** - * Upload the tiles from temporary local storage to the tiles storage disk. - */ - public function uploadToStorage() - { - // +1 for the connecting slash. - $prefixLength = strlen($this->tempPath) + 1; - $iterator = $this->getIterator($this->tempPath); - $disk = Storage::disk(config('image.tiles.disk')); - $fragment = fragment_uuid_path($this->image->uuid); - try { - foreach ($iterator as $pathname => $fileInfo) { - $disk->putFileAs($fragment, $fileInfo, substr($pathname, $prefixLength)); - } - } catch (Exception $e) { - $disk->deleteDirectory($fragment); - throw $e; + FileFacade::deleteDirectory($this->tempPath); } } - - /** - * Get the vips image instance. - * - * @param string $path - * - * @return \Jcupitt\Vips\Image - */ - protected function getVipsImage($path) - { - return VipsImage::newFromFile($path, ['access' => 'sequential']); - } - - /** - * Get the recursive directory iterator for the given path. - * - * @param string $path - * - * @return RecursiveIteratorIterator - */ - protected function getIterator($path) - { - return new RecursiveIteratorIterator( - new RecursiveDirectoryIterator( - $path, - FilesystemIterator::KEY_AS_PATHNAME | - FilesystemIterator::CURRENT_AS_FILEINFO | - FilesystemIterator::SKIP_DOTS - ) - ); - } } diff --git a/app/Jobs/TileSingleObject.php b/app/Jobs/TileSingleObject.php new file mode 100644 index 000000000..16073f1f5 --- /dev/null +++ b/app/Jobs/TileSingleObject.php @@ -0,0 +1,133 @@ +storage = $storage; + $this->targetPath = $targetPath; + } + + /** + * Execute the job. + * + * @return void + */ + abstract protected function handle(); + + /** + * Generate tiles for the object and put them to temporary storage. + * + * @param File $file + * @param string $path Path to the cached image file. + */ + public function generateTiles($file, $path) + { + $this->getVipsImage($path)->dzsave($this->tempPath, [ + 'layout' => 'zoomify', + 'container' => 'fs', + 'strip' => true, + ]); + } + + /** + * Upload the tiles from temporary local storage to the tiles storage disk. + */ + public function uploadToStorage() + { + // +1 for the connecting slash. + $prefixLength = strlen($this->tempPath) + 1; + $iterator = $this->getIterator($this->tempPath); + $disk = Storage::disk($this->storage); + try { + foreach ($iterator as $pathname => $fileInfo) { + $disk->putFileAs($this->targetPath, $fileInfo, substr($pathname, $prefixLength)); + } + } catch (Exception $e) { + $disk->deleteDirectory($this->targetPath); + throw $e; + } + } + + /** + * Get the vips image instance. + * + * @param string $path + * + * @return \Jcupitt\Vips\Image + */ + protected function getVipsImage($path) + { + return VipsImage::newFromFile($path, ['access' => 'sequential']); + } + + /** + * Get the recursive directory iterator for the given path. + * + * @param string $path + * + * @return RecursiveIteratorIterator + */ + protected function getIterator($path) + { + return new RecursiveIteratorIterator( + new RecursiveDirectoryIterator( + $path, + FilesystemIterator::KEY_AS_PATHNAME | + FilesystemIterator::CURRENT_AS_FILEINFO | + FilesystemIterator::SKIP_DOTS + ) + ); + } +} diff --git a/tests/php/Jobs/ProcessNewImageTest.php b/tests/php/Jobs/ProcessNewImageTest.php index 5b820a0db..4b16bb9fc 100644 --- a/tests/php/Jobs/ProcessNewImageTest.php +++ b/tests/php/Jobs/ProcessNewImageTest.php @@ -149,7 +149,7 @@ public function testHandleTileLargeImage() Queue::fake(); $job->handle(); - Queue::assertPushed(TileSingleImage::class, fn ($job) => $job->image->id === $image->id); + Queue::assertPushed(TileSingleImage::class, fn ($job) => $job->file->id === $image->id); $image->refresh(); $this->assertTrue($image->tiled); $this->assertTrue($image->tilingInProgress); diff --git a/tests/php/Jobs/TileSingleImageTest.php b/tests/php/Jobs/TileSingleImageTest.php index ace04048a..040c9cd70 100644 --- a/tests/php/Jobs/TileSingleImageTest.php +++ b/tests/php/Jobs/TileSingleImageTest.php @@ -15,7 +15,9 @@ class TileSingleImageTest extends TestCase public function testGenerateTiles() { $image = ImageTest::create(); - $job = new TileSingleImageStub($image); + Storage::fake('tiles'); + $targetPath = fragment_uuid_path($image->uuid); + $job = new TileSingleImageStub($image, config('image.tiles.disk'), $targetPath); $mock = Mockery::mock(Image::class); $mock->shouldReceive('dzsave') @@ -35,26 +37,44 @@ public function testUploadToStorage() { config(['image.tiles.disk' => 'tiles']); $image = ImageTest::create(); - $fragment = fragment_uuid_path($image->uuid); - $job = new TileSingleImageStub($image); + $targetPath = fragment_uuid_path($image->uuid); + $job = new TileSingleImageStub($image, config('image.tiles.disk'), $targetPath); File::makeDirectory($job->tempPath); File::put("{$job->tempPath}/test.txt", 'test'); try { Storage::fake('tiles'); $job->uploadToStorage(); - Storage::disk('tiles')->assertExists($fragment); - Storage::disk('tiles')->assertExists("{$fragment}/test.txt"); + Storage::disk('tiles')->assertExists($targetPath); + Storage::disk('tiles')->assertExists("{$targetPath}/test.txt"); } finally { File::deleteDirectory($job->tempPath); } } + public function testHandleFunction() + { + $image = ImageTest::create(); + $image->tilingInProgress = true; + $targetPath = fragment_uuid_path($image->uuid); + $job = new TileSingleImage($image, config('image.tiles.disk'), $targetPath); + + $this->assertEquals($job->file->tilingInProgress, true); + Storage::fake('tiles'); + // execute the job with handle() method + $job->handle(); + $this->assertEquals($job->file->tilingInProgress, false); + Storage::disk('tiles')->assertExists($targetPath); + // check that temporary storage path got properly deleted in handle() method + Storage::disk('tiles')->assertMissing($job->tempPath); + } + public function testQueue() { config(['image.tiles.queue' => 'myqueue']); $image = ImageTest::create(); - $job = new TileSingleImageStub($image); + $targetPath = fragment_uuid_path($image->uuid); + $job = new TileSingleImageStub($image, config('image.tiles.disk'), $targetPath); $this->assertSame('myqueue', $job->queue); } }