update
This commit is contained in:
270
inc/Hura8/System/Controller/bFileHandle.php
Normal file
270
inc/Hura8/System/Controller/bFileHandle.php
Normal file
@@ -0,0 +1,270 @@
|
||||
<?php
|
||||
|
||||
namespace Hura8\System\Controller;
|
||||
|
||||
use Hura8\Interfaces\AppResponse;
|
||||
use Hura8\Interfaces\FileHandleInfo;
|
||||
use Hura8\Interfaces\FileHandleResponse;
|
||||
use Hura8\System\FileSystem;
|
||||
use Hura8\System\HuraImage;
|
||||
use Hura8\System\IDGenerator;
|
||||
use League\MimeTypeDetection\FinfoMimeTypeDetector;
|
||||
|
||||
/**
|
||||
* @date 16-Jan-2024
|
||||
* @description based-class to handling files, this is used for :
|
||||
* - FileUpload class
|
||||
* - CopyFileFromUrl class
|
||||
* ...
|
||||
*
|
||||
*/
|
||||
|
||||
abstract class bFileHandle
|
||||
{
|
||||
|
||||
static $image_extensions = array(".jpg",".jpeg",".gif", ".png", ".webp", '.avif', ".ico");
|
||||
|
||||
protected $permit_file_extensions = array(
|
||||
".jpg",".jpeg",".gif", ".png", ".webp", '.avif',
|
||||
".doc", ".docx",".xls",".xlsx", ".ppt", ".pdf",
|
||||
".rar", ".zip",
|
||||
//".avi",".mov",".mpg", ".wmv", ".mpeg",
|
||||
//".mp3",".mp4", ".ogg", ".oga", ".wav", ".wma"
|
||||
);
|
||||
|
||||
protected $permit_mine_types = array(
|
||||
'image/jpeg', 'image/png','image/gif', 'image/webp', 'image/avif',
|
||||
|
||||
// zip files
|
||||
'application/zip', 'application/x-zip-compressed', 'multipart/x-zip', 'application/x-compressed',
|
||||
);
|
||||
|
||||
// full system's path where the file will be finally stored or it's currently there for processing
|
||||
// example: /var/www/html/domain.com/public_html/media/product/
|
||||
protected $target_dir = "";
|
||||
|
||||
// directory appears to the public
|
||||
// example: /media/product/
|
||||
protected $public_dir = "";
|
||||
|
||||
protected $tmp_dir = ROOT_DIR . "/var/tmp_upload/"; // system tmp dir to store uploaded file for security examination before moving to target_dir
|
||||
protected $tmp_folder = ""; // tmp folder per session in the $tmp_dir to hold user's files. This folder will be removed when operation is complete
|
||||
|
||||
protected $setup_success = false;
|
||||
|
||||
/**
|
||||
* @param string $target_dir will be created if not exist i.e. media/product/ or media/user_upload/date('d-m-Y')
|
||||
* @param ?array $permit_file_extensions null to allow all default file extensions
|
||||
*/
|
||||
public function __construct( string $target_dir, ?array $permit_file_extensions = null ) {
|
||||
|
||||
if(is_array($permit_file_extensions)) {
|
||||
$this->permit_file_extensions = $permit_file_extensions;
|
||||
}
|
||||
|
||||
if($target_dir) {
|
||||
$this->target_dir = PUBLIC_DIR . DIRECTORY_SEPARATOR. $target_dir;
|
||||
$this->public_dir = "/".$target_dir;
|
||||
}
|
||||
|
||||
$setup_res = $this->setUp();
|
||||
if($setup_res->getStatus() == AppResponse::SUCCESS) {
|
||||
$this->setup_success = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @description (rename if set) and resize file
|
||||
* @param string $name
|
||||
* @param string $new_name
|
||||
* @param array $resized_sizes [] array('small' => ['width' => 100, 'height' => 100], 'large' => ['width' => 200, 'height' => 200] )
|
||||
* @return array|false
|
||||
*/
|
||||
public function resizeFile(string $name, string $new_name = '', array $resized_sizes = []) {
|
||||
|
||||
if($new_name) {
|
||||
$renamed = $this->renameFile($name, $new_name);
|
||||
if(!$renamed) {
|
||||
return false;
|
||||
}
|
||||
|
||||
//$file_name = $renamed['file_name'];
|
||||
//$public_path = $renamed['public_path'];
|
||||
$local_path = $renamed['local_path'];
|
||||
}else{
|
||||
//$file_name = $name;
|
||||
//$public_path = $this->public_dir . "/".$name;
|
||||
$local_path = $this->target_dir . "/" . $name;
|
||||
}
|
||||
|
||||
$objHuraImage = new HuraImage();
|
||||
|
||||
list(, $expected_files, ) = $objHuraImage->resize($local_path, $resized_sizes);
|
||||
|
||||
return $expected_files;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @description we can rename the uploaded file to new file name, for example: we want product's image have the format [PRODUCT_ID]-name
|
||||
* @param string $name
|
||||
* @param string $new_name
|
||||
* @return array | false
|
||||
*/
|
||||
public function renameFile(string $name, string $new_name) {
|
||||
if(@rename($this->target_dir . "/" . $name, $this->target_dir . "/" . $new_name)){
|
||||
return [
|
||||
"file_name" => $new_name,
|
||||
"public_path" => $this->public_dir . "/".$new_name,
|
||||
"local_path" => $this->target_dir . "/" . $new_name,
|
||||
];
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// public utility
|
||||
public static function getFileExtension($file_name) {
|
||||
return strtolower(strrchr($file_name,"."));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @description run clean up after finish using the FileHandle instance
|
||||
*/
|
||||
public function cleanUp() {
|
||||
if($this->tmp_folder) {
|
||||
FileSystem::removeDir($this->tmp_folder);
|
||||
}
|
||||
}
|
||||
|
||||
protected function processFile(
|
||||
$original_file_name,
|
||||
$original_file_tmp_name, // temporary uploaded file as in case of $_FILES[$input_file_name]["tmp_name"]
|
||||
$fixed_file_name="",
|
||||
$max_file_size=0
|
||||
) : FileHandleResponse {
|
||||
|
||||
if(!$original_file_name) {
|
||||
return new FileHandleResponse(AppResponse::ERROR, 'no file', null);
|
||||
}
|
||||
|
||||
$file_size = filesize($original_file_tmp_name);
|
||||
if($max_file_size > 0 && $max_file_size < $file_size) {
|
||||
return new FileHandleResponse(AppResponse::ERROR, 'Size is too large: '.round($file_size/1000).'KB', null);
|
||||
}
|
||||
|
||||
//validate extension
|
||||
$file_ext = self::getFileExtension($original_file_name);
|
||||
|
||||
if(!in_array($file_ext, $this->permit_file_extensions)) {
|
||||
return new FileHandleResponse(AppResponse::ERROR, "Type ".$file_ext." is not allowed!", null);
|
||||
}
|
||||
|
||||
$file_name = substr($original_file_name, 0, strrpos($original_file_name,"."));
|
||||
$file_name = preg_replace("/[^a-z0-9_-]/i","", $file_name);
|
||||
$file_name = substr($file_name, 0, 50); // max- length
|
||||
$clean_file_name = ($fixed_file_name) ?: $file_name . $file_ext;
|
||||
|
||||
$tmp_file_path = $this->tmp_folder . "/". $clean_file_name;
|
||||
$return_data = null;
|
||||
|
||||
//debug_var([$original_data, $tmp_file_path]);
|
||||
if(@rename($original_file_tmp_name, $tmp_file_path)){
|
||||
|
||||
$is_file_image = (in_array($file_ext, static::$image_extensions ));
|
||||
|
||||
if($is_file_image) {
|
||||
list($width, $height) = getimagesize($tmp_file_path);
|
||||
}else{
|
||||
$width = 0;
|
||||
$height = 0;
|
||||
}
|
||||
|
||||
$detector = new FinfoMimeTypeDetector();
|
||||
$mimeType = $detector->detectMimeTypeFromPath($tmp_file_path);
|
||||
|
||||
// if image, we re-create and optimize it
|
||||
if($is_file_image) {
|
||||
if(in_array($mimeType, $this->permit_mine_types)) {
|
||||
$objHuraImage = new HuraImage();
|
||||
if($objHuraImage->create($tmp_file_path, $this->target_dir . DIRECTORY_SEPARATOR . $clean_file_name)){
|
||||
$return_data = new FileHandleInfo([
|
||||
"file_name" => $clean_file_name,
|
||||
"public_path" => $this->public_dir . "/".$clean_file_name,
|
||||
"local_path" => $this->target_dir . "/" . $clean_file_name,
|
||||
"mime_type" => $mimeType,
|
||||
"file_size" => $file_size,
|
||||
"file_ext" => $file_ext,
|
||||
"width" => $width,
|
||||
"height" => $height,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
}elseif(@rename ($tmp_file_path, $this->target_dir . DIRECTORY_SEPARATOR . $clean_file_name )) {
|
||||
$return_data = new FileHandleInfo([
|
||||
"file_name" => $clean_file_name,
|
||||
"public_path" => $this->public_dir . "/".$clean_file_name,
|
||||
"local_path" => $this->target_dir . "/" . $clean_file_name,
|
||||
"mime_type" => $mimeType,
|
||||
"file_size" => $file_size,
|
||||
"file_ext" => $file_ext,
|
||||
"width" => 0,
|
||||
"height" => 0,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
// delete tmp file on server
|
||||
if(file_exists($original_file_tmp_name)) {
|
||||
@unlink($original_file_tmp_name);
|
||||
}
|
||||
|
||||
if($return_data) {
|
||||
return new FileHandleResponse(AppResponse::SUCCESS, 'Success', $return_data);
|
||||
}
|
||||
|
||||
return new FileHandleResponse(AppResponse::ERROR, 'Unknown', null);
|
||||
}
|
||||
|
||||
|
||||
protected function setUp(): AppResponse
|
||||
{
|
||||
// check target dir
|
||||
if($this->target_dir && !is_dir($this->target_dir)) {
|
||||
@mkdir($this->target_dir, 0755, true);
|
||||
}
|
||||
|
||||
if(!file_exists($this->target_dir)) {
|
||||
return new AppResponse(AppResponse::ERROR, $this->target_dir.' not exists');
|
||||
}
|
||||
|
||||
// create tmp_folder to upload file to
|
||||
$this->tmp_folder = $this->create_tmp_folder();
|
||||
if(!$this->tmp_folder) {
|
||||
return new AppResponse(AppResponse::ERROR, "Check ".$this->tmp_dir." and make sure it exists and writable");
|
||||
}
|
||||
|
||||
return new AppResponse(AppResponse::SUCCESS);
|
||||
}
|
||||
|
||||
|
||||
protected function create_tmp_folder() : ?string {
|
||||
$tmp_folder = $this->tmp_dir . IDGenerator::createStringId(5);
|
||||
if(!@mkdir($tmp_folder, 0777, true)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// retest
|
||||
if(!$tmp_folder || !is_dir($tmp_folder)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $tmp_folder;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user