📁 File management based on Flysystem with an integration into Nette Framework.
The best way to install 68publishers/file-storage is using Composer:
$ composer require 68publishers/file-storage
With this extension, you can register more storages with different roots, filesystem adapters etc. The first registered storage is also considered as the default storage.
extensions:
68publishers.file_storage: SixtyEightPublishers\FileStorage\Bridge\Nette\DI\FileStorageExtension
68publishers.file_storage:
storages:
default:
config:
base_path: /data/files
filesystem:
adapter: League\Flysystem\Local\LocalFilesystemAdapter(%wwwDir%/data/files)
config: [] # an optional config for filesystem adapter
assets:
path/to/file.png: my/file.png # single file copying
path/to/directory: my-directory # copy whole directory
s3:
config:
host: https://my-bucket.s3.amazonaws.com
filesystem:
adapter: League\Flysystem\AwsS3V3\AwsS3V3Adapter(@s3client, my-bucket)
name | type | default | description |
---|---|---|---|
base_path | string | '' |
Base path to a directory where the files are accessible. |
host | null or string | null |
Hostname, use if the files are not stored locally or if you want to generate an absolute links. |
version_parameter_name | string | _v |
Name of a version parameter in URL. |
Generated DI Container will contain an autowired services of type FileStorageProviderInterface
and FileStorageInterface
(the default storage).
use Nette\DI\Container;
use SixtyEightPublishers\FileStorage\FileStorageInterface;
use SixtyEightPublishers\FileStorage\FileStorageProviderInterface;
/** @var Container $container */
$defaultStorage = $container->getByType(FileStorageInterface::class);
$provider = $container->getByType(FileStorageProviderInterface::class);
$defaultStorage = $provider->get();
# or $defaultStorage = $provider->get('default');
$s3storage = $provider->get('s3');
use SixtyEightPublishers\FileStorage\FileStorageInterface;
/** @var FileStorageInterface $storage */
# Create a resource from file or url:
$resource = $storage->createResourceFromFile(
$storage->createPathInfo('test/invoice.pdf'),
__DIR__ . '/path/to/invoice.pdf'
);
$storage->save($resource);
# Create resource from file that is stored in storage:
$resource = $storage->createResource(
$storage->createPathInfo('test/invoice.pdf')
);
# copy to the new location
$storage->save($resource->withPathInfo(
$storage->createPathInfo('test/invoice-2.pdf')
));
use SixtyEightPublishers\FileStorage\FileStorageInterface;
/** @var FileStorageInterface $storage */
if ($storage->exists($storage->createPathInfo('test/invoice.pdf'))) {
echo 'file exists!';
}
use SixtyEightPublishers\FileStorage\FileStorageInterface;
/** @var FileStorageInterface $storage */
$storage->delete($storage->createPathInfo('test/invoice.pdf'));
use SixtyEightPublishers\FileStorage\FileStorageInterface;
/** @var FileStorageInterface $storage */
# /data/files/test/invoice.pdf
echo $storage->link($storage->createPathInfo('test/invoice.pdf'));
# or
$fileInfo = $storage->createFileInfo($storage->createPathInfo('test/invoice.pdf'));
echo $fileInfo->link();
use Nette\DI\Container;
use SixtyEightPublishers\FileStorage\FileStorageProviderInterface;
use SixtyEightPublishers\FileStorage\Cleaner\StorageCleanerInterface;
/** @var Container $container */
$cleaner = $container->getByType(StorageCleanerInterface::class);
$provider = $container->getByType(FileStorageProviderInterface::class);
$storage = $provider->get('default');
# get files count in the specific namespace:
$cleaner->getCount($storage->getFilesystem(), [
StorageCleanerInterface::OPTION_NAMESPACE => 'test',
]);
# get files count in the whole storage:
$cleaner->getCount($storage->getFilesystem());
# remove files in the specific namespace:
$cleaner->clean($storage->getFilesystem(), [
StorageCleanerInterface::OPTION_NAMESPACE => 'test',
]);
# clean the whole storage:
$cleaner->clean($storage->getFilesystem());
use Nette\DI\Container;
use SixtyEightPublishers\FileStorage\FileStorageProviderInterface;
use SixtyEightPublishers\FileStorage\Asset\AssetsCopierInterface;
/** @var Container $container */
$copier = $container->getByType(AssetsCopierInterface::class);
$provider = $container->getByType(FileStorageProviderInterface::class);
# Copies assets defined in the configuration
$copier->copy($provider->get('default'));
$copier->copy($provider->get('s3'));
Assets can be defined in the configuration under each storage separately but compiler extensions can define other assets:
use Nette\DI\CompilerExtension;
use SixtyEightPublishers\FileStorage\Bridge\Nette\DI\Assets;
use SixtyEightPublishers\FileStorage\Bridge\Nette\DI\AssetsProviderInterface;
final class MyCompilerExtension extends CompilerExtension implements AssetsProviderInterface
{
public function provideAssets() : array
{
return [
new Assets('s3', [
'path/to/file1.jpeg' => 'namespace/file1.jpeg',
'path/to/file2.jpeg' => 'namespace/file2.jpeg',
]),
];
}
}
The package provides custom Doctrine DBAL type file_info
. You can register it manually in this way:
use Doctrine\DBAL\Types\Type;
use SixtyEightPublishers\FileStorage\FileStorageProviderInterface;
use SixtyEightPublishers\FileStorage\Bridge\Doctrine\DbalType\FileInfoType;
/** @var FileStorageProviderInterface $fileStorageProvider */
Type::addType(FileInfoType::NAME, FileInfoType::class);
# this line is important:
Type::getType(FileInfoType::NAME)->setFileStorageProvider($fileStorageProvider);
Or you can use a compiler extension FileStorageDoctrineExtension
. The extension requires an integration of package 68publishers/doctrine-bridge.
extensions:
68publishers.file_storage.doctrine: SixtyEightPublishers\FileStorage\Bridge\Nette\DI\FileStorageDoctrineExtension
68publishers.file_storage.doctrine:
type_name: file_info # default
use Doctrine\ORM\Mapping as ORM;
use SixtyEightPublishers\FileStorage\FileInfoInterface;
/**
* @ORM\Entity
*/
class File
{
# ID and other columns
/**
* @ORM\Column(type="file_info")
*/
private FileInfoInterface $source;
public function __construct(FileInfoInterface $source)
{
$this->source = $source;
}
public function getSource(): FileInfoInterface
{
return $this->source;
}
}
use Doctrine\ORM\EntityManagerInterface;
use SixtyEightPublishers\FileStorage\FileStorageInterface;
/** @var EntityManagerInterface $em */
/** @var FileStorageInterface $storage */
$pathInfo = $storage->createPathInfo('test/avatar.png');
$resource = $storage->createResourceFromFile($pathInfo, __DIR__ . '/path/to/uploaded/file.png');
$storage->save($resource);
$pathInfo = $pathInfo->withVersion(time());
$entity = new File($storage->createFileInfo($pathInfo));
$em->persist($entity);
$em->flush();
# /data/files/test/avatar.png?_v=1611837352
echo (string) $entity->getSource();
extensions:
68publishers.file_storage.latte: SixtyEightPublishers\FileStorage\Bridge\Nette\DI\FileStorageLatteExtension
{varType SixtyEightPublishers\FileStorage\FileInfoInterface $fileInfo}
{* method FileInfo::__toString() calls ::link() internally so both lines are the same: *}
<a href="{$fileInfo->link()}" download>Download a file</a>
<a href="{$fileInfo}" download>Download a file</a>
{* Create FileInfo from string *}
<a href="{file_info('test/invoice.pdf')}" download>Download a file</a>
extensions:
68publishers.file_storage.console: SixtyEightPublishers\FileStorage\Bridge\Nette\DI\FileStorageConsoleExtension
Clean storage command:
$ bin/console file-storage:clean [<storage>] [--namespace <value>]
Copy storage assets:
$ bin/console file-storage:copy-assets [<storage>]
Before opening a pull request, please check your changes using the following commands
$ make init # to pull and start all docker images
$ make cs.check
$ make stan
$ make tests.all