SDK for PHP를 사용한 S3 디렉터리 버킷 예제 - AWS SDK 코드 예제

AWS Doc SDK ExamplesWord AWS SDK 리포지토리에는 더 많은 GitHub 예제가 있습니다.

기계 번역으로 제공되는 번역입니다. 제공된 번역과 원본 영어의 내용이 상충하는 경우에는 영어 버전이 우선합니다.

SDK for PHP를 사용한 S3 디렉터리 버킷 예제

다음 코드 예제에서는 S3 디렉터리 버킷과 AWS SDK for PHP 함께를 사용하여 작업을 수행하고 일반적인 시나리오를 구현하는 방법을 보여줍니다.

기본 사항은 서비스 내에서 필수 작업을 수행하는 방법을 보여주는 코드 예제입니다.

각 예제에는 컨텍스트에서 코드를 설정하고 실행하는 방법에 대한 지침을 찾을 수 있는 전체 소스 코드에 대한 링크가 포함되어 있습니다.

기본 사항

다음 코드 예시는 다음과 같은 작업을 수행하는 방법을 보여줍니다.

  • VPC 및 VPC 엔드포인트 설정

  • S3 Express 버킷을 사용하도록 S3 Express 정책, 역할 및 사용자 설정

  • 두 개의 S3 클라이언트 생성

  • 버킷 2개 생성

  • 객체 생성 및 복사

  • 성능 차이 입증

  • 버킷을 채워 어휘 차이를 표시합니다.

  • 사용자에게 리소스를 정리할지 확인하도록 요청합니다.

PHP용 SDK
참고

더 많은 on GitHub가 있습니다. AWS 코드 예시 리포지토리에서 전체 예시를 찾고 설정 및 실행하는 방법을 배워보세요.

Amazon S3 Express One Zone의 기본 사항을 보여주는 시나리오를 실행합니다.

