Quay lại

Thực Hành: Triển Khai Upload File Lên S3 Bằng Laravel Và Multipart Upload Chuyên mục S3 Service    2024-05-01    28 Lượt xem    24 Lượt thích    comment-3 Created with Sketch Beta. 0 Bình luận    capacity-filled Sidebar

   

IAM User Access To S3

Xin chào tất cả mọi người hôm nay mình sẽ hướng dẫn các bạn sử dụng s3 để lưu trữ files, với bài viết này chúng ta sẽ sử dụng 2 cách để xử lý lưu trữ.

Cách thứ nhất là sử dụng Multipart dùng để upload những files lớn lên s3 và cách thứ 2 là sử dụng Storage để xử lý lưu trữ files với kích thước vừa và nhỏ lên s3.

Trên thực tế chúng ta sẽ có 2 cách thông dụng để có thể truyền files lên s3 đó là sử dụng một IAM User và lấy secret key và access key của nó để có thể  để quản lý và truy cập vào s3, cách còn lại là sử dụng IAM Roles khi bạn chạy ứng dụng trên một môi trường EC2 hoặc Lambda, ở bài viết này mình sẽ hướng dẫn sử dụng cách IAM User (secret key và access key).

Để thực hiện được với s3 mình có một số yêu cầu sau:

  • Có tài khoản AWS.
  • Có hiểu biết cơ bản về IAM và policies.

Bước 1: Tạo Bucket

  1. Đăng nhập vào AWS Console: Đăng nhập vào AWS Console bằng tài khoản có đủ quyền để tạo và quản lý bucket trên Amazon S3.

  2. Điều hướng đến trang S3: Trong AWS Console, chọn dịch vụ "S3" từ menu tìm kiếm hoặc menu dịch vụ.

  3. Tạo một Bucket mới:

    • Nhấp vào nút "Create bucket".
    • Nhập "bucket name" cho dự án của bạn. Lưu ý rằng tên bucket phải là duy nhất trên toàn hệ thống S3 và không được thay đổi sau khi đã tạo.
    • Chọn khu vực lưu trữ (region) cho bucket. Chọn khu vực gần với người dùng của bạn để giảm độ trễ khi truy cập.
    • Ở bước Block public access (bucket settings) => Bỏ tích Block all public access
    • Đảm bảo các tùy chọn bổ sung như logging và versioning được cấu hình theo nhu cầu của bạn (optinal). Nếu bạn bật versioning thì tên file phải đảm bảo giống nhau, không cần phải thêm timestamp hoặc bất kỳ phần tử duy nhất nào khác vào tên file khi upload lên S3 khi bạn sử dụng versioning.

  4. Thiết lập quyền truy cập cho Bucket: Bucket policy được sử dụng để cung cấp quyền truy cập các tài nguyền của s3 cho các users như (IAM user, nhóm người dùng, hoặc end users) có quyền truy cập và thực hiện các hành động đọc, ghi, xóa hoặc thay đổi các metadata trên các object đó, chính vì vậy để những files như images, video của chúng ta có thể truy cập ngoài internet thì chúng ta phải cấu hình policy.

    Bucket policy có thể áp dụng cho một số đối tượng khác nhau:

    • Người dùng IAM (Identity and Access Management): Bạn có thể sử dụng bucket policy để điều chỉnh quyền truy cập vào các đối tượng trong bucket cho các người dùng IAM trong tài khoản AWS của bạn.

    • Các tài khoản AWS khác: Bạn cũng có thể cho phép các tài khoản AWS khác (không phải là tài khoản của bạn) truy cập vào các đối tượng trong bucket của bạn thông qua bucket policy.

    • Công chúng (Public): Bạn có thể cấu hình bucket policy để cho phép mọi người, bao gồm cả người dùng không đăng nhập (anonymous users), truy cập vào các đối tượng trong bucket của bạn.

    • Các bước thực hiện như sau:

    1. Vào trong bucket của bạn vừa tạo sau đó chọn tab "Permissions" kéo xuống và tìm đến "Bucket policy" sau đó thì chỉnh sửa policy theo đoạn policy dưới đây, nhớ là thay tên bucket của bạn vào phần Resource. 
      Note: Nếu trong ứng dụng của bạn không có tính năng cho phép người dùng upload ảnh thì không cần phải thêm action "s3:GetObject"
    2. {
          "Version": "2012-10-17",
          "Id": "Policy1710384852296",
          "Statement": [
              {
                  "Sid": "Stmt1710384850357",
                  "Effect": "Allow",
                  "Principal": "*",
                  "Action": [
                      "s3:GetObject",
                      "s3:PutObject"
                  ],
                  "Resource": "arn:aws:s3:::your-bucket-name/*"
              }
          ]
      }

