276 lines
8.4 KiB
PHP
276 lines
8.4 KiB
PHP
|
|
<?php
|
||
|
|
/**
|
||
|
|
* Created by Hurasoft
|
||
|
|
* Date: 28-May-2022
|
||
|
|
* Time: 1:44 PM
|
||
|
|
* Description: Use this class in the cdn host to handle the upload of files from Hura8's admin and put the uploaded file in the cdn host's local directory
|
||
|
|
*/
|
||
|
|
|
||
|
|
namespace Hura8\System;
|
||
|
|
|
||
|
|
|
||
|
|
class CDNFileUploadHandle
|
||
|
|
{
|
||
|
|
|
||
|
|
const SECURITY_SALT = 'ahss@3asdaaSDFSD';
|
||
|
|
|
||
|
|
//default config
|
||
|
|
protected $config = [
|
||
|
|
//1. file settings
|
||
|
|
"max_file_size" => 2000000,//bytes ~ 1MB
|
||
|
|
'allowed_file_types' => [
|
||
|
|
'image' => ['.jpeg', '.jpg', '.gif', '.png'],
|
||
|
|
'script' => ['.css', '.js'],
|
||
|
|
],
|
||
|
|
|
||
|
|
//2. uploaded by client
|
||
|
|
'uid' => '',
|
||
|
|
'token' => '',
|
||
|
|
'time' => '',
|
||
|
|
'item_type' => 'product', // product|article|media
|
||
|
|
'target_path' => '',
|
||
|
|
'upload_method' => 'content', //file || content
|
||
|
|
"file_content" => "", //only needed if upload_method = content
|
||
|
|
"file_name" => "",//only needed if upload_method = content
|
||
|
|
];
|
||
|
|
private $tmp_file_prop = null; //array, temporary file's props in tmp folder
|
||
|
|
private $accepted_file_input_names = [
|
||
|
|
"file", //<input type=file name="file">
|
||
|
|
//"qqfile", //name use for files uploaded through FineUploader library
|
||
|
|
];
|
||
|
|
private $user_tmp_dir = '';
|
||
|
|
|
||
|
|
public function __construct(){
|
||
|
|
|
||
|
|
}
|
||
|
|
|
||
|
|
protected function createToken($id, $time) {
|
||
|
|
return sha1(join("-", [$id , $time , static::SECURITY_SALT]));
|
||
|
|
}
|
||
|
|
|
||
|
|
public function start() {
|
||
|
|
|
||
|
|
$this->_get_post_info();
|
||
|
|
|
||
|
|
if(!$this->validateUser()) {
|
||
|
|
return $this->set_return_result('error', 'User failed to verify', []);
|
||
|
|
}
|
||
|
|
|
||
|
|
//receive files
|
||
|
|
$file = $this->receive_file();
|
||
|
|
|
||
|
|
//return file-location to upload API to return to client application
|
||
|
|
return $this->set_return_result('success', 'Upload succeeded', $file );
|
||
|
|
}
|
||
|
|
|
||
|
|
protected function validateUser() {
|
||
|
|
return ($this->config['token'] == $this->createToken($this->config['uid'], $this->config['time']));
|
||
|
|
}
|
||
|
|
|
||
|
|
protected function _get_post_info() {
|
||
|
|
$expected_keys = [
|
||
|
|
'upload_method',
|
||
|
|
'file_name',
|
||
|
|
'target_path',
|
||
|
|
'file_content',
|
||
|
|
'uid',
|
||
|
|
'time',
|
||
|
|
'token',
|
||
|
|
'item_type',
|
||
|
|
];
|
||
|
|
|
||
|
|
foreach ($expected_keys as $key) {
|
||
|
|
$this->config[$key] = isset($_POST[$key]) ? $_POST[$key] : null;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
//return boolean
|
||
|
|
protected function remove_tmp_file() {
|
||
|
|
$this->deleteDirectory($this->user_tmp_dir);
|
||
|
|
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
//return boolean
|
||
|
|
protected function validate_file() {
|
||
|
|
//$this->tmp_file_prop
|
||
|
|
//validate file size
|
||
|
|
if($this->config['max_file_size'] && $this->config['max_file_size'] < $this->tmp_file_prop['size']) {
|
||
|
|
return 'Size too large';
|
||
|
|
}
|
||
|
|
|
||
|
|
//validate allowed extension: allow image but upload .docx files
|
||
|
|
$has_extension = false;
|
||
|
|
$file_type = '';
|
||
|
|
foreach ( $this->config['allowed_file_types'] as $group => $group_ext ) {
|
||
|
|
if(in_array( $this->tmp_file_prop['ext'], $group_ext )) {
|
||
|
|
$has_extension = true;
|
||
|
|
$file_type = $group;
|
||
|
|
break ;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
if( ! $has_extension ) return "File type not allowed";
|
||
|
|
|
||
|
|
//validate claimed extension: claim image but not actual image
|
||
|
|
$full_file_path = $this->tmp_file_prop['tmp_location'] . DIRECTORY_SEPARATOR . $this->tmp_file_prop['name'];
|
||
|
|
if( $file_type == 'image' && ! v::image()->validate( $full_file_path )) {
|
||
|
|
return "Not actual image";
|
||
|
|
}
|
||
|
|
|
||
|
|
return '';
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
//return mixed : array $tmp_file_prop or false
|
||
|
|
protected function receive_file() {
|
||
|
|
|
||
|
|
//check upload method
|
||
|
|
if( $this->config['upload_method'] == 'content' ) {
|
||
|
|
//upload by content - file created on server
|
||
|
|
$file_ext = $this->get_ext($this->config['file_name']);
|
||
|
|
$upload_folder = $this->create_upload_folder() ;
|
||
|
|
|
||
|
|
if( ! $this->move_file($upload_folder)) {
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
return [
|
||
|
|
"ext" => $file_ext,
|
||
|
|
"name" => $this->config['file_name'],
|
||
|
|
"folder" => $upload_folder,
|
||
|
|
"size" => strlen($this->config['file_content']),
|
||
|
|
];
|
||
|
|
|
||
|
|
} else {
|
||
|
|
|
||
|
|
//upload by file
|
||
|
|
//get list of files to be uploaded
|
||
|
|
$file_uploaded = null;
|
||
|
|
foreach ($this->accepted_file_input_names as $_name) {
|
||
|
|
if(isset($_FILES[$_name])) {
|
||
|
|
$file_uploaded = $_FILES[$_name];
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
if ( ! $file_uploaded ) return false;
|
||
|
|
|
||
|
|
//if file name too long, then error
|
||
|
|
if ( strlen($file_uploaded['name']) > 225 ) return false;
|
||
|
|
|
||
|
|
//move file to tmp folder
|
||
|
|
$file_ext = $this->get_ext($file_uploaded['name']);
|
||
|
|
$upload_folder = $this->create_upload_folder() ;
|
||
|
|
|
||
|
|
if( ! $this->move_file($upload_folder) ){
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
return [
|
||
|
|
"ext" => $file_ext,
|
||
|
|
"name" => $this->config['file_name'],
|
||
|
|
"folder" => $upload_folder,
|
||
|
|
"size" => $file_uploaded['size'],
|
||
|
|
];
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
protected function set_return_result($status = 'success', $message = 'Upload success', $content = []) {
|
||
|
|
return [
|
||
|
|
"status" => $status,
|
||
|
|
"message" => $message,
|
||
|
|
"files" => $content,
|
||
|
|
];
|
||
|
|
}
|
||
|
|
|
||
|
|
//create upload folder for user: use current date, user id or app id based on $this->config
|
||
|
|
private function create_upload_folder() {
|
||
|
|
//$build_folder = [$this->tmp_dir];
|
||
|
|
/*$user_id = (isset($this->config['user_id'])) ? intval($this->config['user_id']) : null;
|
||
|
|
if($user_id) {
|
||
|
|
$build_folder[] = $user_id;
|
||
|
|
}
|
||
|
|
$build_folder[] = date("Ymd");*/
|
||
|
|
|
||
|
|
//rebuild upload folder
|
||
|
|
$build_folder = array_filter(explode("/", $this->config['target_path']));
|
||
|
|
$folder = join("/", $build_folder);
|
||
|
|
$this->createDir($folder);
|
||
|
|
|
||
|
|
return $folder;
|
||
|
|
}
|
||
|
|
|
||
|
|
//get a file's extension
|
||
|
|
private function get_ext($fileName){
|
||
|
|
return strtolower(strrchr($fileName, '.'));
|
||
|
|
}
|
||
|
|
|
||
|
|
// check if find is image
|
||
|
|
private function isImage($fileName) {
|
||
|
|
return (in_array( $this->get_ext($fileName), ['.jpeg', '.jpg', '.gif', '.png'] ));
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
//for security reason: only allow a-z0-9_- in file name (no other dot . except for the extension)
|
||
|
|
//example: bad-file.php.js -> bad-filephp.js
|
||
|
|
private function rename_file($uploaded_file_name, $ext) {
|
||
|
|
$new_name = preg_replace("/[^a-z0-9_\-]/i", "", str_replace( strrchr($uploaded_file_name, '.'), "", $uploaded_file_name ) ) . $ext;
|
||
|
|
|
||
|
|
return strtolower($new_name);
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
private function move_file($folder) {
|
||
|
|
$new_file = $folder . '/' . $this->config['file_name'];
|
||
|
|
|
||
|
|
if( $this->config['upload_method'] == 'content' ) {
|
||
|
|
// image upload by content
|
||
|
|
if( $this->isImage($new_file) ) {
|
||
|
|
//Store in the filesystem.
|
||
|
|
$fp = fopen($new_file, "w+");
|
||
|
|
$status = fwrite($fp, $this->config['file_content']);
|
||
|
|
fclose($fp);
|
||
|
|
|
||
|
|
return ($status !== false);
|
||
|
|
}
|
||
|
|
// file upload
|
||
|
|
else if( file_put_contents($new_file, $this->config['file_content'])){
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
protected function createDir($path, $folder_permission = 0750){
|
||
|
|
if(file_exists($path)) {
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
return mkdir($path, $folder_permission, true);
|
||
|
|
}
|
||
|
|
|
||
|
|
protected function deleteDirectory($dirPath) {
|
||
|
|
if (!is_dir($dirPath)) {
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
$objects = scandir($dirPath);
|
||
|
|
foreach ($objects as $object) {
|
||
|
|
if ($object != "." && $object !="..") {
|
||
|
|
if (filetype($dirPath . DIRECTORY_SEPARATOR . $object) == "dir") {
|
||
|
|
$this->deleteDirectory($dirPath . DIRECTORY_SEPARATOR . $object);
|
||
|
|
} else {
|
||
|
|
unlink($dirPath . DIRECTORY_SEPARATOR . $object);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
return rmdir($dirPath);
|
||
|
|
}
|
||
|
|
|
||
|
|
}
|