| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529 |
- <?php
- namespace App\Models;
- use Illuminate\Support\Facades\Storage;
- use Illuminate\Http\UploadedFile;
- use DOMDocument;
- use Exception;
- class FileLib
- {
- /*
- * Storage 저장 경로 생성
- */
- public function setPath(string $uploadType, string|null $addPath = null): string
- {
- $uploadPath = $this->getPublicPath($uploadType, $addPath);
- $storagePath = $this->getStoragePath($uploadType, $addPath);
- // 해당 경로가 없다면 생성 해주고 index.php 파일 생성
- if (is_dir($storagePath) === false) {
- mkdir($storagePath, 0707, true);
- touch($storagePath . '/index.php');
- chmod($storagePath . '/index.php', 0644);
- }
- return $uploadPath;
- }
- /*
- * Public 저장 경로 조회
- */
- public function getPublicPath(string $uploadType, string|null $addPath = null): string
- {
- return (join(DIRECTORY_SEPARATOR, [
- UPLOAD_PATH_PUBLIC,
- $uploadType,
- date('Y'),
- date('m'),
- date('d'),
- $addPath
- ]) . DIRECTORY_SEPARATOR);
- }
- /*
- * Storage 저장 경로 조회
- */
- public function getStoragePath(string $uploadType, string|null $addPath = null): string
- {
- return storage_path(UPLOAD_PATH_APP . DIRECTORY_SEPARATOR . $this->getPublicPath($uploadType, $addPath));
- }
- /*
- * 경로 완전 삭제
- */
- public function removePath(string $uploadType, string|null $addPath = null): void
- {
- $storagePath = $this->getStoragePath($uploadType, $addPath);
- if(is_dir($storagePath)) {
- foreach (scandir($storagePath) as $file) {
- if (in_array($file, ['.', '..'])) {
- continue;
- }
- $path = ($storagePath . DIRECTORY_SEPARATOR . $file);
- if(is_dir($path)) {
- $this->removePath($uploadType, $addPath . DIRECTORY_SEPARATOR . $file);
- }else{
- unlink($path);
- }
- }
- rmdir($storagePath);
- }
- }
- /*
- * 본문 내용중 외부 이미지 주소를 서버로 가져온 후에 내부 주소로 변경합니다.
- */
- public function saveAsImage(string|null $contents, string $uploadType, string|null $addPath = null): string|null
- {
- return $this->saveImageFromBlob(
- $this->saveImageFromUrl($contents, $uploadType, $addPath), $uploadType, $addPath);
- }
- /*
- * CURL를 이용한 외부 이미지 다운로드
- */
- public function saveImageFromUrl(string|null $contents, string $uploadType, string|null $addPath = null): string|null
- {
- $dom = new DOMDocument('1.0', 'UTF-8');
- libxml_use_internal_errors(true);
- $dom->loadHTML('<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>' . $contents);
- libxml_clear_errors();
- $images = [];
- foreach ($dom->getElementsByTagName('img') as $img)
- {
- $src = $img->attributes->getNamedItem("src")->value;
- $path = canonicalizePath($src);
- if(file_exists(public_path($path))) {
- $images[] = $path;
- continue;
- }
- if(filter_var($src, FILTER_VALIDATE_URL)) {
- $url = parse_url($src);
- if (!empty($url['host']) && $url['host'] !== request()->getHttpHost())
- {
- $ch = curl_init($src);
- curl_setopt($ch, CURLOPT_HEADER, 0);
- curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
- $err = curl_error($ch);
- $rawData = [];
- if (empty($err)) {
- $rawData = curl_exec($ch);
- }
- curl_close($ch);
- if ($rawData)
- {
- // 파일 경로 설정
- $uploadPath = $this->setPath($uploadType, $addPath);
- $ext = $this->getExtension($src);
- [$unix, $sec] = explode(' ', microtime());
- $fileName = (md5(uniqid(mt_rand())) . '_' . str_replace('.', '', $sec . $unix) . '.' . $ext);
- $savePath = Storage::path($uploadPath . $fileName);
- $saveURL = Storage::url($uploadPath . $fileName);
- $fp = fopen($savePath, 'w+');
- fwrite($fp, $rawData);
- fclose($fp);
- if (file_exists($savePath)) {
- $images[] = $saveURL;
- $contents = str_replace($src, $saveURL, $contents);
- }
- }
- }
- }
- }
- // 기존 이미지 삭제
- $this->clear($images, $uploadType, $addPath);
- unset($dom, $images, $uploadPath, $addPath);
- return $contents;
- }
- /*
- * blob 종류의 이미지 저장
- */
- public function saveImageFromBlob(string|null $contents, string $uploadType, string|null $addPath = null): string|null
- {
- $dom = new DOMDocument('1.0', 'UTF-8');
- libxml_use_internal_errors(true);
- $dom->loadHTML('<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>' . $contents);
- libxml_clear_errors();
- $images = [];
- foreach ($dom->getElementsByTagName('img') as $img)
- {
- $src = $img->attributes->getNamedItem("src")?->value;
- $alt = $img->attributes->getNamedItem("alt")?->value;
- $target = $img->attributes->getNamedItem("data-target")?->value;
- $path = canonicalizePath($src);
- if(file_exists(public_path($path)) || !$target || !strpos($src, 'base64')) {
- $images[] = $path;
- continue;
- }
- try {
- $mime = mime_content_type($src);
- $ext = $this->getMimeExtension($mime);
- if (!$ext) {
- $ext = $this->getExtension($alt);
- }
- } catch (Exception) {
- return null;
- }
- // 파일 경로 설정
- $uploadPath = $this->setPath($uploadType, $addPath);
- $link = str_replace('data:' . $mime . ';base64,', '', $src);
- $link = str_replace(' ', '+', $link);
- $isBase64 = isBase64($link);
- $link = base64_decode($link);
- [$unix, $sec] = explode(' ', microtime());
- $fileName = (md5(uniqid(mt_rand())) . '_' . str_replace('.', '', $sec . $unix) . '.' . $ext);
- $savePath = Storage::path($uploadPath . $fileName);
- $saveUrl = Storage::url($uploadPath . $fileName);
- // 파일저장
- if ($isBase64) {
- file_put_contents($savePath, $link, FILE_APPEND | LOCK_EX);
- }
- if (file_exists($savePath)) {
- $images[] = $saveUrl;
- $contents = str_replace($src, $saveUrl, $contents);
- }
- }
- // 기존 이미지 삭제
- $this->clear($images, $uploadType, $addPath);
- unset($dom, $images, $uploadPath, $addPath);
- return $contents;
- }
- public function isBase64($s): bool
- {
- // Check if there are valid base64 characters
- if (!preg_match('/^[a-zA-Z0-9\/\r\n+]*={0,2}$/', $s)) {
- return false;
- }
- // Decode the string in strict mode and check the results
- $decoded = base64_decode($s, true);
- if (false === $decoded) {
- return false;
- }
- // if string returned contains not printable chars
- if (0 < preg_match('/((?![[:graph:]])(?!\s)(?!\p{L}))./', $decoded, $matched)) {
- return false;
- }
- // Encode the string again
- if (base64_encode($decoded) != $s) {
- return false;
- }
- return true;
- }
- public function isBase64Encoded(string $s): bool
- {
- if ((bool)preg_match('/^[a-zA-Z0-9\/\r\n+]*={0,2}$/', $s) === false) {
- return false;
- }
- $decoded = base64_decode($s, true);
- if ($decoded === false) {
- return false;
- }
- $encoding = mb_detect_encoding($decoded);
- if (!in_array($encoding, ['UTF-8', 'ASCII'], true)) {
- return false;
- }
- return ($decoded !== false && base64_encode($decoded) === $s);
- }
- /*
- * 파일 확장자 조회
- */
- public function getExtension(string $path): string
- {
- return pathinfo($path, PATHINFO_EXTENSION) ?? 'unknown';
- }
- /*
- * 이미지 파일 여부
- */
- public function isImage(string $path): bool
- {
- if(!file_exists($path)) {
- return false;
- }
- $mime = mime_content_type($path);
- if($mime) {
- return in_array($mime, [
- image_type_to_mime_type(IMAGETYPE_GIF),
- image_type_to_mime_type(IMAGETYPE_JPEG),
- image_type_to_mime_type(IMAGETYPE_PNG),
- image_type_to_mime_type(IMAGETYPE_BMP)]
- );
- }
- return false;
- }
- /*
- * 첨부파일 정보 조회
- */
- public function upload(UploadedFile $file, string $uploadType, string|null $addPath = null): object
- {
- $publicPath = $this->getPublicPath($uploadType, $addPath); // 파일 경로 조회
- $filePath = $file->store($publicPath); // 파일 저장
- $fullPath = Storage::path($filePath);
- $fullURL = Storage::url($filePath);
- $imageInfo = $this->getImageInfo($fullPath);
- return (object)[
- 'name' => $file->hashName(),
- 'path' => $fullPath,
- 'url' => $fullURL,
- 'originName' => $file->getClientOriginalName(),
- 'extension' => $file->getClientOriginalExtension(),
- 'size' => $file->getSize(),
- 'isImage' => $imageInfo->answer,
- 'imageWidth' => $imageInfo->width,
- 'imageHeight' => $imageInfo->height,
- 'imageType' => $imageInfo->type
- ];
- }
- /*
- * 이미지 크기 조정
- */
- public function resize(string $filePath, int $width, int $height): void
- {
- [$w, $h, $ext] = getimagesize($filePath);
- $ratio = max($width / $w, $height / $h);
- $h = ceil($height / $ratio);
- $x = ($w - $width / $ratio) / 2;
- $w = ceil($width / $ratio);
- $imgString = file_get_contents($filePath);
- $image = imagecreatefromstring($imgString);
- $tmp = imagecreatetruecolor($width, $height);
- imagecopyresampled($tmp, $image, 0, 0, $x, 0, $width, $height, $w, $h);
- switch ($ext) {
- case IMAGETYPE_JPEG:
- imagejpeg($tmp, $filePath, 100);
- break;
- case IMAGETYPE_PNG:
- imagepng($tmp, $filePath, 0);
- break;
- case IMAGETYPE_GIF:
- imagegif($tmp, $filePath);
- break;
- default:
- break;
- }
- imagedestroy($image);
- imagedestroy($tmp);
- }
- /*
- * mime_content_type 값의 확장자 반환
- */
- public function getMimeExtension(string $imageType): string
- {
- return ([
- 'image/bmp' => 'bmp',
- 'image/cis-cod' => 'cod',
- 'image/gif' => 'gif',
- 'image/ief' => 'ief',
- 'image/jpeg' => 'jpeg',
- 'image/pipeg' => 'jfif',
- 'image/tiff' => 'tif',
- 'image/x-cmu-raster' => 'ras',
- 'image/x-cmx' => 'cmx',
- 'image/x-icon' => 'ico',
- 'image/x-portable-anymap' => 'pnm',
- 'image/x-portable-bitmap' => 'pbm',
- 'image/x-portable-graymap' => 'pgm',
- 'image/x-portable-pixmap' => 'ppm',
- 'image/x-rgb' => 'rgb',
- 'image/x-xbitmap' => 'xbm',
- 'image/x-xpixmap' => 'xpm',
- 'image/x-xwindowdump' => 'xwd',
- 'image/png' => 'png',
- 'image/x-jps' => 'jps',
- 'image/x-freehand' => 'fh'
- ][$imageType] ?? '');
- }
- /*
- * Image type 반환
- */
- public function getImageType(int $imageType): string
- {
- return ([
- IMAGETYPE_UNKNOWN => 'UNKNOWN',
- IMAGETYPE_GIF => 'GIF',
- IMAGETYPE_JPEG => 'JPEG',
- IMAGETYPE_PNG => 'PNG',
- IMAGETYPE_SWF => 'SWF',
- IMAGETYPE_PSD => 'PSD',
- IMAGETYPE_BMP => 'BMP',
- IMAGETYPE_TIFF_II => 'TIFF_II',
- IMAGETYPE_TIFF_MM => 'TIFF_MM',
- IMAGETYPE_JPC => 'JPC',
- IMAGETYPE_JP2 => 'JP2',
- IMAGETYPE_JPX => 'JPX',
- IMAGETYPE_JB2 => 'JB2',
- IMAGETYPE_SWC => 'SWC',
- IMAGETYPE_IFF => 'IFF',
- IMAGETYPE_WBMP => 'WBMP',
- IMAGETYPE_XBM => 'XBM',
- IMAGETYPE_ICO => 'ICO',
- IMAGETYPE_WEBP => 'WEBP',
- IMAGETYPE_AVIF => 'AVIF',
- IMAGETYPE_COUNT => 'COUNT'
- ][$imageType] ?? '');
- }
- /*
- * Image Info 반환
- */
- public function getImageInfo(string $path): object
- {
- $ret = (object)[
- 'answer' => $this->isImage($path),
- 'name' => basename($path),
- 'size' => filesize($path),
- 'type' => null,
- 'width' => 0,
- 'height' => 0,
- 'ext' => null
- ];
- if(!file_exists($path)) {
- return $ret;
- }
- if($ret->answer) {
- [$ret->width, $ret->height, $ret->ext] = getimagesize($path);
- $ret->type = $this->getImageType($ret->ext);
- }
- return $ret;
- }
- /*
- * 파일 삭제
- */
- public function remove(string $path): bool
- {
- if (file_exists($path)) {
- return unlink($path);
- }
- $path = public_path($path);
- if (file_exists($path)) {
- return unlink($path);
- }
- return false;
- }
- /*
- * 본문 내용에 없는 이미지를 삭제한다.
- */
- public function clear(array $images, string $uploadType, string|null $addPath = null): void
- {
- $images = array_map(function ($src) {
- return last(explode(DIRECTORY_SEPARATOR, $src));
- }, $images);
- $storagePath = $this->getStoragePath($uploadType, $addPath);
- if (is_dir($storagePath)) {
- foreach (scandir($storagePath) as $file) {
- if (in_array($file, ['.', '..', 'index.php'])) {
- continue;
- }
- if (!in_array($file, $images)) { // 에디터에 없는 이미지면 삭제
- $filePath = ($storagePath . $file);
- if (is_dir($filePath)) {
- continue;
- }
- unlink($filePath);
- }
- }
- // 이미지가 없으면 디렉토리 삭제
- if (count($images) <= 0) {
- if(unlink($storagePath . 'index.php')) {
- rmdir($storagePath);
- }
- }
- }
- unset($images, $uploadType, $addPath, $storagePath);
- }
- /*
- * 이미지 정보 반환
- */
- public function getImageFromContent(?string $content): array
- {
- $dom = new DOMDocument('1.0', 'UTF-8');
- libxml_use_internal_errors(true);
- $dom->loadHTML('<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>' . $content);
- libxml_clear_errors();
- $images = [];
- foreach($dom->getElementsByTagName('img') as $img) {
- $src = canonicalizePath($img->attributes->getNamedItem("src")?->value);
- $origin = $img->attributes->getNamedItem("alt")?->value;
- $path = public_path($src);
- if(!file_exists($path)) {
- continue;
- }
- $imageInfo = $this->getImageInfo($path);
- $images[] = (object)[
- 'origin' => $origin,
- 'name' => $imageInfo->name,
- 'path' => $path,
- 'url' => $src,
- 'size' => $imageInfo->size,
- 'type' => $imageInfo->type,
- 'width' => $imageInfo->width,
- 'height' => $imageInfo->height
- ];
- }
- return $images;
- }
- }
|