Bước 2: Thiết Lập IAM User

  1. Đăng nhập vào AWS Console: Đăng nhập vào AWS Console bằng tài khoản có đủ quyền để thực hiện các thay đổi về IAM và S3.

  2. Tạo một IAM User mới:

    • Trong AWS Console, điều hướng đến trang IAM.
    • Chọn "Users" từ menu bên trái và nhấn vào nút "Add user".
    • Nhập tên cho user và chọn loại truy cập (giữ mặc định là "Programmatic access" hoặc "Provide user access to the AWS Management Console".
    • Vào Tab "Security credentials" trong user bạn vừa tạo sau đó tìm đến block "Access keys" để có thể tạo Access Key và Secret Access Key.
    • Tiếp theo, chọn quyền (policies) cho user này. Bạn có thể chọn một policy có sẵn hoặc tạo một policy mới.
  3. Tạo hoặc áp dụng một Policy cho IAM User:

    • Chúng ta nên custom một policy mới cho user này để chúng ta dễ kiểm soát những actions của user trên tài nguyên s3 thay vì chọn một cái "AmazonS3FullAccess" nha:
      • Click vào user bạn vừa tạo, sau đó chọn "Add permissions".
      • Sau đó chọn "Create inline policy".
      • Chọn "service s3" và sau click vào tab "JSON" để tạo policy từ một định dạng JSON.
      • Copy IAM policy dưới đây và paste vào "Policy editor":
        {
            "Version": "2012-10-17",
            "Statement": [
                {
                    "Effect": "Allow",
                    "Action": [
                        "s3:ListBucket",
                        "s3:ListObjects",
                        "s3:GetObject",
                        "s3:DeleteObject",
                        "s3:GetObjectAcl",
                        "s3:PutObjectAcl",
                        "s3:PutObject"
                    ],
                    "Resource": [
                        "arn:aws:s3:::your-bucket-name",
                        "arn:aws:s3:::your-bucket-name/*"
                    ]
                }
            ]
        }​
      • Điều chỉnh các quyền truy cập (actions) và tài nguyên (resources) theo nhu cầu của bạn sau đó thì chọn "Next".
      • Bước cuối cùng nhập "Policy name" và gắn policy này cho user.

Sau khi hoàn thành, IAM user sẽ có các Access Key và Secret Access Key, mà bạn cần sử dụng trong ứng dụng của mình để truy cập vào AWS S3. Hãy lưu trữ các thông tin này một cách an toàn, vì chúng rất quan trọng và không thể lấy lại khi đã mất. Đến cuối bài viết này mình sẽ giải thích sự giống và khác nhau của "IAM policy" và "Bucket policy" nha.

Bước 3: Sử Dụng Trong Laravel

  1. Cài đặt AWS SDK cho PHP: Sử dụng Composer để cài đặt AWS SDK cho PHP vào dự án Laravel của bạn. Bạn có thể thực hiện điều này bằng cách chạy lệnh sau trong terminal: 

    1. aws/aws-sdk-php-laravel

      composer require aws/aws-sdk-php-laravel
    2. league/flysystem-aws-s3-v3

      composer require league/flysystem-aws-s3-v3
  2. Cấu hình AWS trong Laravel: Mở tập tin config/services.php và thêm cấu hình AWS S3 như sau:

    's3' => [
        'key' => env('AWS_ACCESS_KEY_ID'),
        'secret' => env('AWS_SECRET_ACCESS_KEY'),
        'region' => env('AWS_DEFAULT_REGION'),
        'bucket' => env('AWS_BUCKET'),
    ],
  3. Thiết lập biến môi trường: Đảm bảo bạn đã thiết lập các biến môi trường trong tập tin .env của Laravel:
    Hãy nhập Access Key và Secret Access Key từ user IAM bạn vừa tạo. 

    AWS_ACCESS_KEY_ID=your_access_key
    AWS_SECRET_ACCESS_KEY=your_secret_key
    AWS_DEFAULT_REGION=your_bucket_s3_region
    AWS_BUCKET=your_bucket_s3_name
    AWS_USE_PATH_STYLE_ENDPOINT=false
  4. Tạo Route và Controller: Tạo một route và một controller để xử lý việc upload file từ form và lưu trữ nó trên Amazon S3.

  5. Viết code xử lý: Mình tạo ra một Trait riêng để xử lý việc upload files lên s3 để mình tái sử dụng nó, và mình sử dụng nó trong services, các bạn có thể copy functions của mình dưới đây sau đó thì sử dụng trong file helpers cũng được sau đó thì tùy biến cho phù hợp nếu thực sự cần thiết, cái cốt lỗi ở đây là cách xử lý thôi, còn tùy vào các dự án mà các bạn dùng cho phù hợp, mình đã có mô tả chi tiết về từng functions bên dưới, hoặc các bạn vào github của mình để xem cách xử lý nhé!

    <?php
    
    namespace App\Traits;
    
    use Illuminate\Support\Facades\Storage;
    use Symfony\Component\HttpFoundation\File\UploadedFile;
    use Aws\S3\MultipartUploader;
    use Aws\Exception\MultipartUploadException;
    use Aws\Exception\AwsException;
    use Aws\S3\S3Client;
    
    trait FileUploadTrait
    {
        /**
         * upLoadObjectToS3
         *
         * @param  UploadedFile $objectFile
         * @param  string $directoryName
         * @param  string $folder
         * @return array
         */
        public function upLoadObjectToS3(string $folder, UploadedFile $objectFile, string $directoryName): array
        {
            $initial = [
                'status' => false,
                'fileName' => '',
                'filePath' => ''
            ];
            if ($objectFile) {
                $fileName = $this->getFileName($objectFile);
                $path = $folder . '/' . $directoryName . '/' . $fileName;
                Storage::disk('s3')->put($path, file_get_contents($objectFile), 's3');
                $initial = [
                    'status' => true,
                    'fileName' => $fileName,
                    'filePath' => $this->getObjectUrlFromS3($path),
                ];
            }
            return $initial;
        }
    
        /**
         * multipartUploaderToS3
         * 
         * use this function if uploading file size 100MB to 5GB
         * @param  UploadedFile $objectFile
         * @param  string $directoryName
         * @return array
         */
        public function multipartUploaderToS3(string $folder, UploadedFile $objectFile, string $directoryName): array
        {
            $initial = [
                'status' => false,
                'fileName' => '',
                'filePath' => ''
            ];
            if ($objectFile) {
                $fileName = $this->getFileName($objectFile);
                $path = $folder . '/' . $directoryName . '/' . $fileName;
                $contents = fopen($objectFile, 'rb');
    
                $s3 = new S3Client([
                    'version' => 'latest',
                    'region'  => env('AWS_DEFAULT_REGION')
                ]);
    
                // Use MultipartUploader to upload files to S3
                $uploader = new MultipartUploader($s3, $contents, [
                    'bucket' => env('AWS_BUCKET'),
                    'key'    => $path,
                ]);
    
                try {
                    $result = $uploader->upload();
                    $initial = [
                        'status' => true,
                        'fileName' => $fileName,
                        'filePath' => $result['ObjectURL'],
                    ];
                    return $initial;
                } catch (MultipartUploadException $e) {
                    $initial['message'] = 'Failed to upload file';
                } catch (AwsException $e) {
                    $initial['message'] = 'AWS error: ' . $e->getMessage();
                }
            }
    
            return $initial;
        }
    
        /**
         * getUrlFromS3
         *
         * @param string|array $pathFile
         * @return void
         */
        public function getObjectUrlFromS3(string|array $pathFile): string|array
        {
            $initial = [
                'status' => true,
                'filePath' => null
            ];
            if (empty($pathFile)) {
                return '';
            }
            if (is_array($pathFile)) {
                $arrPathExist = [];
                foreach ($pathFile as $item) {
                    if (Storage::disk('s3')->exists($item)) {
                        $arrPathExist[] = Storage::disk('s3')->url($item);
                    }
                }
    
                if(!empty($arrPathExist)) {
                    $initial['filePath'] = $arrPathExist;
                    return $initial;
                }
            }
            if (is_string($pathFile) && Storage::disk('s3')->exists($pathFile)) {
                $url = Storage::disk('s3')->url($pathFile);
                $initial['filePath'] = $url;
                return $initial;
            }
        }
    
        /**
         * deleteObjectS3
         *
         * @param string|array $pathFile
         * @return void
         */
        public function deleteObjectS3(string|array $pathFile): array
        {
            $initial = [
                'status' => false,
            ];
            if (empty($pathFile)) {
                return $initial;
            }
            if (is_array($pathFile)) {
                $arrPathExist = [];
                foreach ($pathFile as $item) {
                    if (Storage::disk('s3')->exists($item)) {
                        $arrPathExist[] = $item;
                    }
                }
                if (!empty($arrPathExist)) {
                    $data = Storage::disk('s3')->delete($arrPathExist);
                    $initial['status'] = $data;
                }
            }
            if (is_string($pathFile) && (Storage::disk('s3')->exists($pathFile))) {
                $data = Storage::disk('s3')->delete($pathFile);
                $initial['status'] = $data;
            }
    
            return $initial;
        }
    
        /**
         * getFileName
         *
         * @param object $objectFile
         * @return string
         */
        private function getFileName(UploadedFile $objectFile): string
        {
            $originName = $objectFile->getClientOriginalName();
            $fileName = pathinfo($originName, PATHINFO_FILENAME);
            $extension = $objectFile->getClientOriginalExtension();
            $fileName = $fileName . '_' . time() . '.' . $extension;
    
            return $fileName;
        }
    }
    

Như vậy là mình đã hướng dẫn xong rồi mong rằng nó giúp được một phần nào đó trong dự án của các bạn.


Có thể bạn chưa biết?

IAM policy và bucket policy là hai loại chính sách trong Amazon S3 được sử dụng để quản lý quyền truy cập vào các tài nguyên S3, nhưng chúng có một số điểm khác nhau. Dưới đây là sự giống và khác nhau giữa chúng:

Giống nhau:

  1. Quản lý quyền truy cập: Cả hai loại policy đều được sử dụng để quản lý quyền truy cập vào các tài nguyên S3, bao gồm các bucket và các đối tượng trong bucket.

  2. Sử dụng cú pháp JSON: Cả hai loại policy đều được viết bằng cú pháp JSON để xác định các quyền hạn và tài nguyên mà người dùng được phép truy cập.

Khác biệt:

  1. Phạm vi áp dụng:

    • IAM policy được gắn với người dùng, nhóm người dùng hoặc vai trò trong AWS IAM, và xác định quyền hạn của họ trên tất cả các tài nguyên trong tài khoản AWS.
    • Bucket policy được gắn với một bucket cụ thể trong Amazon S3 và xác định quyền truy cập vào các đối tượng trong bucket đó.
  2. Người được ảnh hưởng:

    • IAM policy ảnh hưởng đến người dùng, nhóm người dùng hoặc vai trò mà nó được gắn vào.
    • Bucket policy ảnh hưởng đến tất cả các người dùng và tài khoản AWS khác nhau mà cố gắng truy cập vào các đối tượng trong bucket.
  3. Quyền hạn chi tiết:

    • IAM policy thường cung cấp các quyền hạn chi tiết cho từng người dùng hoặc vai trò cụ thể, cho phép bạn tùy chỉnh quyền truy cập một cách linh hoạt.
    • Bucket policy thường được sử dụng để cấu hình quyền truy cập "toàn bộ hoặc không", điều chỉnh quyền truy cập cho tất cả các người dùng hoặc tài khoản AWS được liên kết với bucket.

Khi nào sử dụng:

  • IAM policy: Sử dụng IAM policy khi bạn muốn điều chỉnh quyền truy cập của người dùng cụ thể, nhóm người dùng hoặc vai trò trong tài khoản AWS của bạn.

  • Bucket policy: Sử dụng bucket policy khi bạn muốn quản lý quyền truy cập vào các đối tượng trong một bucket cụ thể và áp dụng chúng cho tất cả các người dùng hoặc tài khoản AWS được liên kết với bucket đó.


Ví dụ trong trường hợp chúng ta đã cấu hình một IAM policy cho user cho phép hành động "s3:DeleteObject", nhưng không cấp quyền cho hành động này trong Bucket policy, thì user đó vẫn có thể thực hiện hành động xoá đối tượng (delete object).

Nguyên lý hoạt động là khi bạn cung cấp quyền truy cập cho một người dùng thông qua IAM policy, thì quyền truy cập đó sẽ được ưu tiên hơn so với quyền truy cập được cấu hình trong bucket policy. Bucket policy chỉ được áp dụng khi không có IAM policy nào ứng với người dùng đó, hoặc nếu IAM policy không cung cấp quyền truy cập cho một hành động cụ thể.

Vì vậy, trong trường hợp của bạn, nếu user được cấp quyền s3:DeleteObject thông qua IAM policy, thì user đó vẫn có thể xoá các đối tượng trong bucket, bất kể bucket policy có cấm hành động này hay không.

Bình luận (0)

Michael Gough
Michael Gough
Michael Gough
Michael Gough
Michael Gough
Michael Gough
Michael Gough
Michael Gough
Michael Gough
Michael Gough
Michael Gough
Michael Gough
Michael Gough
Michael Gough
Michael Gough
Michael Gough

Bài viết liên quan

Learning English Everyday