2022-02-20 20:22:07 +00:00
|
|
|
<?php
|
|
|
|
/**
|
|
|
|
* Akeeba Engine
|
|
|
|
*
|
|
|
|
* @package akeebaengine
|
2024-03-20 03:03:34 +00:00
|
|
|
* @copyright Copyright (c)2006-2024 Nicholas K. Dionysopoulos / Akeeba Ltd
|
2022-02-20 20:22:07 +00:00
|
|
|
* @license GNU General Public License version 3, or later
|
|
|
|
*/
|
|
|
|
|
|
|
|
namespace Akeeba\MiniTest\Test;
|
|
|
|
|
|
|
|
|
2023-12-19 02:28:16 +00:00
|
|
|
use Akeeba\S3\Connector;
|
|
|
|
use Akeeba\S3\Input;
|
2022-02-20 20:22:07 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Upload, download and delete big files (over 1MB), without multipart uploads. Uses string or file sources.
|
|
|
|
*
|
|
|
|
* @package Akeeba\MiniTest\Test
|
|
|
|
*/
|
|
|
|
class BigFiles extends AbstractTest
|
|
|
|
{
|
|
|
|
/**
|
|
|
|
* Should I download the file after uploading it to test for contents consistency?
|
|
|
|
*
|
|
|
|
* @var bool
|
|
|
|
*/
|
|
|
|
protected static $downloadAfter = true;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Should I delete the uploaded file after the test case is done?
|
|
|
|
*
|
|
|
|
* @var bool
|
|
|
|
*/
|
|
|
|
protected static $deleteRemote = true;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Should I use multipart (chunked) uploads?
|
|
|
|
*
|
|
|
|
* @var bool
|
|
|
|
*/
|
|
|
|
protected static $multipart = false;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Chunk size for each multipart upload. Must be at least 5MB or the library overrides us.
|
|
|
|
*
|
|
|
|
* @var int
|
|
|
|
*/
|
|
|
|
protected static $uploadChunkSize = 5242880;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Number of uploaded chunks.
|
|
|
|
*
|
2023-12-19 02:28:16 +00:00
|
|
|
* This is set by static::upload(). Zero for single part uploads, non-zero for multipart uploads.
|
2022-02-20 20:22:07 +00:00
|
|
|
*
|
|
|
|
* @var int
|
|
|
|
*/
|
|
|
|
protected static $numberOfChunks = 0;
|
|
|
|
|
|
|
|
public static function upload5MBString(Connector $s3, array $options): bool
|
|
|
|
{
|
2023-12-19 02:28:16 +00:00
|
|
|
return static::upload($s3, $options, static::FIVE_MB, 'bigtest_5mb.dat');
|
2022-02-20 20:22:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
public static function upload6MBString(Connector $s3, array $options): bool
|
|
|
|
{
|
2023-12-19 02:28:16 +00:00
|
|
|
return static::upload($s3, $options, static::SIX_MB, 'bigtest_6mb.dat');
|
2022-02-20 20:22:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
public static function upload10MBString(Connector $s3, array $options): bool
|
|
|
|
{
|
2023-12-19 02:28:16 +00:00
|
|
|
return static::upload($s3, $options, static::TEN_MB, 'bigtest_10mb.dat');
|
2022-02-20 20:22:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
public static function upload11MBString(Connector $s3, array $options): bool
|
|
|
|
{
|
2023-12-19 02:28:16 +00:00
|
|
|
return static::upload($s3, $options, static::ELEVEN_MB, 'bigtest_11mb.dat');
|
2022-02-20 20:22:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
public static function upload5MBFile(Connector $s3, array $options): bool
|
|
|
|
{
|
2023-12-19 02:28:16 +00:00
|
|
|
return static::upload($s3, $options, static::FIVE_MB, 'bigtest_5mb.dat', false);
|
2022-02-20 20:22:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
public static function upload6MBFile(Connector $s3, array $options): bool
|
|
|
|
{
|
2023-12-19 02:28:16 +00:00
|
|
|
return static::upload($s3, $options, static::SIX_MB, 'bigtest_6mb.dat', false);
|
2022-02-20 20:22:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
public static function upload10MBFile(Connector $s3, array $options): bool
|
|
|
|
{
|
2023-12-19 02:28:16 +00:00
|
|
|
return static::upload($s3, $options, static::TEN_MB, 'bigtest_10mb.dat', false);
|
2022-02-20 20:22:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
public static function upload11MBFile(Connector $s3, array $options): bool
|
|
|
|
{
|
2023-12-19 02:28:16 +00:00
|
|
|
return static::upload($s3, $options, static::ELEVEN_MB, 'bigtest_11mb.dat', false);
|
2022-02-20 20:22:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
protected static function upload(Connector $s3, array $options, int $size, string $uri, bool $useString = true): bool
|
|
|
|
{
|
|
|
|
// Randomize the name. Required for archive buckets where you cannot overwrite data.
|
|
|
|
$dotPos = strrpos($uri, '.');
|
|
|
|
$uri = substr($uri, 0, $dotPos) . '.' . md5(microtime(false)) . substr($uri, $dotPos);
|
|
|
|
|
2023-12-19 02:28:16 +00:00
|
|
|
static::$numberOfChunks = 0;
|
2022-02-20 20:22:07 +00:00
|
|
|
|
|
|
|
if ($useString)
|
|
|
|
{
|
2023-12-19 02:28:16 +00:00
|
|
|
$sourceData = static::getRandomData($size);
|
2022-02-20 20:22:07 +00:00
|
|
|
$input = Input::createFromData($sourceData);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// Create a file with random data
|
2023-12-19 02:28:16 +00:00
|
|
|
$sourceFile = static::createFile($size);
|
2022-02-20 20:22:07 +00:00
|
|
|
$input = Input::createFromFile($sourceFile);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Upload the file. Throws exception if it fails.
|
|
|
|
$bucket = $options['bucket'];
|
|
|
|
|
2023-12-19 02:28:16 +00:00
|
|
|
if (!static::$multipart)
|
2022-02-20 20:22:07 +00:00
|
|
|
{
|
|
|
|
$s3->putObject($input, $bucket, $uri);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// Get an upload session
|
|
|
|
$uploadSession = $s3->startMultipart($input, $bucket, $uri);
|
|
|
|
|
|
|
|
// This array holds the etags of uploaded parts. Used by finalizeMultipart.
|
|
|
|
$eTags = [];
|
|
|
|
$partNumber = 1;
|
|
|
|
|
|
|
|
while (true)
|
|
|
|
{
|
|
|
|
// We need to create a new input for each upload chunk
|
|
|
|
if ($useString)
|
|
|
|
{
|
|
|
|
$input = Input::createFromData($sourceData);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
$input = Input::createFromFile($sourceFile);
|
|
|
|
}
|
|
|
|
|
|
|
|
$input->setUploadID($uploadSession);
|
|
|
|
$input->setEtags($eTags);
|
|
|
|
$input->setPartNumber($partNumber);
|
|
|
|
|
2023-12-19 02:28:16 +00:00
|
|
|
$etag = $s3->uploadMultipart($input, $bucket, $uri, [], static::$uploadChunkSize);
|
2022-02-20 20:22:07 +00:00
|
|
|
|
|
|
|
// If the result was null we have no more file parts to process.
|
|
|
|
if (is_null($etag))
|
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Append the etag to the etags array
|
|
|
|
$eTags[] = $etag;
|
|
|
|
|
|
|
|
// Set the etags array in the Input object (required by finalizeMultipart)
|
|
|
|
$input->setEtags($eTags);
|
|
|
|
|
|
|
|
$partNumber++;
|
|
|
|
}
|
|
|
|
|
2023-12-19 02:28:16 +00:00
|
|
|
static::$numberOfChunks = count($eTags);
|
2022-02-20 20:22:07 +00:00
|
|
|
|
|
|
|
// Finalize the multipart upload. Tells Amazon to construct the file from the uploaded parts.
|
|
|
|
$s3->finalizeMultipart($input, $bucket, $uri);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Tentatively accept that this method succeeded.
|
|
|
|
$result = true;
|
|
|
|
|
|
|
|
// Should I download the file and compare its contents?
|
2023-12-19 02:28:16 +00:00
|
|
|
if (static::$downloadAfter)
|
2022-02-20 20:22:07 +00:00
|
|
|
{
|
|
|
|
if ($useString)
|
|
|
|
{
|
|
|
|
// Download the data. Throws exception if it fails.
|
|
|
|
$downloadedData = $s3->getObject($bucket, $uri);
|
|
|
|
|
|
|
|
// Compare the file contents.
|
2023-12-19 02:28:16 +00:00
|
|
|
$result = static::areStringsEqual($sourceData, $downloadedData);
|
2022-02-20 20:22:07 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// Download the data. Throws exception if it fails.
|
2023-12-19 02:28:16 +00:00
|
|
|
$downloadedFile = tempnam(static::getTempFolder(), 'as3');
|
2022-02-20 20:22:07 +00:00
|
|
|
$s3->getObject($bucket, $uri, $downloadedFile);
|
|
|
|
|
|
|
|
// Compare the file contents.
|
2023-12-19 02:28:16 +00:00
|
|
|
$result = static::areFilesEqual($sourceFile, $downloadedFile);
|
2022-02-20 20:22:07 +00:00
|
|
|
|
|
|
|
@unlink($downloadedFile);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Remove the local files
|
|
|
|
if (!$useString)
|
|
|
|
{
|
|
|
|
@unlink($sourceFile);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Should I delete the remotely stored file?
|
2023-12-19 02:28:16 +00:00
|
|
|
if (static::$deleteRemote)
|
2022-02-20 20:22:07 +00:00
|
|
|
{
|
|
|
|
// Delete the remote file. Throws exception if it fails.
|
|
|
|
$s3->deleteObject($bucket, $uri);
|
|
|
|
}
|
|
|
|
|
|
|
|
return $result;
|
|
|
|
}
|
|
|
|
}
|