echo "\n"; echo "--------------------------------------\n"; echo "Welcome to the Amazon S3 Express Basics demo using PHP!\n"; echo "--------------------------------------\n"; // Change these both of these values to use a different region/availability zone. $region = "us-west-2"; $az = "usw2-az1"; $this->s3Service = new S3Service(new S3Client(['region' => $region])); $this->iamService = new IAMService(new IamClient(['region' => $region])); $uuid = uniqid(); echo <<<INTRO Let's get started! First, please note that S3 Express One Zone works best when working within the AWS infrastructure, specifically when working in the same Availability Zone. To see the best results in this example, and when you implement Directory buckets into your infrastructure, it is best to put your Compute resources in the same AZ as your Directory bucket.\n INTRO; pressEnter(); // 1. Configure a gateway VPC endpoint. This is the recommended method to allow S3 Express One Zone traffic without // the need to pass through an internet gateway or NAT device. echo "\n"; echo "1. First, we'll set up a new VPC and VPC Endpoint if this program is running in an EC2 instance in the same AZ as your Directory buckets will be.\n"; $ec2Choice = testable_readline("Are you running this in an EC2 instance located in the same AZ as your intended Directory buckets? Enter Y/y to setup a VPC Endpoint, or N/n/blank to skip this section."); if($ec2Choice == "Y" || $ec2Choice == "y") { echo "Great! Let's set up a VPC, retrieve the Route Table from it, and create a VPC Endpoint to connect the S3 Client to.\n"; pressEnter(); $this->ec2Service = new EC2Service(new Ec2Client(['region' => $region])); $cidr = "10.0.0.0/16"; $vpc = $this->ec2Service->createVpc($cidr); $this->resources['vpcId'] = $vpc['VpcId']; $this->ec2Service->waitForVpcAvailable($vpc['VpcId']); $routeTable = $this->ec2Service->describeRouteTables([], [ [ 'Name' => "vpc-id", 'Values' => [$vpc['VpcId']], ], ]); $serviceName = "com.amazonaws." . $this->ec2Service->getRegion() . ".s3express"; $vpcEndpoint = $this->ec2Service->createVpcEndpoint($serviceName, $vpc['VpcId'], [$routeTable[0]]); $this->resources['vpcEndpointId'] = $vpcEndpoint['VpcEndpointId']; }else{ echo "Skipping the VPC setup. Don't forget to use this in production!\n"; } // 2. Policies, user, and roles with CDK. echo "\n"; echo "2. Policies, users, and roles with CDK.\n"; echo "Now, we'll set up some policies, roles, and a user. This user will only have permissions to do S3 Express One Zone actions.\n"; pressEnter(); $this->cloudFormationClient = new CloudFormationClient([]); $stackName = "cfn-stack-s3-express-basics-" . uniqid(); $file = file_get_contents(__DIR__ . "/../../../../resources/cfn/s3_express_basics/s3_express_template.yml"); $result = $this->cloudFormationClient->createStack([ 'StackName' => $stackName, 'TemplateBody' => $file, 'Capabilities' => ['CAPABILITY_IAM'], ]); $waiter = $this->cloudFormationClient->getWaiter("StackCreateComplete", ['StackName' => $stackName]); try { $waiter->promise()->wait(); }catch(CloudFormationException $caught){ echo "Error waiting for the CloudFormation stack to create: {$caught->getAwsErrorMessage()}\n"; throw $caught; } $this->resources['stackName'] = $stackName; $stackInfo = $this->cloudFormationClient->describeStacks([ 'StackName' => $result['StackId'], ]); $expressUserName = ""; $regularUserName = ""; foreach($stackInfo['Stacks'][0]['Outputs'] as $output) { if ($output['OutputKey'] == "RegularUser") { $regularUserName = $output['OutputValue']; } if ($output['OutputKey'] == "ExpressUser") { $expressUserName = $output['OutputValue']; } } $regularKey = $this->iamService->createAccessKey($regularUserName); $regularCredentials = new Credentials($regularKey['AccessKeyId'], $regularKey['SecretAccessKey']); $expressKey = $this->iamService->createAccessKey($expressUserName); $expressCredentials = new Credentials($expressKey['AccessKeyId'], $expressKey['SecretAccessKey']); // 3. Create an additional client using the credentials with S3 Express permissions. echo "\n"; echo "3. Create an additional client using the credentials with S3 Express permissions.\n"; echo "This client is created with the credentials associated with the user account with the S3 Express policy attached, so it can perform S3 Express operations.\n"; pressEnter(); $s3RegularClient = new S3Client([ 'Region' => $region, 'Credentials' => $regularCredentials, ]); $s3RegularService = new S3Service($s3RegularClient); $s3ExpressClient = new S3Client([ 'Region' => $region, 'Credentials' => $expressCredentials, ]); $s3ExpressService = new S3Service($s3ExpressClient); echo "All the roles and policies were created an attached to the user. Then, a new S3 Client and Service were created using that user's credentials.\n"; echo "We can now use this client to make calls to S3 Express operations. Keeping permissions in mind (and adhering to least-privilege) is crucial to S3 Express.\n"; pressEnter(); // 4. Create two buckets. echo "\n"; echo "3. Create two buckets.\n"; echo "Now we will create a Directory bucket, which is the linchpin of the S3 Express One Zone service.\n"; echo "Directory buckets behave in different ways from regular S3 buckets, which we will explore here.\n"; echo "We'll also create a normal bucket, put an object into the normal bucket, and copy it over to the Directory bucket.\n"; pressEnter(); // Create a directory bucket. These are different from normal S3 buckets in subtle ways. $directoryBucketName = "s3-express-demo-directory-bucket-$uuid--$az--x-s3"; echo "Now, let's create the actual Directory bucket, as well as a regular bucket.\n"; pressEnter(); $s3ExpressService->createBucket($directoryBucketName, [ 'CreateBucketConfiguration' => [ 'Bucket' => [ 'Type' => "Directory", // This is what causes S3 to create a Directory bucket as opposed to a normal bucket. 'DataRedundancy' => "SingleAvailabilityZone", ], 'Location' => [ 'Name' => $az, 'Type' => "AvailabilityZone", ], ], ]); $this->resources['directoryBucketName'] = $directoryBucketName; // Create a normal bucket. $normalBucketName = "normal-bucket-$uuid"; $s3RegularService->createBucket($normalBucketName); $this->resources['normalBucketName'] = $normalBucketName; echo "Great! Both buckets were created.\n"; pressEnter(); // 5. Create an object and copy it over. echo "\n"; echo "5. Create an object and copy it over.\n"; echo "We'll create a basic object consisting of some text and upload it to the normal bucket.\n"; echo "Next, we'll copy the object into the Directory bucket using the regular client.\n"; echo "This works fine, because Copy operations are not restricted for Directory buckets.\n"; pressEnter(); $objectKey = "basic-text-object"; $s3RegularService->putObject($normalBucketName, $objectKey, $args = ['Body' => "Look Ma, I'm a bucket!"]); $this->resources['objectKey'] = $objectKey; // Create a session to access the directory bucket. The SDK Client will automatically refresh this as needed. $s3ExpressService->createSession($directoryBucketName); $s3ExpressService->copyObject($directoryBucketName, $objectKey, "$normalBucketName/$objectKey"); echo "It worked! It's important to remember the user permissions when interacting with Directory buckets.\n"; echo "Instead of validating permissions on every call as normal buckets do, Directory buckets utilize the user credentials and session token to validate.\n"; echo "This allows for much faster connection speeds on every call. For single calls, this is low, but for many concurrent calls, this adds up to a lot of time saved.\n"; pressEnter(); // 6. Demonstrate performance difference. echo "\n"; echo "6. Demonstrate performance difference.\n"; $downloads = 1000; echo "Now, let's do a performance test. We'll download the same object from each bucket $downloads times and compare the total time needed. Note: the performance difference will be much more pronounced if this example is run in an EC2 instance in the same AZ as the bucket.\n"; $downloadChoice = testable_readline("If you would like to download each object $downloads times, press enter. Otherwise, enter a custom amount and press enter."); if($downloadChoice && is_numeric($downloadChoice) && $downloadChoice < 1000000){ // A million is enough. I promise. $downloads = $downloadChoice; } // Download the object $downloads times from each bucket and time it to demonstrate the speed difference. $directoryStartTime = hrtime(true); for($i = 0; $i < $downloads; ++$i){ $s3ExpressService->getObject($directoryBucketName, $objectKey); } $directoryEndTime = hrtime(true); $directoryTimeDiff = $directoryEndTime - $directoryStartTime; $normalStartTime = hrtime(true); for($i = 0; $i < $downloads; ++$i){ $s3RegularService->getObject($normalBucketName, $objectKey); } $normalEndTime = hrtime(true); $normalTimeDiff = $normalEndTime - $normalStartTime; echo "The directory bucket took $directoryTimeDiff nanoseconds, while the normal bucket took $normalTimeDiff.\n"; echo "That's a difference of " . ($normalTimeDiff - $directoryTimeDiff) . " nanoseconds, or " . (($normalTimeDiff - $directoryTimeDiff)/1000000000) . " seconds.\n"; pressEnter(); // 7. Populate the buckets to show the lexicographical difference. echo "\n"; echo "7. Populate the buckets to show the lexicographical difference.\n"; echo "Now let's explore how Directory buckets store objects in a different manner to regular buckets.\n"; echo "The key is in the name \"Directory!\"\n"; echo "Where regular buckets store their key/value pairs in a flat manner, Directory buckets use actual directories/folders.\n"; echo "This allows for more rapid indexing, traversing, and therefore retrieval times!\n"; echo "The more segmented your bucket is, with lots of directories, sub-directories, and objects, the more efficient it becomes.\n"; echo "This structural difference also causes ListObjects to behave differently, which can cause unexpected results.\n"; echo "Let's add a few more objects with layered directories as see how the output of ListObjects changes.\n"; pressEnter(); // Populate a few more files in each bucket so that we can use ListObjects and show the difference. $otherObject = "other/$objectKey"; $altObject = "alt/$objectKey"; $otherAltObject = "other/alt/$objectKey"; $s3ExpressService->putObject($directoryBucketName, $otherObject); $s3RegularService->putObject($normalBucketName, $otherObject); $this->resources['otherObject'] = $otherObject; $s3ExpressService->putObject($directoryBucketName, $altObject); $s3RegularService->putObject($normalBucketName, $altObject); $this->resources['altObject'] = $altObject; $s3ExpressService->putObject($directoryBucketName, $otherAltObject); $s3RegularService->putObject($normalBucketName, $otherAltObject); $this->resources['otherAltObject'] = $otherAltObject; $listDirectoryBucket = $s3ExpressService->listObjects($directoryBucketName); $listNormalBucket = $s3RegularService->listObjects($normalBucketName); // Directory bucket content echo "Directory bucket content\n"; foreach($listDirectoryBucket['Contents'] as $result){ echo $result['Key'] . "\n"; } // Normal bucket content echo "\nNormal bucket content\n"; foreach($listNormalBucket['Contents'] as $result){ echo $result['Key'] . "\n"; } echo "Notice how the normal bucket lists objects in lexicographical order, while the directory bucket does not. This is because the normal bucket considers the whole \"key\" to be the object identifies, while the directory bucket actually creates directories and uses the object \"key\" as a path to the object.\n"; pressEnter(); echo "\n"; echo "That's it for our tour of the basic operations for S3 Express One Zone.\n"; $cleanUp = testable_readline("Would you like to delete all the resources created during this demo? Enter Y/y to delete all the resources."); if($cleanUp){ $this->cleanUp(); } namespace S3; use Aws\CommandInterface; use Aws\Exception\AwsException; use Aws\Result; use Aws\S3\Exception\S3Exception; use Aws\S3\S3Client; use AwsUtilities\AWSServiceClass; use DateTimeInterface; class S3Service extends AWSServiceClass { protected S3Client $client; protected bool $verbose; public function __construct(S3Client $client = null, $verbose = false) { if ($client) { $this->client = $client; } else { $this->client = new S3Client([ 'version' => 'latest', 'region' => 'us-west-2', ]); } $this->verbose = $verbose; } public function setVerbose($verbose) { $this->verbose = $verbose; } public function isVerbose(): bool { return $this->verbose; } public function getClient(): S3Client { return $this->client; } public function setClient(S3Client $client) { $this->client = $client; } public function emptyAndDeleteBucket($bucketName, array $args = []) { try { $objects = $this->listAllObjects($bucketName, $args); $this->deleteObjects($bucketName, $objects, $args); if ($this->verbose) { echo "Deleted all objects and folders from $bucketName.\n"; } $this->deleteBucket($bucketName, $args); } catch (AwsException $exception) { if ($this->verbose) { echo "Failed to delete $bucketName with error: {$exception->getMessage()}\n"; echo "\nPlease fix error with bucket deletion before continuing.\n"; } throw $exception; } } public function createBucket(string $bucketName, array $args = []) { $parameters = array_merge(['Bucket' => $bucketName], $args); try { $this->client->createBucket($parameters); if ($this->verbose) { echo "Created the bucket named: $bucketName.\n"; } } catch (AwsException $exception) { if ($this->verbose) { echo "Failed to create $bucketName with error: {$exception->getMessage()}\n"; echo "Please fix error with bucket creation before continuing."; } throw $exception; } } public function putObject(string $bucketName, string $key, array $args = []) { $parameters = array_merge(['Bucket' => $bucketName, 'Key' => $key], $args); try { $this->client->putObject($parameters); if ($this->verbose) { echo "Uploaded the object named: $key to the bucket named: $bucketName.\n"; } } catch (AwsException $exception) { if ($this->verbose) { echo "Failed to create $key in $bucketName with error: {$exception->getMessage()}\n"; echo "Please fix error with object uploading before continuing."; } throw $exception; } } public function getObject(string $bucketName, string $key, array $args = []): Result { $parameters = array_merge(['Bucket' => $bucketName, 'Key' => $key], $args); try { $object = $this->client->getObject($parameters); if ($this->verbose) { echo "Downloaded the object named: $key to the bucket named: $bucketName.\n"; } } catch (AwsException $exception) { if ($this->verbose) { echo "Failed to download $key from $bucketName with error: {$exception->getMessage()}\n"; echo "Please fix error with object downloading before continuing."; } throw $exception; } return $object; } public function copyObject($bucketName, $key, $copySource, array $args = []) { $parameters = array_merge(['Bucket' => $bucketName, 'Key' => $key, "CopySource" => $copySource], $args); try { $this->client->copyObject($parameters); if ($this->verbose) { echo "Copied the object from: $copySource in $bucketName to: $key.\n"; } } catch (AwsException $exception) { if ($this->verbose) { echo "Failed to copy $copySource in $bucketName with error: {$exception->getMessage()}\n"; echo "Please fix error with object copying before continuing."; } throw $exception; } } public function listObjects(string $bucketName, $start = 0, $max = 1000, array $args = []) { $parameters = array_merge(['Bucket' => $bucketName, 'Marker' => $start, "MaxKeys" => $max], $args); try { $objects = $this->client->listObjectsV2($parameters); if ($this->verbose) { echo "Retrieved the list of objects from: $bucketName.\n"; } } catch (AwsException $exception) { if ($this->verbose) { echo "Failed to retrieve the objects from $bucketName with error: {$exception->getMessage()}\n"; echo "Please fix error with list objects before continuing."; } throw $exception; } return $objects; } public function listAllObjects($bucketName, array $args = []) { $parameters = array_merge(['Bucket' => $bucketName], $args); $contents = []; $paginator = $this->client->getPaginator("ListObjectsV2", $parameters); foreach ($paginator as $result) { if($result['KeyCount'] == 0){ break; } foreach ($result['Contents'] as $object) { $contents[] = $object; } } return $contents; } public function deleteObjects(string $bucketName, array $objects, array $args = []) { $listOfObjects = array_map( function ($object) { return ['Key' => $object]; }, array_column($objects, 'Key') ); if(!$listOfObjects){ return; } $parameters = array_merge(['Bucket' => $bucketName, 'Delete' => ['Objects' => $listOfObjects]], $args); try { $this->client->deleteObjects($parameters); if ($this->verbose) { echo "Deleted the list of objects from: $bucketName.\n"; } } catch (AwsException $exception) { if ($this->verbose) { echo "Failed to delete the list of objects from $bucketName with error: {$exception->getMessage()}\n"; echo "Please fix error with object deletion before continuing."; } throw $exception; } } public function deleteBucket(string $bucketName, array $args = []) { $parameters = array_merge(['Bucket' => $bucketName], $args); try { $this->client->deleteBucket($parameters); if ($this->verbose) { echo "Deleted the bucket named: $bucketName.\n"; } } catch (AwsException $exception) { if ($this->verbose) { echo "Failed to delete $bucketName with error: {$exception->getMessage()}\n"; echo "Please fix error with bucket deletion before continuing."; } throw $exception; } } public function deleteObject(string $bucketName, string $fileName, array $args = []) { $parameters = array_merge(['Bucket' => $bucketName, 'Key' => $fileName], $args); try { $this->client->deleteObject($parameters); if ($this->verbose) { echo "Deleted the object named: $fileName from $bucketName.\n"; } } catch (AwsException $exception) { if ($this->verbose) { echo "Failed to delete $fileName from $bucketName with error: {$exception->getMessage()}\n"; echo "Please fix error with object deletion before continuing."; } throw $exception; } } public function listBuckets(array $args = []) { try { $buckets = $this->client->listBuckets($args); if ($this->verbose) { echo "Retrieved all " . count($buckets) . "\n"; } } catch (AwsException $exception) { if ($this->verbose) { echo "Failed to retrieve bucket list with error: {$exception->getMessage()}\n"; echo "Please fix error with bucket lists before continuing."; } throw $exception; } return $buckets; } public function preSignedUrl(CommandInterface $command, DateTimeInterface|int|string $expires, array $options = []) { $request = $this->client->createPresignedRequest($command, $expires, $options); try { $presignedUrl = (string)$request->getUri(); } catch (AwsException $exception) { if ($this->verbose) { echo "Failed to create a presigned url: {$exception->getMessage()}\n"; echo "Please fix error with presigned urls before continuing."; } throw $exception; } return $presignedUrl; } public function createSession(string $bucketName) { try{ $result = $this->client->createSession([ 'Bucket' => $bucketName, ]); return $result; }catch(S3Exception $caught){ if($caught->getAwsErrorType() == "NoSuchBucket"){ echo "The specified bucket does not exist."; } throw $caught; } } }