<?php
namespace App\Service\FileUploader;
use Psr\Log\LoggerInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\Finder\Finder;
use Symfony\Component\HttpFoundation\AcceptHeader;
use Symfony\Component\HttpFoundation\AcceptHeaderItem;
use Symfony\Component\HttpFoundation\File\File;
use Symfony\Component\HttpFoundation\File\UploadedFile;
use Symfony\Component\HttpFoundation\BinaryFileResponse;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\Filesystem\Filesystem;
use App\Service\FileUploader\Exception;
use App\Service\FileUploader\Result;
use Intervention\Image\ImageManagerStatic as Image;
class UploadService
{
/**
* @var ContainerInterface
*/
protected $container;
/**
* @var Filesystem
*/
protected $filesystem;
protected $request;
private $logger;
public function __construct(ContainerInterface $container, LoggerInterface $logger, RequestStack $requestStack) {
$this->container = $container;
$this->logger = $logger;
$this->filesystem = new Filesystem();
$this->request = $requestStack->getCurrentRequest();
}
/**
* EntityのUploadConfigを返す
*
* $UploadService->getUploadConfig(Entity $NewsEntity)
* $UploadService->getUploadConfig("News\Entry", "main_img")
* $UploadService->getUploadConfig("News\Entry:main_img")
* で指定する
*
* Entity側では App\Entity\Traits\FileConfigurationTrait を実装
* protected $uploadConfig = [
* "main_img" => [
* "upload_dir" => "news/main_img/",
* "mime" => [
* "image/jpeg",
* "image/png"
* ],
* "image_process" => [
* "resize" => [1200, 800],
* "thumb" => [150, 150],
* "limit" => [1200, 1000] はみ出た場合のみトリム
* "fit" => [1200, 1000]
* "thumb_resize" => [300, 300, bgcolor #fff] トリムせずに縮小して指定サイズにカンバスをセット
* ]
* ]
* ];
* で設定する
*
* @return array
*/
public function getUploadConfig($entity, $fileClass = null) {
if(is_string($entity) && null === $fileClass) {
list($entity, $fileClass) = explode(":", $entity);
}
if(is_string($entity)) {
$entyt = "\\" . $entity;
$entity = new $entity;
}
if(!method_exists($entity, "getUploadConfigrations")) {
throw new \InvalidArgumentException('Entity not have method getUploadConfigrations()');
}
return $entity->getUploadConfigrations($fileClass);
}
public function upload(UploadedFile $file, array $uploadConfig) {
$uploadDir = $this->getUploadDir($uploadConfig);
$result = new Result($file, $uploadDir);
// Mime check
if(
isset($uploadConfig['mime']) &&
is_array($uploadConfig['mime']) &&
!in_array($result->getMime(), $uploadConfig['mime'], true)
) {
throw new Exception('Mime type deny.');
}
// 画像MIMEであるか?
if($this->isImageMime($result->getMime())) {
if (isset($uploadConfig["image_process"])) {
// 画像処理
$this->imageProcessing($result, $uploadConfig);
$result->setPreviewUrl($this->getPreviewUrl($uploadConfig, $result->getRemoteName()));
} else {
// ファイル保存
$img = Image::make($file);
$result->setImgSize($img->width(), $img->height());
$img->destroy();
$result->move();
$result->setPreviewUrl($this->getPreviewUrl($uploadConfig, $result->getRemoteName()));
$result->setIsImage(true);
}
} else {
// ファイル保存
$result->move();
$result->setPreviewUrl($this->getPreviewUrl($uploadConfig, $result->getRemoteName()));
$result->setIsImage(false);
}
return $result;
}
protected function getPreviewUrl(array $config, $remoteFile) {
return $this->container->getParameter('image.admin.preview_url').
str_replace("\\", "-", $config['fileClass']).
"/".
$remoteFile;
}
public function getUploadDir(array $uploadConfig) {
if(!isset($uploadConfig['upload_dir'])) {
throw new \InvalidArgumentException('UploadDir parameter not found in Cms Upload config. check Entity property $uploadConfig');
}
// remote upload dir
$uploadDir = $this->container->getParameter('cms_upload_dir');
$uploadDir .= "/". $uploadConfig['upload_dir'];
if(!preg_match("/\/$/", $uploadDir)) {
$uploadDir .= "/";
}
if(!$this->filesystem->exists($uploadDir)) {
$this->filesystem->mkdir($uploadDir, 0755);
}
return $uploadDir;
}
public function isImageMime($mime) {
if($mime instanceof UploadedFile) {
$mime = $mime->getMimeType();
}
$imageMime = $this->container->getParameter('image.mime');
return (in_array($mime, $imageMime, true));
}
public function isPdf($mime) {
return $mime === "application/pdf";
}
protected function moveFile(UploadedFile $file, $dir, $newName) {
$file->move($dir, $newName);
return null;
}
public function imageProcessing(Result $result, array $config) {
$img = Image::make($result->getfile());
foreach($config["image_process"] as $method => $value) {
switch($method) {
case "orientate":
$img->orientate();break;
case "fit":
$img->fit($value[0], $value[1], function($constraint) {
$constraint->upsize();
});break;
case "resize":
$img->resize($value[0], $value[1], function($constraint) {
$constraint->aspectRatio();
$constraint->upsize();
});
break;
case "resize_canvas":
$value[2] = isset($value[2])? $value[2]: "center";
$value[3] = isset($value[3])? $value[3]: false;
$value[4] = isset($value[4])? $value[4]: null;
$this->logger->info(print_r($value, true));
$img->resizeCanvas($value[0], $value[1], $value[2], $value[3], $value[4]);
break;
case "limit":
$w = $img->width();
$h = $img->height();
$doTrim = false;
// はみ出ている場合のみトリム
if($w > $value[0]) {
$toWidth = $value[0];
$doTrim = true;
} else {
$toWidth = $w;
}
if($h > $value[1]) {
$toHeight = $value[1];
$doTrim = true;
} else {
$toHeight = $h;
}
if($doTrim) {
$img->resizeCanvas($toWidth, $toHeight);
}
break;
case "callback":
call_user_func($value, $img, $result);break;
case "thumb":
$thumb = Image::make($result->getFile());
$thumb->orientate();
$thumb->fit($value[0], $value[1], function($constraint) {
$constraint->upsize();
});
$thumbFilePath = $result->createThumbFilename();
$thumb->save($thumbFilePath);
$thumb->save($result->createThumbFilename("webp"), null, "webp");
$thumb->destroy();
$result->setThumbName($thumbFilePath);
$result->setThumbPreviewUrl(
$this->getPreviewUrl($config, $result->getThumbName())
);
break;
case "thumb_resize":
$thumb = Image::make($result->getFile());
$thumb->orientate();
$thumb->resize($value[0], $value[1], function($constraint) {
$constraint->aspectRatio();
});
$color = (isset($value[2]))? $value[2]: null;
$thumb->resizeCanvas($value[0], $value[1], "center", false,$color);
$thumbFilePath = $result->createThumbFilename();
$thumb->save($thumbFilePath);
$thumb->save($result->createThumbFilename("webp"), null, "webp");
$thumb->destroy();
$result->setThumbName($thumbFilePath);
$result->setThumbPreviewUrl(
$this->getPreviewUrl($config, $result->getThumbName())
);
break;
}
}
$result->setImgSize($img->width(), $img->height());
$result->setIsImage(true);
$remoteName = $result->getRemotePath();
$img->save($remoteName);
$img->save($result->getRemotePathWithoutExtension(). ".webp", null, 'webp');
$img->destroy();
}
protected function createPdfThumb(Result $result) {
}
/**
* ファイルを出力する
*
* @param string $filename
* @param array $config self::getUploadedConfig()で取得した配列
* @return BinaryFileResponse
*/
public function output($filename, $config) {
$uploadDir = $this->getUploadDir($config);
$path = $uploadDir. $filename;
$fs = new FileSystem;
if(!$fs->exists($path)) {
throw new NotFoundHttpException('file not found.'. $path);
}
return new BinaryFileResponse($path);
}
public function outputResponseOrWebpResponse($filename, $config): Response
{
$dir = $this->getUploadDir($config);
$targetFile = $dir . $filename;
$file = new File($targetFile);
$acceptHeader = AcceptHeader::fromString($this->request->headers->get('accept'));
if(!$acceptHeader->has('image/webp')) {
return new BinaryFileResponse($targetFile);
}
$guessExtension = $file->guessExtension();
switch(strtolower($guessExtension)) {
case "webp":
return new BinaryFileResponse($targetFile);
case "jpg":
case "jpeg":
case "png":
$webpPath = $dir. basename($targetFile, ".". $guessExtension). ".webp";
$fs = new Filesystem();
if($fs->exists($webpPath)) {
return new BinaryFileResponse($webpPath);
}
break;
}
return new BinaryFileResponse($targetFile);
}
/**
* アップロード済のファイルのwidget用のデータを返す
*
* @param string $filename
* @param array $config self::getUploadedConfig()で取得した配列
* @return array
*/
public function getUploadedValue($filename, $config) {
$uploadDir = $this->getUploadDir($config);
$fs = new FileSystem();
// remote file
if(!$fs->exists($uploadDir . $filename)) {
throw new \InvalidArgumentException('File not found.');
}
$file = new UploadedFile($uploadDir. $filename, $filename);
$isImage = $this->isImageMime($file->getMimeType());
$thumb_prev = null;
if($isImage) {
$thumbName = preg_replace("/(.[a-z]{3,4})$/", "", $filename);
if($fs->exists($uploadDir. $thumbName)) {
$thumb_prev = $this->getPreviewUrl($config, $thumbName);
}
}
return [
"status" => "200",
"is_image" => $isImage,
"preview_url" => $this->getPreviewUrl($config, $filename),
"thumb_preview_url" => $thumb_prev,
];
}
}