File Manager

Current Directory: /home/astoriaah/www/administrator/components/com_akeeba/BackupEngine/Postproc
Viewing File: /home/astoriaah/www/administrator/components/com_akeeba/BackupEngine/Postproc/Azure.php
<?php /** * Akeeba Engine * * @package akeebaengine * @copyright Copyright (c)2006-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace Akeeba\Engine\Postproc; defined('AKEEBAENGINE') || die(); use Akeeba\Engine\Factory; use Akeeba\Engine\Postproc\Connector\AzureModern\Connector as AzureConnector; use Akeeba\Engine\Postproc\Exception\BadConfiguration; use Akeeba\Engine\Postproc\Exception\RangeDownloadNotSupported; class Azure extends Base { public function __construct() { $this->supportsDelete = true; $this->supportsDownloadToFile = true; $this->supportsDownloadToBrowser = true; $this->inlineDownloadToBrowser = false; } public function processPart($localFilepath, $remoteBaseName = null) { // Retrieve engine configuration data $config = Factory::getConfiguration(); $container = $config->get('engine.postproc.azure.container', 0); $disableMultipart = $config->get('engine.postproc.azure.chunk_upload', 1) == 0; $preferredChunkSize = $config->get('engine.postproc.azure.chunk_upload_size', 10) * 1048576; $directory = $config->get('volatile.postproc.directory', null); $partList = $config->get('volatile.postproc.partList', []); // Treat directory and place it in volatile storage if it's not already there if (is_null($directory)) { $directory = $config->get('engine.postproc.azure.directory', ''); $directory = trim($directory); $directory = ltrim(Factory::getFilesystemTools()->TranslateWinPath($directory), '/'); $directory = empty($directory) ? '' : $directory; $directory = Factory::getFilesystemTools()->replace_archive_name_variables($directory); $config->set('volatile.postproc.directory', $directory); } // Calculate relative remote filename $filename = basename($localFilepath); if (!empty($directory) && ($directory != '/')) { $filename = $directory . '/' . $filename; } // Store the absolute remote path in the class property $this->remotePath = $filename; // Get the connector /** @var AzureConnector $connector */ $connector = $this->getConnector(); // Get the total file size @clearstatcache($localFilepath); $fileSize = @filesize($localFilepath) ?: 0; // Get facts about multipart support $canDoSinglePart = $connector->getBestBlockSize($localFilepath, 0) === 0; $chunkSize = $config->get('volatile.postproc.chunkSize', null); $chunkSize = $chunkSize ?: $connector->getBestBlockSize($localFilepath, $preferredChunkSize); // Only allow $disableMultipart if I can do a single part upload. $disableMultipart = $disableMultipart && $canDoSinglePart; // Force disable multipart if the file size is under the chunk size. $disableMultipart = $disableMultipart || ($canDoSinglePart && $fileSize <= $chunkSize); $config->set('volatile.postproc.chunkSize', $chunkSize); // Simplest case: single part upload if ($disableMultipart) { Factory::getLog()->debug(sprintf('Azure BLOB Storage -- Using single part upload of %s', $localFilepath)); $config->remove('volatile.postproc.directory'); $config->remove('volatile.postproc.partList'); $config->remove('volatile.postproc.chunkSize'); $connector->putBlob($container, $filename, $localFilepath); return true; } // Multipart upload. $chunksProcessed = count($partList); $offset = $chunksProcessed * $chunkSize; if (empty($offset)) { Factory::getLog()->debug( sprintf( 'Azure BLOB Storage -- Starting multipart upload of %s (total size %d, chunk size %d)', $localFilepath, $fileSize, $chunkSize ) ); } else { Factory::getLog()->debug( sprintf( 'Azure BLOB Storage -- Continuing multipart upload of %s, chunk #%d, chunk size %d', $localFilepath, $chunksProcessed + 1, $chunkSize ) ); } $fp = @fopen($localFilepath, 'r'); if ($fp === false) { // TODO Throw } if ($offset > 0) { fseek($fp, $offset); } $data = fread($fp, $chunkSize); $lastChunk = feof($fp); fclose($fp); $partList[] = $connector->putBlock($container, $filename, $data); $config->set('volatile.postproc.partList', $partList); // If we have not reached EOF there's more work to do uploading this file if (!$lastChunk) { return false; } // If we're here, we have finished uploading all chunks. Let's tell Azure to stitch them together. Factory::getLog()->debug(sprintf('Azure BLOB Storage -- Finalising multipart upload of %s', $localFilepath)); $connector->putBlockList($container, $filename, $partList); Factory::getLog()->debug(sprintf('Azure BLOB Storage -- Multipart uploading of %s is now complete', $localFilepath)); // Teardown of volatile data and return marking everything as all done $config->remove('volatile.postproc.directory'); $config->remove('volatile.postproc.partList'); $config->remove('volatile.postproc.chunkSize'); return true; } public function delete($path) { $connector = $this->getConnector(); $config = Factory::getConfiguration(); $container = $config->get('engine.postproc.azure.container', 0); $connector->deleteBlob($container, $path); } public function downloadToFile($remotePath, $localFile, $fromOffset = null, $length = null) { if (!is_null($fromOffset)) { // Ranges are not supported throw new RangeDownloadNotSupported(); } /** @var AzureConnector $connector */ $connector = $this->getConnector(); $config = Factory::getConfiguration(); $container = $config->get('engine.postproc.azure.container', 0); $connector->getBlob($container, $remotePath, $localFile); } public function downloadToBrowser($remotePath) { /** @var AzureConnector $connector */ $connector = $this->getConnector(); $config = Factory::getConfiguration(); $container = $config->get('engine.postproc.azure.container', 0); return $connector->getSignedURL($container, $remotePath, 600); } protected function makeConnector() { $config = Factory::getConfiguration(); $account = trim($config->get('engine.postproc.azure.account', '')); $key = trim($config->get('engine.postproc.azure.key', '')); $container = $config->get('engine.postproc.azure.container', 0); $useSSL = $config->get('engine.postproc.azure.usessl', 1) == 1; // Sanity checks if (!function_exists('curl_init')) { throw new BadConfiguration('cURL is not enabled, please enable it in order to post-process your archives'); } if (empty($account)) { throw new BadConfiguration('You have not set up your Microsoft Azure account name'); } if (empty($key)) { throw new BadConfiguration('You have not set up your Microsoft Azure account key'); } if (empty($container)) { throw new BadConfiguration('You have not set up your Microsoft Azure container name'); } return new AzureConnector($account, $key, false, $useSSL); } }