From e6753d7bdb7e165806bdc0cc1cf2392dffff81ec Mon Sep 17 00:00:00 2001 From: Chris Date: Fri, 23 Mar 2018 20:34:34 +0100 Subject: [PATCH] added support for backblaze buckets for scalability. closes #49 --- .gitignore | 1 + classes/backblaze.php | 275 +++++++++++++++++++++++++++++++++++++ inc/example.config.inc.php | 14 +- models/pictsharemodel.php | 31 ++++- 4 files changed, 319 insertions(+), 2 deletions(-) create mode 100644 .gitignore create mode 100644 classes/backblaze.php diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2b0fc6c --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +test.php \ No newline at end of file diff --git a/classes/backblaze.php b/classes/backblaze.php new file mode 100644 index 0000000..22ec6a0 --- /dev/null +++ b/classes/backblaze.php @@ -0,0 +1,275 @@ + + */ + +class Backblaze +{ + private $token; + private $apiURL; + private $bucket; + private $dlURL; + private $ulURL; + private $ulToken; + private $bucket_name; + private $files; + + function __construct() + { + if( BACKBLAZE !== true || !defined('BACKBLAZE_ID') || !defined('BACKBLAZE_KEY') || !defined('BACKBLAZE_BUCKET_ID')) + return; + $this->authorize(); + $this->bucket = BACKBLAZE_BUCKET_ID; + $this->bucket_name = (( defined('BACKBLAZE_BUCKET_NAME') && BACKBLAZE_BUCKET_NAME != "")?BACKBLAZE_BUCKET_NAME:$this->bucketIdToName($bucket)); + } + + function authorize() + { + $account_id = BACKBLAZE_ID; // Obtained from your B2 account page + $application_key = BACKBLAZE_KEY; // Obtained from your B2 account page + $credentials = base64_encode($account_id . ":" . $application_key); + $url = "https://api.backblazeb2.com/b2api/v1/b2_authorize_account"; + + $session = curl_init($url); + + // Add headers + $headers = array(); + $headers[] = "Accept: application/json"; + $headers[] = "Authorization: Basic " . $credentials; + curl_setopt($session, CURLOPT_HTTPHEADER, $headers); // Add headers + + curl_setopt($session, CURLOPT_HTTPGET, true); // HTTP GET + curl_setopt($session, CURLOPT_RETURNTRANSFER, true); // Receive server response + $server_output = curl_exec($session); + curl_close ($session); + $data = json_decode($server_output,true); + + $this->token = $data['authorizationToken']; + $this->apiURL = $data['apiUrl']; + $this->dlURL = $data['downloadUrl']; + } + + function upload($hash) + { + if(!$this->ulURL) + $this->getUploadInfo(); + $file_name = $hash; + $my_file = ROOT.DS.'upload'.DS.$hash.DS.$hash; + $handle = fopen($my_file, 'r'); + $read_file = fread($handle,filesize($my_file)); + + $upload_url = $this->ulURL; // Provided by b2_get_upload_url + $upload_auth_token = $this->ulToken; // Provided by b2_get_upload_url + $bucket_id = $this->bucket; // The ID of the bucket + $content_type = "text/plain"; + $sha1_of_file_data = sha1_file($my_file); + + $session = curl_init($upload_url); + + // Add read file as post field + curl_setopt($session, CURLOPT_POSTFIELDS, $read_file); + + // Add headers + $headers = array(); + $headers[] = "Authorization: " . $upload_auth_token; + $headers[] = "X-Bz-File-Name: " . $file_name; + $headers[] = "Content-Type: " . $content_type; + $headers[] = "X-Bz-Content-Sha1: " . $sha1_of_file_data; + curl_setopt($session, CURLOPT_HTTPHEADER, $headers); + + curl_setopt($session, CURLOPT_POST, true); // HTTP POST + curl_setopt($session, CURLOPT_RETURNTRANSFER, true); // Receive server response + $server_output = curl_exec($session); // Let's do this! + curl_close ($session); // Clean up + //var_dump($server_output); // Tell me about the rabbits, George! + } + + function getUploadInfo() + { + $api_url = $this->apiURL; // From b2_authorize_account call + $auth_token = $this->token; // From b2_authorize_account call + $bucket_id = $this->bucket; // The ID of the bucket you want to upload to + + $session = curl_init($api_url . "/b2api/v1/b2_get_upload_url"); + + // Add post fields + $data = array("bucketId" => $bucket_id); + $post_fields = json_encode($data); + curl_setopt($session, CURLOPT_POSTFIELDS, $post_fields); + + // Add headers + $headers = array(); + $headers[] = "Authorization: " . $auth_token; + curl_setopt($session, CURLOPT_HTTPHEADER, $headers); + + curl_setopt($session, CURLOPT_POST, true); // HTTP POST + curl_setopt($session, CURLOPT_RETURNTRANSFER, true); // Receive server response + $server_output = curl_exec($session); // Let's do this! + curl_close ($session); // Clean up + $data = json_decode($server_output,true); // Tell me about the rabbits, George! + $this->ulURL = $data['uploadUrl']; + $this->ulToken = $data['authorizationToken']; + } + + function download($hash) + { + if(file_exists(ROOT.DS.'upload'.DS.$hash.DS.$hash)) return false; + $download_url = $this->dlURL; // From b2_authorize_account call + $bucket_name = $this->bucket_name; // The NAME of the bucket you want to download from + $file_name = $hash; // The name of the file you want to download + $auth_token = $this->token; // From b2_authorize_account call + $uri = $download_url . "/file/" . $bucket_name . "/" . $file_name; + + $session = curl_init($uri); + + // Add headers + $headers = array(); + $headers[] = "Authorization: " . $auth_token; + curl_setopt($session, CURLOPT_HTTPHEADER, $headers); + + curl_setopt($session, CURLOPT_HTTPGET, true); // HTTP POST + curl_setopt($session, CURLOPT_RETURNTRANSFER, true); // Receive server response + $server_output = curl_exec($session); // Let's do this! + curl_close ($session); // Clean up + $is_binary = preg_match('~[^\x20-\x7E\t\r\n]~', $server_output); // Tell me about the rabbits, George! + if(!$is_binary) return false; + + mkdir(ROOT.DS.'upload'.DS.$hash); + $file = ROOT.DS.'upload'.DS.$hash.DS.$hash; + + file_put_contents($file, $server_output); + return true; + } + + function bucketIdToName($bucket) + { + $api_url = $this->apiURL; // From b2_authorize_account call + $auth_token = $this->token; // From b2_authorize_account call + $account_id = BACKBLAZE_ID; + + $session = curl_init($api_url . "/b2api/v1/b2_list_buckets"); + + // Add post fields + $data = array("accountId" => $account_id); + $post_fields = json_encode($data); + curl_setopt($session, CURLOPT_POSTFIELDS, $post_fields); + + // Add headers + $headers = array(); + $headers[] = "Authorization: " . $auth_token; + curl_setopt($session, CURLOPT_HTTPHEADER, $headers); + + curl_setopt($session, CURLOPT_POST, true); // HTTP POST + curl_setopt($session, CURLOPT_RETURNTRANSFER, true); // Receive server response + $server_output = curl_exec($session); // Let's do this! + curl_close ($session); // Clean up + $data = json_decode($server_output,true); // Tell me about the rabbits, George! + if(is_array($data)) + foreach($data['buckets'] as $bucket) + { + if($bucket['bucketId']==$this->bucket) return $bucket['bucketName']; + } + return false; + } + + function deleteFile($hash,$file_id=false) + { + $api_url = $this->apiURL; // From b2_authorize_account call + $auth_token = $this->token; // From b2_authorize_account call + $file_name = $hash; // The file name of the file you want to delete + if(!$file_id) + $file_id = $this->fileExistsInBucket($hash); + + $session = curl_init($api_url . "/b2api/v1/b2_delete_file_version"); + + // Add post fields + $data = array("fileId" => $file_id, "fileName" => $file_name); + $post_fields = json_encode($data); + curl_setopt($session, CURLOPT_POSTFIELDS, $post_fields); + + // Add headers + $headers = array(); + $headers[] = "Authorization: " . $auth_token; + curl_setopt($session, CURLOPT_HTTPHEADER, $headers); + + curl_setopt($session, CURLOPT_POST, true); // HTTP POST + curl_setopt($session, CURLOPT_RETURNTRANSFER, true); // Receive server response + $server_output = curl_exec($session); // Let's do this! + curl_close ($session); // Clean up + } + + function fileExistsInBucket($hash) + { + $api_url = $this->apiURL; // From b2_authorize_account call + $auth_token = $this->token; // From b2_authorize_account call + $bucket_id = $this->bucket; // The ID of the bucket + + $session = curl_init($api_url . "/b2api/v1/b2_list_file_names"); + + // Add post fields + $data = array("bucketId" => $bucket_id, + "startFileName" => $hash); + $post_fields = json_encode($data); + curl_setopt($session, CURLOPT_POSTFIELDS, $post_fields); + + // Add headers + $headers = array(); + $headers[] = "Authorization: " . $auth_token; + curl_setopt($session, CURLOPT_HTTPHEADER, $headers); + + curl_setopt($session, CURLOPT_POST, true); // HTTP POST + curl_setopt($session, CURLOPT_RETURNTRANSFER, true); // Receive server response + $server_output = curl_exec($session); // Let's do this! + curl_close ($session); // Clean up + $data = json_decode($server_output,true); + + foreach($data['files'] as $file) + { + //it's either the first one or it doesn't exist + if($file['fileName']==$hash) + return $file['fileId']; + else return false; + } + + return false; + } + + function getAllFilesInBucket($startFileName=null) + { + $api_url = $this->apiURL; // From b2_authorize_account call + $auth_token = $this->token; // From b2_authorize_account call + $bucket_id = $this->bucket; // The ID of the bucket + + $session = curl_init($api_url . "/b2api/v1/b2_list_file_names"); + + // Add post fields + $data = array("bucketId" => $bucket_id, + "startFileName" => $startFileName); + $post_fields = json_encode($data); + curl_setopt($session, CURLOPT_POSTFIELDS, $post_fields); + + // Add headers + $headers = array(); + $headers[] = "Authorization: " . $auth_token; + curl_setopt($session, CURLOPT_HTTPHEADER, $headers); + + curl_setopt($session, CURLOPT_POST, true); // HTTP POST + curl_setopt($session, CURLOPT_RETURNTRANSFER, true); // Receive server response + $server_output = curl_exec($session); // Let's do this! + curl_close ($session); // Clean up + $data = json_decode($server_output,true); + + foreach($data['files'] as $file) + { + $name = $file['fileName']; + $id = $file['fileId']; + $this->files[$name] = $id; + } + + if($data['nextFileName']) + $this->getAllFilesInBucket($data['nextFileName']); + } +} \ No newline at end of file diff --git a/inc/example.config.inc.php b/inc/example.config.inc.php index 631c7db..f445483 100644 --- a/inc/example.config.inc.php +++ b/inc/example.config.inc.php @@ -71,4 +71,16 @@ define('FORCE_DOMAIN', false); //Shall errors be displayed to the user? //For dev environments: true, in production: false -define('SHOW_ERRORS', false); \ No newline at end of file +define('SHOW_ERRORS', false); + + +//for scalability reasons you might want to upload images to cloud providers +//remove comments to use + +/* BACKBLAZE B2 */ +/* You can find your info here: https://secure.backblaze.com/b2_buckets.htm */ +//define('BACKBLAZE',true); //true=>use backblaze to upload images, false=>don't +//define('BACKBLAZE_ID',''); +//define('BACKBLAZE_KEY', ''); +//define('BACKBLAZE_BUCKET_ID', ''); +//define('BACKBLAZE_BUCKET_NAME', ''); \ No newline at end of file diff --git a/models/pictsharemodel.php b/models/pictsharemodel.php index a703094..b7f9d52 100644 --- a/models/pictsharemodel.php +++ b/models/pictsharemodel.php @@ -59,6 +59,15 @@ class PictshareModel extends Model else return array('status'=>'ERR','Reason'=>'Image not found'); } + + function couldThisBeAnImage($string) + { + $len = strlen($string); + $dot = strpos($string,'.'); + if(!$dot) return false; + if($dot<=10 && ( ($len-$dot) == 4 || ($len-$dot) == 5 ) ) + return true; + } function urlToData($url) { @@ -75,7 +84,7 @@ class PictshareModel extends Model if($this->isImage($el)) { - //if there are mor than one hashes in url + //if there are more than one hashes in url //make an album from them if($data['hash']) { @@ -85,6 +94,12 @@ class PictshareModel extends Model } $data['hash']=$el; } + else if(BACKBLAZE === true && $this->couldThisBeAnImage($el)) //looks like it might be a hash but didn't find it here. Let's see + { + $b = new Backblaze(); + if($b->download($el)) //if the backblaze download function says it's an image, we'll take it + $data['hash'] = $el; + } else if($el=='mp4' || $el=='raw' || $el=='preview' || $el=='webm' || $el=='ogg') $data[$el] = 1; else if($this->isSize($el)) @@ -195,6 +210,14 @@ class PictshareModel extends Model } rmdir($base_path); + + //delete from backblaze if configured + if(BACKBLAZE===true) + { + $b = new Backblaze(); + $b->deleteFile($hash); + } + return true; } @@ -466,6 +489,12 @@ class PictshareModel extends Model fclose($fh); } + if(BACKBLAZE===true) + { + $b = new Backblaze(); + $b->upload($hash); + } + return array('status'=>'OK','type'=>$type,'hash'=>$hash,'url'=>DOMAINPATH.PATH.$hash,'domain'=>DOMAINPATH,'deletecode'=>$this->generateDeleteCodeForImage($hash)); }