{"id":15080711,"url":"https://github.com/68publishers/image-storage","last_synced_at":"2026-03-04T18:02:10.723Z","repository":{"id":34924410,"uuid":"191010984","full_name":"68publishers/image-storage","owner":"68publishers","description":"🌆 Extension for 68publishers/file-storage that can generate images on-the-fly and more!","archived":false,"fork":false,"pushed_at":"2026-02-23T07:48:17.000Z","size":416,"stargazers_count":1,"open_issues_count":1,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2026-02-23T15:52:15.528Z","etag":null,"topics":["bundle","extension","image","image-storage","nette","nette-extension","php"],"latest_commit_sha":null,"homepage":"","language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/68publishers.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null},"funding":{"custom":["https://www.buymeacoffee.com/68publishers"]}},"created_at":"2019-06-09T13:39:21.000Z","updated_at":"2026-02-23T07:45:01.000Z","dependencies_parsed_at":"2025-05-14T00:34:26.355Z","dependency_job_id":"614d86d0-3bc3-4645-aa08-4073c14d12c1","html_url":"https://github.com/68publishers/image-storage","commit_stats":{"total_commits":73,"total_committers":2,"mean_commits":36.5,"dds":0.0273972602739726,"last_synced_commit":"ac10114e27d215fd314ed537acadc2f4229f9aec"},"previous_names":[],"tags_count":42,"template":false,"template_full_name":null,"purl":"pkg:github/68publishers/image-storage","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/68publishers%2Fimage-storage","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/68publishers%2Fimage-storage/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/68publishers%2Fimage-storage/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/68publishers%2Fimage-storage/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/68publishers","download_url":"https://codeload.github.com/68publishers/image-storage/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/68publishers%2Fimage-storage/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30088336,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-04T15:40:14.053Z","status":"ssl_error","status_checked_at":"2026-03-04T15:40:13.655Z","response_time":59,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["bundle","extension","image","image-storage","nette","nette-extension","php"],"created_at":"2024-09-25T05:05:27.101Z","updated_at":"2026-03-04T18:02:10.715Z","avatar_url":"https://github.com/68publishers.png","language":"PHP","funding_links":["https://www.buymeacoffee.com/68publishers"],"categories":[],"sub_categories":[],"readme":"\u003ch1 align=\"center\"\u003eImage Storage\u003c/h1\u003e\n\n\u003cp align=\"center\"\u003e:city_sunset: Extension for \u003ca href=\"https://github.com/68publishers/file-storage\"\u003e68publishers/file-storage\u003c/a\u003e that can generate images on-the-fly and more!\u003c/p\u003e\n\u003cp align=\"center\"\u003eBased on \u003ca href=\"https://github.com/thephpleague/flysystem\"\u003ethephpleague/flysystem\u003c/a\u003e and \u003ca href=\"https://github.com/Intervention/image\"\u003eintervention/image\u003c/a\u003e\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n\u003ca href=\"https://github.com/68publishers/image-storage/actions\"\u003e\u003cimg alt=\"Checks\" src=\"https://badgen.net/github/checks/68publishers/image-storage/master\"\u003e\u003c/a\u003e\n\u003ca href=\"https://coveralls.io/github/68publishers/image-storage?branch=master\"\u003e\u003cimg alt=\"Coverage Status\" src=\"https://coveralls.io/repos/github/68publishers/image-storage/badge.svg?branch=master\"\u003e\u003c/a\u003e\n\u003ca href=\"https://packagist.org/packages/68publishers/image-storage\"\u003e\u003cimg alt=\"Total Downloads\" src=\"https://badgen.net/packagist/dt/68publishers/image-storage\"\u003e\u003c/a\u003e\n\u003ca href=\"https://packagist.org/packages/68publishers/image-storage\"\u003e\u003cimg alt=\"Latest Version\" src=\"https://badgen.net/packagist/v/68publishers/image-storage\"\u003e\u003c/a\u003e\n\u003ca href=\"https://packagist.org/packages/68publishers/image-storage\"\u003e\u003cimg alt=\"PHP Version\" src=\"https://badgen.net/packagist/php/68publishers/image-storage\"\u003e\u003c/a\u003e\n\u003c/p\u003e\n\n## Installation\n\nThe best way to install 68publishers/image-storage is using Composer:\n\n```sh\n$ composer require 68publishers/image-storage\n```\n\n## Integration into Nette Framework\n\nFirstly, please read a documentation of [68publishers/file-storage](https://github.com/68publishers/file-storage/blob/master/README.md).\n\n### File storage configuration example\n\nEach image-storage is based on file-storage. so firstly we need to register our storage under the file-storage extension.\nHere is an example configuration:\n\n```neon\n68publishers.file_storage:\n    storages:\n        local:\n            config:\n                base_path: /images\n                signature_key: my-arbitrary-private-key\n                allowed_pixel_density: [ 1, 2, 3 ]\n                allowed_resolutions: [ 50x50, 200x200, 300x300, 200x, x200 ]\n                allowed_qualities: [ 50, 80, 100 ]\n                encode_quality: 90\n                cache_max_age: 31536000\n            filesystem:\n                adapter: League\\Flysystem\\Local\\LocalFilesystemAdapter(%wwwDir%/images)\n            assets:\n                assets/image/noimage.png: noimage/default.png # copy the default no-image\n                assets/image/noimage_user.png: noimage/user.png # copy the default no-image for users\n```\n\n#### Storage config options\n\n| Name                     | Type                       | Default    | Description                                                                                                                                                        |\n|--------------------------|----------------------------|------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------|\n| base_path                | string                     | `''`       | Base path to a directory where the files are accessible.                                                                                                           |\n| host                     | null or string             | `null`     | Hostname, use if the files are not stored locally or if you want to generate an absolute links                                                                     |\n| version_parameter_name   | string                     | `_v`       | A query parameter's name used for a file's version (just for a cache).                                                                                             |\n| signature_parameter_name | string                     | `_s`       | A query parameter's name used for a signature token.                                                                                                               |\n| signature_key            | null or string             | `null`     | Your private signature key used for a token encryption. Signatures in requests are checked and validated only if this parameter is set.                            |\n| signature_algorithm      | string                     | `sha256`   | An algorithm used for encryption of signatures (HMAC).                                                                                                             |\n| modifier_separator       | string                     | `,`        | A separator for modifier definitions in a path. For example if you set this parameter as `;` then a modifier string in a path will look like this: `w:100;o:auto`. |\n| modifier_assigner        | string                     | `:`        | An assigner for modifier definitions in a path. For example if you set this parameter as `=` then a modifier string in a path will look like this: `w=100,o=auto`. |\n| allowed_pixel_density    | array\u003cint\u003e or array\u003cfloat\u003e | `[]`       | An array of allowed pixed densities. The validation is enabled when the array is not empty.                                                                        |\n| allowed_resolutions      | array\u003cstring\u003e              | `[]`       | An array of allowed resolutions like `100x`, `x200` or `100x200`. The validation is enabled when the array is not empty.                                           |\n| allowed_qualities        | array\u003cint\u003e                 | `[]`       | An array of allowed qualities. The validation is enabled when the array is not empty.                                                                              |\n| encode_quality           | int                        | `90`       | An encode quality for cached images.                                                                                                                               |\n| cache_max_age            | int                        | `31536000` | The maximum cache age in seconds. The value is used for HTTP headers Cache-Control and Expires.                                                                    |\n\n### Image storage configuration example\n\nNow we can register the `ImageStorageExtension` and define the `local` image-storage:\n\n```neon\nextensions:\n    68publishers.image_storage: SixtyEightPublishers\\ImageStorage\\Bridge\\Nette\\DI\\ImageStorageExtension\n\n68publishers.image_storage:\n    driver: gd # \"gd\" or \"imagick\" or \"68publishers.imagick\", the default is \"gd\"\n    storages:\n        local:\n            source_filesystem:\n                adapter: League\\Flysystem\\Local\\LocalFilesystemAdapter(%appDir%/../private-data/images)\n                config: [] # an optional config for source filesystem adapter\n            server: local # \"local\" or \"external\", the default is \"local\"\n            route: yes # registers automatically ImageServer presenter into your Router. The option can be applied only if the \"server\" option is set to \"local\" and the option \"base_path\" is set in the FileStorage config\n            no_image:\n                default: noimage/default.png\n                user: noimage/user.png\n            no_image_patterns:\n                user: '^user_avatar\\/' # the noimage \"user\" will be used for missing files with paths that matches this regex\n            presets:\n                my_preset:\n                    modifiers:\n                      w: 150\n                      ar: '2x1.5'\n                my_preset_2:\n                    modifiers:\n                        ar: 1x2\n                    w: [300, 600, 900]\n                    defaultW: 600\n```\n\n### Animated GIFs\n\nAnimated GIFs are not supported by [intervention/image](https://github.com/Intervention/image) but this package comes with a custom `imagick` driver that supports it.\nThe driver is used when you pass a value `68publishers.imagick` into a `driver` option.\n\n### Basic usage\n\nBasic usage is similar to usage of the `file-storage`.\n\n#### Persisting files\n\nFiles persisting is almost the same as persisting in the `file-storage` but source images are stored without a file extension.\n\n```php\nuse SixtyEightPublishers\\ImageStorage\\ImageStorageInterface;\n\n/** @var ImageStorageInterface $storage */\n\n# Create resource from local file or url:\n$resource = $storage-\u003ecreateResourceFromFile(\n    $storage-\u003ecreatePathInfo('test/my-image.jpeg'),\n    __DIR__ . '/path/to/my-image.jpeg'\n);\n\n$storage-\u003esave($resource);\n\n# Create resource from a file that is stored in storage:\n$resource = $storage-\u003ecreateResource(\n    $storage-\u003ecreatePathInfo('test/my-image')\n);\n\n# Copy to the new location\n$storage-\u003esave($resource-\u003ewithPathInfo(\n    $storage-\u003ecreatePathInfo('test/my-image-2')\n));\n```\n\n#### Check a file existence\n\n```php\nuse SixtyEightPublishers\\ImageStorage\\ImageStorageInterface;\n\n/** @var ImageStorageInterface $storage */\n\n$pathInfo = $storage-\u003ecreatePathInfo('test/my-image');\n\nif ($storage-\u003eexists($pathInfo)) {\n    echo 'source image exists!';\n}\n\nif ($storage-\u003eexists($pathInfo-\u003ewithModifiers(['w' =\u003e 150]))) {\n    echo 'cached image with width 150 in JPEG (default) format exists!';\n}\n\nif ($storage-\u003eexists($pathInfo-\u003ewithModifiers(['w' =\u003e 150])-\u003ewithExtension('webp'))) {\n    echo 'cached image with width 150 in WEBP format exists!';\n}\n```\n\n#### Deleting files\n\n```php\nuse SixtyEightPublishers\\ImageStorage\\ImageStorageInterface;\nuse SixtyEightPublishers\\ImageStorage\\Persistence\\ImagePersisterInterface;\n\n/** @var ImageStorageInterface $storage */\n\n# delete all cached images only:\n$storage-\u003edelete($storage-\u003ecreatePathInfo('test/my-image'), [\n    ImagePersisterInterface::OPTION_DELETE_CACHE_ONLY =\u003e TRUE,\n]);\n\n# delete cached images and source image:\n$storage-\u003edelete($storage-\u003ecreatePathInfo('test/my-image'));\n\n# delete only cached image with 200px width in PNG format\n$storage-\u003edelete($storage-\u003ecreatePathInfo('test/my-image.png')-\u003ewithModifiers(['w' =\u003e 200]));\n```\n\n#### Create links to images\n\nAn original images are not accessible. If you want to access an original image you must request it with a modifier `['original' =\u003e TRUE]`.\n\n```php\nuse SixtyEightPublishers\\ImageStorage\\ImageStorageInterface;\n\n/** @var ImageStorageInterface $storage */\n\n$pathInfo = $storage-\u003ecreatePathInfo('test/my-image.png')\n    -\u003ewithModifiers(['original' =\u003e TRUE])\n    -\u003ewithVersion(time());\n\n# /images/test/original/my-image.png?_v=1611837352\necho $storage-\u003elink($pathInfo);\n\n# /images/test/original/my-image.webp?_v=1611837352\necho $storage-\u003elink($pathInfo-\u003ewithExtension('webp'));\n\n# /images/test/ar:2x1,w:200/my-image.webp?_v=1611837352\u0026_s={GENERATED_SIGNATURE_TOKEN}\necho $storage-\u003elink($pathInfo-\u003ewithExtension('webp')-\u003ewithModifiers(['w' =\u003e 200, 'ar' =\u003e '2x1']));\n\n# you can also wrap PathInfo to FileInfo object:\n$fileInfo = $storage-\u003ecreateFileInfo($pathInfo);\n\n# /images/test/original/my-image.png?_v=1611837352\necho $fileInfo-\u003elink();\n\n# /images/test/original/my-image.webp?_v=1611837352\necho $fileInfo-\u003ewithExtension('webp')-\u003elink();\n\n# /images/test/ar:2x1,w:200/my-image.webp?_v=1611837352\u0026_s={GENERATED_SIGNATURE_TOKEN}\necho $fileInfo-\u003ewithExtension('webp')-\u003ewithModifiers(['w' =\u003e 200, 'ar' =\u003e '2x1'])-\u003elink();\n```\n\nThe HTML attribute `srcset` can be also generated:\n\n```php\nuse SixtyEightPublishers\\ImageStorage\\ImageStorageInterface;\nuse SixtyEightPublishers\\ImageStorage\\Responsive\\Descriptor\\XDescriptor;\nuse SixtyEightPublishers\\ImageStorage\\Responsive\\Descriptor\\WDescriptor;\n\n/** @var ImageStorageInterface $storage */\n\n$pathInfo = $storage-\u003ecreatePathInfo('test/my-image.png')\n    -\u003ewithModifiers(['w' =\u003e 200, 'ar' =\u003e '2x1'])\n    -\u003ewithVersion(time());\n\n/*\n/images/test/ar:2x1,pd:1,w:200/my-image.png?_v=1611837352\u0026_s={TOKEN} ,\n/images/test/ar:2x1,pd:2,w:200/my-image.png?_v=1611837352\u0026_s={TOKEN} 2.0x,\n/images/test/ar:2x1,pd:3,w:200/my-image.png?_v=1611837352\u0026_s={TOKEN} 3.0x\n*/\necho $storage-\u003esrcSet($pathInfo, new XDescriptor(1, 2, 3));\n\n/*\n/images/test/ar:2x1,w:200/my-image.png?_v=1611837352\u0026_s={TOKEN} 200w,\n/images/test/ar:2x1,w:400/my-image.png?_v=1611837352\u0026_s={TOKEN} 400w,\n/images/test/ar:2x1,w:600/my-image.png?_v=1611837352\u0026_s={TOKEN} 600w,\n/images/test/ar:2x1,w:800/my-image.png?_v=1611837352\u0026_s={TOKEN} 800w\n*/\necho $storage-\u003esrcSet($pathInfo, new WDescriptor(200, 400, 600, 800));\n\n# you can also wrap PathInfo to FileInfo object:\n$fileInfo = $storage-\u003ecreateFileInfo($pathInfo);\n\necho $fileInfo-\u003esrcSet(new XDescriptor(1, 2, 3));\necho $fileInfo-\u003esrcSet(new WDescriptor(200, 400, 600, 800));\n```\n\n### Usage with Latte\n\n```neon\nextensions:\n    68publishers.image_storage.latte: SixtyEightPublishers\\ImageStorage\\Bridge\\Nette\\DI\\ImageStorageLatteExtension\n```\n\nThe extension adds these functions into the Latte:\n\n- `w_descriptor(...)` - a shortcut for `new SixtyEightPublishers\\ImageStorage\\Responsive\\Descriptor\\XDescriptor(...)`\n- `x_descriptor(...)` - a shortcut for `new SixtyEightPublishers\\ImageStorage\\Responsive\\Descriptor\\WDescriptor(...)`\n- `w_descriptor_range(int $min, int $max, int $step)` - a shortcut for `SixtyEightPublishers\\ImageStorage\\Responsive\\Descriptor\\WDescriptor::fromRange($min, $max, $step)`\n- `no_image(?string $noImageName = NULL, ?string $storageName = NULL)` - creates a FilInfo object that contains path to no-image file\n\nBasic usage:\n\n```latte\n{varType SixtyEightPublishers\\ImageStorage\\FileInfoInterface $fileInfo}\n\n{* Note: method FileInfo::__toString() calls ::link() internally *}\n\n\u003cimg src=\"{$fileInfo-\u003elink()}\" alt=\"\"\u003e\n\u003cimg srcset=\"{$fileInfo-\u003esrcSet(x_descriptor(1, 2, 3))}\" src=\"{$fileInfo}\" alt=\"\"\u003e\n\n{* Create FileInfo from string *}\n{var $fileInfo = file_info('test/my-image.png')-\u003ewithModifiers(['o' =\u003e 90, 'ar' =\u003e '2x1'])}\n\n\u003cimg srcset=\"{$fileInfo-\u003esrcSet(w_descriptor(400, 800, 1200))}\" src=\"{$fileInfo}\" alt=\"\"\u003e\n\n{* Create default NoImage if the variable doesn't exists *}\n{var $fileInfo = ($fileInfo ?? no_image())-\u003ewithModifiers(['o' =\u003e 90, 'ar' =\u003e '2x1'])}\n\n\u003cimg srcset=\"{$fileInfo-\u003esrcSet(w_descriptor(400, 800, 1200))}\" src=\"{$fileInfo}\" alt=\"\"\u003e\n```\n\nAn advanced example with a tag `\u003cpicture\u003e`:\n\n```latte\n{var $large = file_info('test/my-image.jpeg')-\u003ewithModifiers([w =\u003e 1172, ar =\u003e '1x0.29'])}\n{var $medium = $large-\u003ewithModifiers([w =\u003e 768, ar =\u003e '1x0.59'])}\n\n\u003cpicture\u003e\n    \u003csource srcset=\"{$large-\u003ewithExt('webp')-\u003esrcSet(w_descriptor_range(768, 1172 * 3, 200))}\" media=\"(min-width: 768px)\" sizes=\"(min-width: 1188px) calc(1188px - 2 * 0.5rem), (min-width: 992px) calc(100vw - 2 * 0.5rem), calc(100vw - 2 * 1.5rem)\" type=\"image/webp\"\u003e\n    \u003csource srcset=\"{$large-\u003esrcSet(w_descriptor_range(768, 1172 * 3, 200))}\" media=\"(min-width: 768px)\" sizes=\"(min-width: 1188px) calc(1188px - 2 * 0.5rem), (min-width: 992px) calc(100vw - 2 * 0.5rem), calc(100vw - 2 * 1.5rem)\"\u003e\n    \u003csource srcset=\"{$medium-\u003ewithExt('webp')-\u003esrcSet(w_descriptor_range(320, 768 * 3, 200))}\" sizes=\"(min-width: 576px) calc(100vw - 2 * 1.5rem), calc(100vw - 2 * 0.5rem)\" type=\"image/webp\"\u003e\n    \u003csource srcset=\"{$medium-\u003esrcSet(w_descriptor_range(320, 768 * 3, 200))}\" sizes=\"(min-width: 576px) calc(100vw - 2 * 1.5rem), calc(100vw - 2 * 0.5rem)\"\u003e\n    \u003cimg src=\"{$large}\" alt=\"\"\u003e\n\u003c/picture\u003e\n```\n\n### Symfony Console commands\n\nThe image-storage extends a command `file-storage:clean` with an option `cache-only` so the command now looks like this:\n\n```bash\n$ bin/console file-storage:clean [\u003cstorage\u003e] [--namespace \u003cvalue\u003e] [--cache-only]\n```\n\n## Supported image formats and modifiers\n\n### Image formats\n\n- JPEG - `.jpeg` or `.jpg`\n- Progressive JPEG - `.pjpg`\n- PNG - `.png`\n- GIF - `.gif`\n- WEBP - `.webp`\n- AVIF - `.avif`\n\n### Modifiers\n\n| Name          | Shortcut | Type              | Note                                                                                                                                                                     |\n|---------------|----------|-------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------|\n| Original      | original | -                 | A modifier without a value, use it if you want to return the original image                                                                                              |\n| Height        | h        | Integer           | Can be restricted by parameter `AllowedResolutions`                                                                                                                      |\n| Width         | w        | Integer           | Can be restricted by parameter `AllowedResolutions`                                                                                                                      |\n| Pixel density | pd       | Integer or Float  | Can be restricted by parameter `AllowedPixelDensity`                                                                                                                     |\n| Aspect ratio  | ar       | String            | Required format is `{Int or Float}x{Int or Float}` and a height or a width (not both) must be also defined. For example `w:200,ar:1x2` is an equivalent of `w:200,h:400` |\n| Fit           | f        | String            | See [supported fits](#supported-fits) for the list of supported values                                                                                                   |\n| Orientation   | o        | Integer or String | Allowed values are `auto, 0, 90, -90, 180, -180, 270, -270`                                                                                                              |\n| Quality       | q        | Integer           | Can be restricted by parameter `AllowedQualities`                                                                                                                        |\n\n### Supported fits\n\n- `contain` - Preserving aspect ratio, resize the image to be as large as possible while ensuring its dimensions are less than or equal to both those specified.\n- `stretch` - Ignore the aspect ratio of the input and stretch to both provided dimensions.\n- `fill` - Preserving aspect ratio, contain within both provided dimensions using \"letterboxing\" where necessary.\n- `crop-*` - Preserving aspect ratio, ensure the image covers both provided dimensions by cropping to fit.\n    - `crop-center`\n    - `crop-left`\n    - `crop-right`\n    - `crop-top`\n    - `crop-top-left`\n    - `crop-top-right`\n    - `crop-bottom`\n    - `crop-bottom-left`\n    - `crop-bottom-right`\n\n## Image server\n\n### Local image server\n\nThe default image server for each storage is `local`. That means your application will handle requests and generate, store and serve modified images.\nThe extension automatically registers [ImageStoragePresenter](src/Bridge/Nette/Application/ImageServerPresenter.php) and Routes for local storages if the `route: true` option is set for the storage.\nIf you have this setting disabled, you must register the Presenter yourself.\n\nNow you must modify the configuration of a web server. For example, if the webserver is Apache then modify a file `.htaccess` that is located in your www directory.\n\n```apacheconf\n# locale images\nRewriteCond %{REQUEST_FILENAME} !-f\nRewriteCond %{REQUEST_FILENAME} !-d\nRewriteRule ^(images\\/)(.+) index.php [L]\n```\n\nThe Application will be called only if a static file has not yet been generated. Otherwise, the server will serve the static file.\n\n### External image server: an integration with AWS S3 and image-storage-lambda\n\nThe image storage can be integrated with the Amazon S3 object storage and the package [68publishers/image-storage-lambda](https://github.com/68publishers/image-storage-lambda). So your image storage can be completely serverless!\nOf course, you can deploy the `image-storage-lambda` application manually and also synchronize options from the `image-storage` with the `image-storage-lambda` manually.\n\nAt least you can follow these simple steps for a partial integration:\n\n1) Create a deployment bucket on the S3\n\nWhen you deploy the AWS SAM application in guide mode (`sam deploy --guided`) the deployment bucket will be created automatically. But the application will be built in a non-guided mode so we must create the bucket manually.\nIf you don't know how to create an S3 bucket please follow the [Amazon documentation](https://docs.aws.amazon.com/AmazonS3/latest/gsg/CreatingABucket.html). We recommend to enable versioning on this bucket.\n\n2) Required packages `league/flysystem-aws-s3-v3` (the S3 adapter for Flysystem) and `yosymfony/toml` (suggested by this package) in your application\n\n```bash\n$ composer require league/flysystem-aws-s3-v3 yosymfony/toml\n```\n\n3) Configure the image storage with the S3 filesystem (an example with a minimal configuration):\n\n```neon\nservices:\n    s3_client:\n        class: Aws\\S3\\S3Client([... your S3 config ...])\n        autowired: no\n\n68publishers.file_storage:\n    storages:\n        s3_images:\n            config:\n                # configure what you want but omit the `host` option for now\n            filesystem:\n                adapter: League\\Flysystem\\AwsS3V3\\AwsS3V3Adapter(@s3_client, my-awesome-cache-bucket) # the bucket doesn't exists at this point\n            # if you have your own no-images:\n            assets:\n                %assetsDir%/noimage: noimage\n\n68publishers.image_storage:\n    storages:\n        s3_images:\n            source_filesystem:\n                adapter: League\\Flysystem\\AwsS3V3\\AwsS3V3Adapter(@s3_client, my-awesome-source-bucket) # the bucket doesn't exists at this point\n            server: external\n            # if you have your own no-images:\n            no_image:\n                default: noimage/default.png\n                user: noimage/user.png\n            no_image_patterns:\n                user: '^user_avatar\\/'\n```\n\n4) Register and configure the compiler extension `ImageStorageLambdaExtension`\n\n```neon\nextensions:\n    68publishers.image_storage.lambda: SixtyEightPublishers\\ImageStorage\\Bridge\\Nette\\DI\\ImageStorageLambdaExtension\n\n68publishers.image_storage.lambda:\n    output_dir: %appDir%/config/image-storage-lambda # the default path\n    stacks:\n        s3_images:\n            s3_bucket: {NAME OF YOUR DEPLOYMENT BUCKET FROM THE STEP 1}\n            region: eu-central-1\n\n            # optional settings:\n            stack_name: my-awesome-image-storage # the storage name is used by default\n            version: 2.0 # default is 1.0\n            s3_prefix: custom-prefix # the stack_name is used by default\n            confirm_changeset: yes # must be changeset manually confirmed during deploy? the default value is false\n            capabilities: CAPABILITY_IAM # default, CAPABILITY_IAM or CAPABILITY_NAMED_IAM\n\n            # optional, automatically detected from AwsS3V3Adapter by default\n            source_bucket_name: source-bucket-name\n            cache_bucket_name: cache-bucket-name\n```\n\n5) Generate configuration for the `image-storage-lambda`\n\n```sh\n$ php bin/console image-storage:lambda:dump-config\n```\n\nThe configuration file will be placed by default in a directory `app/config/image-storage-lambda/my-awesome-image-storage/samconfig.toml`. Keep this file versioned in the Git.\n\n6) Download `image-storage-lambda`, build and deploy!\n\nFirstly setup your local environment by requirements defined [here](https://github.com/68publishers/image-storage-lambda#requirements). Then download the package outside your project.\n\n```sh\n$ git clone https://github.com/68publishers/image-storage-lambda.git image-storage-lambda\n$ cd ./image-storage-lambda\n```\n\nUnfortunately SAM CLI doesn't allow you to define a path to your `samconfig.toml` file (related issue https://github.com/awslabs/aws-sam-cli/issues/1615) at this moment.\nSo you must copy the config to the root of the `image-storage-lambda` application.\nAnd then you can build and deploy the application!\n\n```sh\n$ cp ../my-project/app/config/image-storage-lambda/my-awesome-image-storage/samconfig.toml samconfig.toml\n$ sam build\n$ sam deploy\n```\n\n7) Set the CloudFront URL as a host in the image storage config\n\nThe URL of your CloudFront distribution is listed in Outputs after a successful deployment. More information are [here](https://github.com/68publishers/image-storage-lambda#what-is-the-url-of-my-api).\n\n```neon\n# ...\n68publishers.image_storage:\n    storages:\n        s3_images:\n            config:\n                host: {CLOUDFRONT URL}\n# ...\n```\n\n## Contributing\n\nBefore opening a pull request, please check your changes using the following commands\n\n```sh\n$ make init # to pull and start all docker images\n\n$ make cs.check\n$ make stan\n$ make tests.all\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2F68publishers%2Fimage-storage","html_url":"https://awesome.ecosyste.ms/projects/github.com%2F68publishers%2Fimage-storage","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2F68publishers%2Fimage-storage/lists"}