tobento / app-media
App media support.
Installs: 39
Dependents: 2
Suggesters: 0
Security: 0
Stars: 0
Watchers: 1
Forks: 0
Open Issues: 0
pkg:composer/tobento/app-media
Requires
- php: >=8.4
- enshrined/svg-sanitize: ^0.22.0
- league/mime-type-detection: ^1.16
- tobento/app: ^2.0
- tobento/app-cache: ^2.0
- tobento/app-console: ^2.0
- tobento/app-event: ^2.0
- tobento/app-file-storage: ^2.0
- tobento/app-http: ^2.0
- tobento/app-logging: ^2.0
- tobento/app-message: ^2.0
- tobento/app-migration: ^2.0
- tobento/app-queue: ^2.0
- tobento/app-view: ^2.0
- tobento/js-cropper: ^1.0
- tobento/service-collection: ^2.0
- tobento/service-filesystem: ^2.0
- tobento/service-icon: ^2.0
- tobento/service-imager: ^2.0
- tobento/service-picture: ^2.0
Requires (Dev)
- phpunit/phpunit: ^12.3
- tobento/app-language: ^2.0
- tobento/app-profiler: ^2.0
- tobento/app-testing: ^2.0
- tobento/app-translation: ^2.0
- tobento/app-user: ^2.0
- tobento/apps: ^2.0
- tobento/service-storage: ^2.0
- vimeo/psalm: ^6.13
README
The App Media package provides a set of features and services for working with media files, including:
- responsive images using the HTML
<picture>element - file display and file download
- image editing for cropping, resizing, and transforming images
- upload validation for validating uploaded files
and more ...
Table of Contents
Getting Started
Add the latest version of the app media project running this command.
composer require tobento/app-media
Requirements
- PHP 8.4 or greater
Documentation
App
Check out the App Skeleton if you are using the skeleton.
You may also check out the App to learn more about the app in general.
Media Boot
The media boot does the following:
- installs and loads the media config
- implements media interfaces
- boots features from media config
use Tobento\App\AppFactory; use Tobento\App\Media\FeaturesInterface; // Create the app $app = new AppFactory()->createApp(); // Add directories: $app->dirs() ->dir(realpath(__DIR__.'/../'), 'root') ->dir(realpath(__DIR__.'/../app/'), 'app') ->dir($app->dir('app').'config', 'config', group: 'config') ->dir($app->dir('root').'public', 'public') ->dir($app->dir('root').'vendor', 'vendor'); // Adding boots $app->boot(\Tobento\App\Media\Boot\Media::class); // Implemented interfaces: $features = $app->get(FeaturesInterface::class); // Run the app $app->run();
Media Config
The configuration for the media is located in the app/config/media.php file at the default App Skeleton config location.
Features
File Feature
This feature provides convenient access to files and file URLs from any supported
file storage.
Its primary purpose is to retrieve file URLs or file objects for internal use within your application.
This feature works with both public and private storages.
Private storages are not intended to generate public URLs.
When using a private storage, the File feature is primarily meant for retrieving
file objects for further processing within your application, not for producing URLs.
Requirements
This feature does not have any requirements.
Install
In the media config file you can configure this feature:
'features' => [ new Feature\File( // define the supported storages which have public urls: supportedStorages: ['images'], // you may throw exeptions if storage or file does not exist for debugging e.g.: throw: true, // false default ), ],
Retrieve Files
To retrieve files, use the storage method from the File::class returning a read-only file storage. The file storage does not throw any exceptions when a file does not exists instead it returns an "empty" file.
use Tobento\App\Media\Feature\File; use Tobento\Service\FileStorage\FileInterface; use Tobento\Service\FileStorage\StorageInterface; $fileUrl = $app->get(File::class) ->storage(storage: 'images') ->file(path: 'path/to/file.jpg') ->url(); $storage = $app->get(File::class)->storage(storage: 'images'); // StorageInterface $file = $storage->file(path: 'path/to/file.jpg'); // FileInterface
By default, the file storage will retrieve only file urls. If you wish to retrieve other file attributes use the file storage with method:
use Tobento\App\Media\Feature\File; $file = $app->get(File::class) ->storage(storage: 'images') ->with('url', 'width', 'height') ->file(path: 'path/to/file.jpg');
If you only want to retrieve a file url, you may prefer to use the url method instead:
use Tobento\App\Media\Feature\File; $file = $app->get(File::class)->url(storage: 'images', path: 'path/to/file.jpg');
Retrieve Files Within Views
Make sure you have booted the View Boot.
Use the view fileStorage method to retrieve file(s) within your views:
$fileUrl = $view->fileStorage(storage: 'images')->file(path: 'path/to/file.jpg')->url();
If you only want to retrieve a file url, you may prefer to use the fileUrl method instead:
$fileUrl = $view->fileUrl(storage: 'images', path: 'path/to/file.jpg');
Using File Display Feature For Urls
If a storage does not support public urls you may use the File Display Feature and in the File Storage Config set the public_url parameter as the route uri configured in the File Display Feature:
'storages' => [ 'files' => [ 'factory' => \Tobento\App\FileStorage\FilesystemStorageFactory::class, 'config' => [ // The location storing the files: 'location' => directory('app').'storage/files/', // Point to the file display feature route uri: 'public_url' => 'https://example.com/media/file/', ], ], ],
File Display Feature
This feature may be used to display a file from a supported file storage, such as an image or PDF, directly in the user's browser.
Requirements
This feature does not have any requirements.
Install
In the media config file you can configure this feature:
'features' => [ new Feature\FileDisplay( // define the supported storages (public-only storages are allowed): supportedStorages: ['images'], // you may change the route uri: routeUri: 'media/file/{storage}/{path*}', // default // you may define a route domain: routeDomain: 'media.example.com', // null is default ), ],
Important
This feature only works with storages of type public.
Private storages will always result in a 404 Not Found response.
Display File
Once installed, files will be publicly accessible by the defined route uri:
https://example.com/media/file/images/path/to/file.jpg
To generate a file url, use the router url method:
use Tobento\Service\Routing\RouterInterface; $router = $app->get(RouterInterface::class); $router->url('media.file.display', ['storage' => 'images', 'path' => 'path/to/file.jpg']);
You may check out the Display And Download Files Using Apps if you want to serve files from a customized app.
File Display Signed Feature
This feature may be used to securely display a file from a supported file storage using signed URLs.
A signed URL ensures that the file can only be accessed when the URL contains a valid cryptographic signature, and optionally an expiration timestamp.
This is ideal for displaying private or protected files such as PDFs, images, or documents that should not be publicly accessible.
Requirements
This feature does not have any requirements.
Install
In the media config file you can configure this feature:
'features' => [ new Feature\FileDisplaySigned( // define the supported storages (private-only storages are allowed): supportedStorages: ['uploads-private'], // you may change the route uri: routeUri: 'media/s/file/{storage}/{path*}', // default // you may define a route domain: routeDomain: 'media.example.com', // null is default ), ],
Important
This feature only works with storages of type private.
Public storages will always result in a 404 Not Found response.
Display File
Once installed, files can only be accessed using a signed URL. Unsigned URLs will always result in a 403 Forbidden response.
A typical signed URL looks like:
https://example.com/media/s/file/uploads-private/path/to/file.pdf/{expires}/{signature}
To generate a signed file URL, use the router url method and call sign:
use Tobento\Service\Routing\RouterInterface; $router = $app->get(RouterInterface::class); $url = $router->url('media.file.display.signed', [ 'storage' => 'private', 'path' => 'path/to/file.pdf', ])->sign();
For additional information on signing options and behavior, visit the Signed URL Generation section.
You may check out the Display And Download Files Using Apps if you want to serve files from a customized app.
File Download Feature
This feature may be used to force downloading a file from a supported file storage in the user's browser.
Requirements
This feature does not have any requirements.
Install
In the media config file you can configure this feature:
'features' => [ new Feature\FileDownload( // define the supported storages (public-only storages are allowed): supportedStorages: ['images'], // you may change the route uri: routeUri: 'media/download/{storage}/{path*}', // default // you may define a route domain: routeDomain: 'media.example.com', // null is default ), ],
Important
This feature only works with storages of type public.
Private storages will always result in a 404 Not Found response.
Download File
Once installed, files will be publicly accessible by the defined route uri:
https://example.com/media/download/images/path/to/file.jpg
To generate a file url, use the router url method:
use Tobento\Service\Routing\RouterInterface; $router = $app->get(RouterInterface::class); $router->url('media.file.download', ['storage' => 'images', 'path' => 'path/to/file.jpg']);
You may check out the Display And Download Files Using Apps if you want to serve files from a customized app.
File Download Signed Feature
This feature may be used to securely download a file from a supported file storage using signed URLs.
A signed URL ensures that the file can only be accessed when the URL contains a valid cryptographic signature, and optionally an expiration timestamp.
This is ideal for downloading private or protected files such as PDFs, images, or documents that should not be publicly accessible.
Requirements
This feature does not have any requirements.
Install
In the media config file you can configure this feature:
'features' => [ new Feature\FileDownloadSigned( // define the supported storages (private-only storages are allowed): supportedStorages: ['uploads-private'], // you may change the route uri: routeUri: 'media/s/download/{storage}/{path*}', // default // you may define a route domain: routeDomain: 'media.example.com', // null is default ), ],
Important
This feature only works with storages of type private.
Public storages will always result in a 404 Not Found response.
Download File
Once installed, files can only be accessed using a signed URL. Unsigned URLs will always result in a 403 Forbidden response.
A typical signed URL looks like:
https://example.com/media/s/download/uploads-private/path/to/file.pdf/{expires}/{signature}
To generate a signed file URL, use the router url method and call sign:
use Tobento\Service\Routing\RouterInterface; $router = $app->get(RouterInterface::class); $url = $router->url('media.file.download.signed', [ 'storage' => 'private', 'path' => 'path/to/file.pdf', ])->sign();
For additional information on signing options and behavior, visit the Signed URL Generation section.
You may check out the Display And Download Files Using Apps if you want to serve files from a customized app.
Icons Feature
This feature may be used to render SVG icons using the Icon Service.
Requirements
This feature does not have any requirements.
Install
In the media config file you can configure this feature:
'features' => [ new Feature\Icons( // Define the directory where to store cached icons: cacheDir: directory('app').'storage/icons/', // You may enable to throw an exception if an icon is not found. // This is useful during development, but in production, you may want to log a message instead (see below). throwIconNotFoundException: true, // default is false ), ],
Render Icons Within Views
To render icons within your views use the view icon method returning an icon implementing the Icon Interface:
<?= $view->icon('edit')->size('m')->label(text: 'Edit') ?>
Access Icons
In addition, to render icons you may just access them within any service:
use Tobento\Service\Icon\IconInterface; use Tobento\Service\Icon\IconsInterface; final class SomeService { public function __construct( private IconsInterface $icons, ) { $icon = $icons->get('edit'); // IconInterface } }
Storing SVG icons
Store your SVG icon files in the app/views/icons/ directory:
app/
views/
icons/
edit.svg
...
Clear cached icons
To clear cached icons you may delete the defined $cacheDir folder manually or run the following command:
php ap icons:clear
During development, if you store more SVG icons, you will need to clear the cache to see the changes!
Log Not Found Icons
Make sure you have booted the App Logging Boot.
In the app/config/logging.php file you may define the logger to be used, otherwise the default logger will be used:
'aliases' => [ \Tobento\App\Media\Icon\FallbackIcons::class => 'daily', // or do not log at all: \Tobento\App\Media\Icon\FallbackIcons::class => 'null', ],
Image Editor Feature
This feature offers a full image-editing interface, making it possible to crop, resize, transform, and adjust images within your application.
Important
The Image Editor loads images in the browser and writes changes back to storage.
For security reasons, access to the editor is protected by a permission check.
By default, the required permission ismedia.image.editor.
You may override this permission, but disabling permission checks is not recommended.
Requirements
This features requires:
composer require tobento/app-language
composer require tobento/app-translation
composer require tobento/app-user
Install
In the media config file you can configure this feature:
'features' => [ new Feature\ImageEditor( // define different image editors templates: templates: [ 'default' => [ 'crop', 'resize', 'fit', // Used for cropping. You may uncomment all if you want to disable cropping. 'background', 'blur', 'brightness', 'contrast', 'colorize', 'flip', 'gamma', 'pixelate', 'rotate', 'sharpen', 'greyscale', 'sepia', // Filters 'quality', 'format', ], ], // define the supported storages: supportedStorages: ['uploads'], // define the supported mime types: supportedMimeTypes: ['image/png', 'image/jpeg', 'image/gif', 'image/webp'], // you may define a custom image actions class: imageActions: \Tobento\App\Media\Image\ImageActions::class, // default // define the user permission or null if no permission is needed (not recommended): userPermission: 'media.image.editor', // default // you may localize routes: localizeRoute: true, // false (default) ), ],
Edit Image
Use the media.image.editor route name to generate the URL where you can edit the specified image.
$url = $router->url('media.image.editor', ['template' => 'default', 'storage' => 'uploads', 'path' => 'image.jpg']);
Events
The Tobento\App\Media\Event\ImageEdited will dispatch after the image is edited.
Picture Feature
This feature may be used to generate HTML markup for responsive images using the HTML picture element.
Workflow
If images are not created yet when you display a picture, a picture job will be sent to the defined queue, generating the images in the background and returning a "fallback" picture from the defined resource. Once, the images are generated, the picture will be displayed with the images generated.
Requirements
This feature does not have any requirements.
Install
In the media config file you can configure this feature:
'features' => [ new Feature\Picture( // Define the storage name where the generated picture metadata is stored. // This storage should be private (not publicly accessible). pictureStorageName: 'picture-data', // Define the storage name where generated images are stored. // This storage must be public (i.e. support URLs) so the images can be displayed. imageStorageName: 'images', // Queue used for generating images in the background: queueName: 'file', ), ],
Make sure you have configured the defined storages in the App File Storage Config.
Make sure you have configured the defined queue in the App Queue Config.
Logging
Make sure you have booted the App Logging Boot.
In the app/config/logging.php file you may define the logger to be used, otherwise the default logger will be used:
'aliases' => [ // Logs if picture generation fails: \Tobento\App\Media\Picture\PictureGenerator::class => 'daily', // or do not log at all: \Tobento\App\Media\Picture\PictureGenerator::class => 'null', // Logs if image action fails: \Tobento\App\Media\Image\ImageActions::class => 'daily', // or do not log at all: \Tobento\App\Media\Image\ImageActions::class => 'null', ],
Display Picture
Within your view file, use the picture method to display a picture based on the given path, resource and definition parameter:
Example Using A Named Definition
You may use named definitions to generate images from. Check out the Picture Definitions section to learn how to add named definitions.
<?= $view->picture( path: 'path/to/image.jpg', resource: 'storage-name', definition: 'name', allowPrivateStorage: false, // default )->imgAttr('alt', 'Alt Text') ?>
About allowPrivateStorage
Use this when the image source is a private storage.
(For most cases, using a public storage as the image source is recommended.)
false(default): private storages are not read and aNullPictureTagis returned.true: allow reading from private storage (useful for backend-only sources such as uploads or protected files).
Depending on your definition this will output:
<picture> <source srcset="https://example.com/path/to/image.webp" type="image/webp"> <source srcset="https://example.com/path/to/image.jpg" type="image/jpeg"> <img src="https://example.com/path/to/image.jpg" alt="Alt Text"> </picture>
Using named definitions have the following advantages:
- you can crop images based on the named definition
- you can have different definitions per view theme
Example Using A Definition
use Tobento\Service\Picture\Definition\ArrayDefinition; <?= $view->picture( path: 'path/to/image.jpg', resource: 'storage-name', definition: new ArrayDefinition('product-main', [ 'img' => [ 'src' => [600], 'alt' => 'Alternative Text', 'loading' => 'lazy', ], // You may define any sources: 'sources' => [ [ 'media' => '(min-width: 800px)', 'srcset' => [ '' => [1200, 500], ], 'type' => 'image/webp', ], [ 'media' => '(max-width: 600px)', 'srcset' => [ '' => [600, 400], ], 'type' => 'image/webp', ], ], ]), ) ?>
Check out the Picture Definition section to learn more about definitions in general.
Example Using An Imager Resource
use Tobento\Service\Imager\ResourceInterface; <?= $view->picture( path: 'path/to/image.jpg', resource: $resource, // ResourceInterface definition: 'name', )->imgAttr('alt', 'Alt Text') ?>
Check out the Picture Creating - Create Picture From Resource section to learn more about.
Picture Definitions
Store your picture definition JSON files in the app/views/picture-definitions/ directory:
app/
views/
picture-definitions/
product-main.json
...
You may check out the Json Files Definitions for more information.
Clearing Generated Picture
Using Console Command
To clear all generated pictures run the following command:
php ap picture:clear
Or clear generated pictures of specific definitions only:
php ap picture:clear --def=product-main --def=post
Using Picture Generator
use Tobento\App\Media\Picture\PictureGeneratorInterface; class SomeService { public function __construct( protected PictureGeneratorInterface $pictureGenerator, ) {} private function deleteGeneratedPictures() { // First, get the picture repository from the generator: $pictureRepository = $this->pictureGenerator->pictureRepository(); // Deletes the created picture with all its created images for the specified path and definition: $pictureRepository->delete( path: 'foo/image.jpg', definition: 'product-main', ); // Deletes all created pictures with all its created images for the specified definition: $pictureRepository->deleteAll( definition: 'product-main', ); // Deletes all created pictures with all its created images for the specified path: $pictureRepository->deleteAllByPath( path: 'foo/image.jpg', ); } }
Picture Editor Feature
This feature provides an interface for editing pictures and their defined variants.
It displays all picture definitions along with their preview images, allowing you to crop, resize, transform, and adjust each variant before regenerating them.
It requires the Picture Feature to be installed.
Important
The Picture Editor loads images in the browser and writes changes back to storage.
For security reasons, access to the editor is protected by a permission check.
By default, the required permission ismedia.picture.editor.
You may override this permission, but disabling permission checks is not recommended.
Requirements
This feature requires:
composer require tobento/app-language
composer require tobento/app-translation
composer require tobento/app-user
Install
In the media config file you can configure this feature:
'features' => [ new Feature\PictureEditor( // define different image editor templates: templates: [ 'default' => [ 'crop', 'resize', 'fit', // Used for cropping. You may uncomment all if you want to disable cropping. 'background', 'blur', 'brightness', 'contrast', 'colorize', 'flip', 'gamma', 'pixelate', 'rotate', 'sharpen', 'greyscale', 'sepia', // Filters 'quality', ], ], // define the supported storages: supportedStorages: ['uploads'], // define the supported mime types: supportedMimeTypes: ['image/png', 'image/jpeg', 'image/gif', 'image/webp'], // you may define a custom image actions class: imageActions: \Tobento\App\Media\Image\ImageActions::class, // default // define the user permission or null if no permission is needed (not recommended): userPermission: 'media.picture.editor', // default // you may localize routes: localizeRoute: true, // false (default) // Images will be generated in the background by the queue by default: queuePictureGeneration: true, // true (default) ), ],
Edit Picture
Use the media.picture.editor route name to generate the url where you can edit the specified picture.
$url = $router->url('media.picture.editor', [ 'template' => 'default', 'storage' => 'uploads', 'path' => 'image.jpg', 'definitions' => ['product', 'product-list'] ]);
Events
The Tobento\App\Media\Event\PictureEdited will be dispatched after the picture is edited.
Services
File Writer
The file writer class writes the given file to the defined File Storage.
use Tobento\App\Media\FileStorage\FileWriter; use Tobento\App\Media\FileStorage\FileWriterInterface; use Tobento\App\Media\FileStorage\Writer; use Tobento\App\Media\Image\ImageProcessor; use Tobento\Service\FileStorage\StorageInterface; $fileWriter = new FileWriter( // Define the file storage where to write the files to: storage: $storage, // StorageInterface // Define how filenames should be handled: filenames: FileWriter::ALNUM, // RENAME, ALNUM, KEEP // Or using a closure for customized filenames: filenames: function (string $filename): string { // customize return $filename; }, // Define how dublicates should be handled: duplicates: FileWriter::RENAME, // RENAME, OVERWRITE, DENY // Define how folders should be handled: folders: FileWriter::ALNUM, // or KEEP // Or using a closure for customized folders: folders: function (string $path): string { // customize return $path; }, // Define the max folder depth limit: folderDepthLimit: 5, // You may add writers handling specific files: writers: [ new Writer\ImageWriter( imageProcessor: new ImageProcessor( actions: [ 'orientate' => [], 'resize' => ['width' => 2000], ], ), ), new Writer\SvgSanitizerWriter(), ], );
You may check out the Image Processor section to learn more about it.
writeFromStream
Use the writeFromStream method to write the given stream to the file storage:
use Psr\Http\Message\StreamInterface; use Tobento\App\Media\Exception\WriteException; use Tobento\App\Media\FileStorage\WriteResponseInterface; $writeResponse = $fileWriter->writeFromStream( stream: $stream, // StreamInterface filename: 'file.txt', folderPath: 'path/to', // or an empty string if no path at all ); var_dump($writeResponse instanceof WriteResponseInterface); // bool(true) // throws WriteException if writing failed!
writeUploadedFile
Use the writeUploadedFile method to write the given uploaded file to the file storage:
use Psr\Http\Message\UploadedFileInterface; use Tobento\App\Media\Exception\WriteException; use Tobento\App\Media\FileStorage\WriteResponseInterface; $writeResponse = $fileWriter->writeUploadedFile( file: $uploadedFile, // UploadedFileInterface folderPath: 'path/to', // or an empty string if no path at all ); var_dump($writeResponse instanceof WriteResponseInterface); // bool(true) // throws WriteException if writing failed!
It is highly recommended to use the Upload Validator before writing the uploaded file to the file storage.
writeResponse
use Psr\Http\Message\UploadedFileInterface; use Tobento\App\Media\FileStorage\WriteResponseInterface; use Tobento\Service\Message\MessagesInterface; $writeResponse = $fileWriter->writeUploadedFile(file: $uploadedFile, folderPath: ''); var_dump($writeResponse instanceof WriteResponseInterface); // bool(true) // Get the path (string) e.g. path/to/file.txt $path = $writeResponse->path(); // Get the content (string|\Stringable): $content = $writeResponse->content(); // Get the original filename (unmodified). Might come from client. $originalFilename = $writeResponse->originalFilename(); // Get the messages: $messages = $writeResponse->messages(); // MessagesInterface
Upload Validator
The upload validator validates the given uploaded file.
use Tobento\App\Media\Upload\Validator; use Tobento\App\Media\Upload\ValidatorInterface; $validator = new Validator( // Define the allowed file extensions: allowedExtensions: ['jpg', 'png', 'gif', 'webp'], // Define if you want to allow only strict filename characters // which are alphanumeric characters, hyphen, spaces, and periods: strictFilenameCharacters: true, // default // Define the max. filename length: maxFilenameLength: 255, // default // You may define the max. file size in bytes or null (unlimited). maxFileSizeInKb: 2000, ); var_dump($validator instanceof ValidatorInterface); // bool(true)
validateUploadedFile
Use the validateUploadedFile method to validate the given uploaded file:
use Psr\Http\Message\UploadedFileInterface; use Tobento\App\Media\Exception\UploadedFileException; try { $validator->validateUploadedFile( file: $uploadedFile, // UploadedFileInterface ); } catch (UploadedFileException $e) { // validation failed. }
Security
The validator validates that
- the file extension and mime type detected by its content is allowed
- the client filename extension, client media type of the file is consistent with its content
- the filename consists only of alphanumeric characters, hyphen, spaces, and periods if
strictFilenameCharactersis set totrue(default) - the filename length does not exceed the defined
maxFilenameLengthparameter - the file size does not exceed the defined
maxFileSizeInKbparameter. Default isnullunlimited
Once the uploaded file is validated and valid, you can be sure that
- the
$uploadedFile->getClientMediaType()is allowed, consistent with its file content and extension - the
$uploadedFile->getClientFilename()file extension is allowed and consistent with its file content
The only thing you have to take care of is the filename except the extension:
$filename = $uploadedFile->getClientFilename(); $extension = pathinfo($filename, PATHINFO_EXTENSION); // is valid as verified
If you use the File Writer to store files, make sure the filenames parameter is configured safely.
use Tobento\App\Media\FileStorage\FileWriter; $fileWriter = new FileWriter( filenames: FileWriter::ALNUM, // or filenames: FileWriter::RENAME, // or filenames: function (string $filename): string { // verify filename! return $verifiedFilename; }, );
File Storage Location
Always store uploaded files outside the webroot or on a different host. If using the File Writer to store files, make sure your defined storage is outside the webroot such as the default configured uploads storage.
Resources
You may read the File Upload Cheatsheet - owasp.org.
Uploaded File Factory
The uploaded file factory creates PSR-7 UploadedFileInterface instances from different resources such as remote URLs or storage files.
use Psr\Http\Client\ClientInterface; use Psr\Http\Message\RequestFactoryInterface; use Psr\Http\Message\StreamFactoryInterface; use Psr\Http\Message\UploadedFileFactoryInterface as Psr17UploadedFileFactoryInterface; use Tobento\App\Media\Upload\UploadedFileFactory; use Tobento\App\Media\Upload\UploadedFileFactoryInterface; $factory = new UploadedFileFactory( uploadedFileFactory: $uploadedFileFactory, // Psr17UploadedFileFactoryInterface streamFactory: $streamFactory, // StreamFactoryInterface client: $client, // ClientInterface (PSR-18) requestFactory: $requestFactory, // RequestFactoryInterface (PSR-17) ); var_dump($factory instanceof UploadedFileFactoryInterface); // bool(true)
createFromRemoteUrl
Creates an uploaded file by downloading the content from a remote URL using a PSR-18 HTTP client.
use Psr\Http\Message\UploadedFileInterface; use Tobento\App\Media\Exception\CreateUploadedFileException; try { $uploadedFile = $factory->createFromRemoteUrl( url: 'https://example.com/image.jpg' // string ); var_dump($uploadedFile instanceof UploadedFileInterface); // bool(true) } catch (CreateUploadedFileException $e) { // creating uploaded file failed. }
If the remote request fails or the response status code is not 200, a CreateUploadedFileException is thrown.
createFromStorageFile
Creates an uploaded file from a Storage File.
use Psr\Http\Message\UploadedFileInterface; use Tobento\App\Media\Exception\CreateUploadedFileException; use Tobento\Service\FileStorage\FileInterface; try { $uploadedFile = $factory->createFromStorageFile( file: $file // FileInterface ); var_dump($uploadedFile instanceof UploadedFileInterface); // bool(true) } catch (CreateUploadedFileException $e) { // creating uploaded file failed. }
If the storage file does not provide a stream, a CreateUploadedFileException is thrown.
Image Processor
The image processor class processes the given image with the defined actions using the Imager Service.
use Tobento\App\Media\Image\ImageProcessor; use Tobento\App\Media\Image\ImageProcessorInterface; use Tobento\Service\Imager\Action; use Tobento\Service\Imager\ActionFactoryInterface; $imageProcessor = new ImageProcessor( // Define the imager actions to be processed: actions: [ 'orientate' => [], 'resize' => ['width' => 300], new Action\Contrast(20), ], // You may define imager actions which are allowed only. // If empty array all are allowed if not in disallowedActions. allowedActions: [ Action\Greyscale::class, ], // You may define imager actions which are not allowed and will be skipped: disallowedActions: [ Action\Colorize::class, ], // You may convert certain images e.g. png to jpeg: convert: ['image/png' => 'image/jpeg'], // You may adjust the image quality: quality: ['image/jpeg' => 90, 'image/webp' => 90], // You may adjust the supported mime types: supportedMimeTypes: ['image/png', 'image/jpeg', 'image/gif'], // default // You may define a custom imager actions class: //actionFactory: $customActionFactory, // ActionFactoryInterface ); var_dump($imageProcessor instanceof ImageProcessorInterface); // bool(true) // Use the following methods to modify the image processor returning a new instance: $imageProcessor = $imageProcessor->withActions([ 'resize' => ['width' => 300], ]); $imageProcessor = $imageProcessor->withConvert([ 'image/png' => 'image/jpeg', ]); $imageProcessor = $imageProcessor->withQuality([ 'image/jpeg' => 90, 'image/webp' => 90, ]);
processFromResource
Use the processFromResource method to process the given resource:
use Tobento\App\Media\Exception\ImageProcessException; use Tobento\Service\Imager\ResourceInterface; use Tobento\Service\Imager\Response\Encoded; $encoded = $imageProcessor->processFromResource( resource: $resource, // ResourceInterface ); var_dump($encoded instanceof Encoded); // bool(true) // throws ImageProcessException if image cannot get processed!
Check out the Resource and Encoded documentation to learn more.
processFromStream
Use the processFromStream method to process the given stream:
use Psr\Http\Message\StreamInterface; use Tobento\App\Media\Exception\ImageProcessException; use Tobento\Service\Imager\Response\Encoded; $encoded = $imageProcessor->processFromStream( stream: $stream, // StreamInterface ); var_dump($encoded instanceof Encoded); // bool(true) // throws ImageProcessException if image cannot get processed!
Check out the Encoded documentation to learn more.
Learn More
Display And Download Files Using Apps
You may use the Apps to create multiple apps, one for your main app and one for displaying and downloading files only:
Once you have created and configured your apps you may
In Media File Display App
use Tobento\Apps\AppBoot; class MediaDisplayApp extends AppBoot { protected const APP_ID = 'media-display'; protected const SLUG = 'app-media'; protected const DOMAINS = ['media.example.com']; }
'features' => [ new Feature\FileDisplay( // define the supported storages: supportedStorages: ['images'], // you may change the route uri: routeUri: '{storage}/{path*}', routeDomain: 'media.example.com', ), ],
In Main App
use Tobento\Apps\AppBoot; class MainApp extends AppBoot { protected const APP_ID = 'main'; protected const SLUG = ''; protected const DOMAINS = ['example.com', 'media.example.com']; }
'features' => [ new Feature\FileDisplay( // define the supported storages: supportedStorages: ['images'], // you may change the route uri: routeUri: '{storage}/{path*}', // you may define a route domain: routeDomain: 'media.example.com', ), ],