From: Raphaël Gertz Date: Mon, 13 Oct 2025 13:28:59 +0000 (+0200) Subject: Add file util class X-Git-Tag: 0.5.5~3 X-Git-Url: https://git.rapsys.eu/packbundle/commitdiff_plain/23c67ea66c28d3f7305f0bd83c0157366b44709d?ds=sidebyside Add file util class --- diff --git a/Util/FileUtil.php b/Util/FileUtil.php new file mode 100644 index 0000000..814892e --- /dev/null +++ b/Util/FileUtil.php @@ -0,0 +1,309 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Rapsys\PackBundle\Util; + +use Psr\Container\ContainerInterface; + +use Rapsys\PackBundle\RapsysPackBundle; +use Rapsys\PackBundle\Util\ImageUtil; +use Rapsys\PackBundle\Util\IntlUtil; +use Rapsys\PackBundle\Util\SluggerUtil; + +use Symfony\Component\Routing\Generator\UrlGeneratorInterface; +use Symfony\Component\Routing\RouterInterface; +use Symfony\Contracts\Translation\TranslatorInterface; + +/** + * Manages file informations + */ +class FileUtil { + /** + * Alias string + */ + protected string $alias; + + /** + * Config array + */ + protected array $config; + + /** + * Creates a new file util + * + * @param ContainerInterface $container The container instance + * @param ImageUtil $image The ImageUtil instance + * @param IntlUtil $intl The IntlUtil instance + * @param RouterInterface $router The router instance + * @param SluggerUtil $slugger The SluggerUtil instance + * @param TranslatorInterface $translator The translator instance + */ + public function __construct(protected ContainerInterface $container, protected ImageUtil $image, protected IntlUtil $intl, protected RouterInterface $router, protected SluggerUtil $slugger, protected TranslatorInterface $translator) { + //Retrieve config + $this->config = $container->getParameter($this->alias = RapsysPackBundle::getAlias()); + } + + /** + * Get file infos + * + * @param string $path The base path + * @param string $realpath The real path + * @param string $name The route name + * @param array $parameters The route parameters + * @param bool $si Use si unit + */ + public function getFile(string $path, string $realpath, string $name, array $parameters = [], bool $si = true): array { + //Set file + $file = new \SplFileObject($realpath); + + //Set size + $size = $file->getSize(); + + //Set unit + $unit = $si ? 1000 : 1024; + + //Set index + $index = [ '', $si ? 'k' : 'K', 'M', 'G', 'T', 'P', 'E' ]; + + //Get exp + $exp = intval((log($size) / log($unit))); + + //Rebase number + $number = round($size / pow($unit, $exp), 2); + + //Set ext, height, image, mime, preview, thumb and width + $ext = $height = $image = $mime = $preview = $thumb = $width = false; + + //With supporter format extension + if (($pos = strrpos($realpath, '.')) && ($ext = substr($realpath, $pos + 1)) && in_array($ext, $this->config['formats'])) { + //With mime type + //XXX: getimagesize is too slow to run against all files + if (($image = getimagesize($realpath)) !== false) { + //Set ext + if (($ext = image_type_to_extension($image[2], false)) === false) { + //Throw error + throw new \Exception(sprintf('Unable to get "%s" file image extension', $realpath)); + } + + //Set height + $height = $image[1]; + + //Set mime + $mime = image_type_to_mime_type($image[2]); + + //Set width + $width = $image[0]; + + //Set preview + $preview = $this->image->getThumb($realpath, $height, $width, $ext); + + //Set preview + $thumb = $this->image->getThumb($realpath, $this->config['thumb']['height'], $this->config['thumb']['weight'], $ext); + } + } + + //Return file infos + return [ + 'created' => (new \DateTime())->setTimestamp($file->getCTime()), + 'height' => $height, + 'intlsize' => $intlsize = $this->intl->number($number), + 'intlunit' => $intlunit = $this->translator->trans($index[$exp].($si ? '' : 'i').'B'), + 'link' => $this->router->generate($name, ['path' => substr($realpath, strlen($path) + 1)] + $parameters), + 'download' => $this->router->generate('rapsyspack_download', ['path' => $short = $this->slugger->short($realpath), 'hash' => $this->slugger->hash($short), 'u' => $mtime = $file->getMTime()]), + 'mime' => $mime, + 'modified' => (new \DateTime())->setTimestamp($mtime), + 'name' => basename($realpath).' ('.$intlsize.' '.$intlunit.')', + 'preview' => $preview, + 'size' => $size, + 'thumb' => $thumb, + 'width' => $width, + //TODO: preview for images ? + //TODO: extra fields ? (preview, miniature, etc ?) + //TODO: mimetype decided extra fields ? (.pdf for doc, webm preview, img preview, etc ?) + //TODO: XXX: finish this !!! + ]; + } + + /** + * Get path infos + * + * @param string $path The base path + * @param string $realpath The real path + * @param string $name The route name + * @param array $parameters The route parameters + * @param bool $si Use si unit + */ + //Route object instead of shitty array ? + public function getInfos(string $path, string $realpath, string $slug, string $name, array $parameters = []) { + //Set result + $result = [ + 'breadcrumbs' => [/*$breadcrumb*/], + 'directories' => [], + 'file' => [], + 'files' => [], + ]; + + //Set base + $base = ''; + + //Iterate on breadcrumbs + foreach (explode('/', substr($realpath, strlen($path))) as $value) { + $result['breadcrumbs'][] = [ + 'name' => $value ? '/ '.$value : $slug, + 'link' => $this->router->generate($name, ['path' => ($base .= ($base == '' ? '' : '/').$value)] + $parameters) + ]; + } + + //With file + if (is_file($realpath)) { + //Set file + $result['file'] = $this->getFile($path, $realpath, $name, $parameters); + //With directory + //TODO: for pagination, files and directories are to be placed in a single array + //TODO: only get informations on files and directories inside the pagination window ! + } elseif (is_dir($realpath)) { + //Set dir + $dir = dir($realpath); + + //Iterate on directory + while (($item = $dir->read()) !== false) { + //Skip ., .., .git, .svn, .htpasswd, .htgroup and .*.un~ items + //TODO: set this regexp in config ? + if (preg_match('/^(\.|\.\.|\.git|\.svn|\.ht(access|passwd|group)|\..*\.un~)$/', $item)) { + //Skip it + continue; + } + + //Check item + if ( + //Without item realpath + !($itempath = realpath($realpath.'/'.$item)) || + //Item realpath not matching album path + //XXX: skip files outside of album/element path (symlink outside of tree) + //TODO: make it configurable ? by album/element/admin ? + $path !== substr($itempath, 0, strlen($path)) + ) { + //Skip it + continue; + } + + //With directory + if (is_dir($itempath)) { + //Append directory + //TODO: use this structure or revert to old $item.'/' => $link form + $result['directories'][] = [ + 'name' => $item.'/', + 'link' => $this->router->generate($name, ['path' => substr($itempath, strlen($path) + 1)] + $parameters) + //TODO: add stats here ? like number of files ? + ]; + //With file + } elseif (is_file($itempath)) { + //Set file + $result['files'][] = $this->getFile($path, $itempath, $name, $parameters); + //With unknown type + } else { + //Throw 404 + throw new \Exception(sprintf('Unknown file "%s" type', $itempath)); + } + } + //With unknown type + } else { + //Throw 404 + throw new \Exception(sprintf('Unknown file "%s" type', $realpath)); + } + + //Return result + return $result; + } + + /** + * Get file infos + * + * @param string $path The file path + * @param bool $si Use si units + * @return array The file infos + * / + public function infos(string $path, bool $si = true): array { + //Stat file + $stat = stat($path); + + //Set unit + $unit = $si ? 1000 : 1024; + + //Set index + $index = [ '', $si ? 'k' : 'K', 'M', 'G', 'T', 'P', 'E' ]; + + //Get exp + $exp = intval((log($stat['size']) / log($unit))); + + //Rebase number + $number = round($stat['size'] / pow($unit, $exp), 2); + + //Set file infos + $fileinfos = [ + 'intlsize' => $intlsize = $this->intl->number($number), + 'intlunit' => $intlunit = $this->translator->trans($index[$exp].($si ? '' : 'i').'B'), + 'name' => basename($path).' ('.$intlsize.' '.$intlunit.')', + 'size' => $stat['size'], + 'ctime' => $stat['ctime'], + 'mtime' => $stat['mtime'], + //TODO: preview for images ? + //TODO: extra fields ? (preview, miniature, etc ?) + //TODO: mimetype decided extra fields ? (.pdf for doc, webm preview, img preview, etc ?) + //TODO: XXX: finish this !!! + ]; + + //With mimetype + if (($mimetype = mime_content_type($path)) !== false) { + //Set file mimetype + $fileinfos['mimetype'] = $mimetype; + + //TODO: with image preview, movie webm preview, others a imagemagick file with format initials ? + + //With image + if ( + $mimetype == 'image/jpeg' || + $mimetype == 'image/png' || + $mimetype == 'image/bmp' || + $mimetype == 'image/tiff' || + $mimetype == 'image/svg+xml' + ) { + //Get image width and height + if (($fileinfos['image'] = getimagesize($path)) === false) { + //Throw error + throw new \Exception(sprintf('Unable to get "%s" file image size', $path)); + } + + //Set thumb + $fileinfos['thumb'] = $this->image->getThumb($path, 64, 64); + } + } + /* + 'src' => + //With location user source image + if (($isFile = is_file($source = $this->config['path'].'/location/'.$location['id'].'/'.$id.'.png')) && ($mtime = stat($source)['mtime'])) { + //Set location image + $this->context['locations'][$locationId]['image'] = $this->image->getThumb($location['miniature'], $mtime, $source); + + //With image mimetype + if (in_array($fileinfos['mimetype'], [ 'image/jpeg', 'image/png', 'image/webp' ])) { + header('Content-Type: text/plain'); + var_dump($fileinfos); + exit; + $file['thumb'] = $this->router->generate('rapsyspack_thumb', [ 'hash' => $this->slugger->hash(''), 'path' => $path, 'slug' => $slug ]); + #public function thumb(Request $request, string $hash, int $updated, string $path, int $width, int $height): Response { + } + * / + + //Return file infos + return $fileinfos; + }*/ +}