+6

[Golang] Dùng Lambda Để Lưu và Lấy Image Từ AWS S3

Mục Tiêu

  • Lưu ảnh lên Amazon Simple Storage Service
  • Lấy ảnh từ Amazon Simple Storage Service, giới hạn TTL

Chuẩn Bị

API Lưu Image

  • Image được input dưới dạng base64

Cấu Hình ENV

Trong file serverless.yaml, add 2 env

provider:
  environment:
    S3_BUCKET: "images-dev-serverlessdeploymentbucket-1g3trol0zorpp"
    IMAGE_BLOG: "image-blog/"
  • Sau khi deploy thành công, thì truy cập aws-console search S3 service để check bucket của project => copy và bỏ vào env
  • Env IMAGE_BLOG là image sẽ được lưu sub-folder trong bucket

List function Code

Tạo AWS session

func CreateSession(req AwsReq) *session.Session {
	sess := session.Must(session.NewSessionWithOptions(session.Options{
		SharedConfigState: session.SharedConfigEnable,
	}))
	return sess
}
  • Để có thể tạo được session, yêu cầu cấu hình credential trước. Checkout Cấu Hình AWS Credential để cấu hình credential

Func kiểm tra size của image

// fn upload image to s3, request iamge is base64
func CheckSizeBase64(baseImage *string) error {
	sizeByte := base64.StdEncoding.DecodedLen(len(*baseImage))
	sizeRe := 5000000
	if sizeByte > sizeRe {
		err := fmt.Errorf("size image large, require size %d and size input %d", sizeRe, sizeByte)
		return err
	}
	return nil
}
  • Image từ client dưới dạng base64 => check size bằng cách kiểm tra số lượng kí tự của base64
  • Func check số lượng kí tự vượt quá 5 triệu thì sẽ báo lỗi

Func Lưu image đến s3

func StoreS3(ctx context.Context, reqDTO StoreS3Req) (*s3.PutObjectOutput, StoreS3Reponse, error) {
	var (
		dataRes = StoreS3Reponse{}
		err     error
		putOut  = &s3.PutObjectOutput{}
	)
	if reqDTO.BaseImage == nil {
		return putOut, dataRes, fmt.Errorf("base64 of image is require")
	}
	if len(*reqDTO.BaseImage) == 0 {
		return putOut, dataRes, fmt.Errorf("byte image decode empty")
	}
	splitBase := strings.Split(*reqDTO.BaseImage, "base64,")
	var newBase string
	if len(splitBase) > 1 {
		newBase = splitBase[1]
	} else {
		newBase = splitBase[0]
	}
	byteImage, err := base64.StdEncoding.DecodeString(newBase)
	if err != nil {
		fmt.Println("image decode string err", err)
		return putOut, dataRes, err
	}

	mimeType := http.DetectContentType(byteImage)
	fmt.Println("mime type:", mimeType)
	if !strings.Contains(mimeType, "jpeg") && !strings.Contains(mimeType, "png") {
		return putOut, dataRes, fmt.Errorf("mime type error")
	}

	byteReader := bytes.NewReader(byteImage)

	dataRes.PathImage = reqDTO.PathImage
	session := CreateSession(AwsReq{
		CustomEndpoint: reqDTO.CustomEndpoint,
	})
	putOut, err = s3.New(session).PutObject(&s3.PutObjectInput{
		Body:        byteReader,
		Bucket:      aws.String(GetS3Bucket()),
		Key:         aws.String(dataRes.PathImage),
		ContentType: &mimeType,
	})

	return putOut, dataRes, err
}
  • Line code mimeType := http.DetectContentType(byteImage) kiểm tra type của image, chỉ handle 2 dạng type ['png','jpeg']

Một số func phụ khác:

  • Get bucket name từ environment:
func GetS3Bucket() string {
	bucket := os.Getenv("S3_BUCKET")
	if bucket != "" {
		return bucket
	}
	log.Default().Println("GetS3Bucket default value:", "pkg.BucketDefault")
	return "pkg.BucketDefault"
}

=> lấy bucket-name thừ environment, nếu env rỗng thì sử dụng bucket-name mặc định.

Test API

  • Request data:
{
    "requestId":"",
    "requestTime":"",
    "data": {
        "file_name": "ducnp",
        "name":"ducnp5-test1",
        "phone":"0335288888",
        "partner_id":"myst",
        "user_id":"ducnp",
        "image":"****am6firEVHTsD//2Q==****"
    }
}
  • Response data:. Truy cập S3 thì sẽ thấy ảnh đã lưu thành công.

ERROR

  • Có thể xuất hiện lỗi này: ==> truy cập vào cloud-watch, để check error. Thường là do chưa cấp quyền cho Role access vào S3.

API Get Image

  • Tương tự như lưu ảnh, API get image thì chỉ khác 1 chỗ đó là hàm PresignUrl

List function Code

PresignUrl

func PresignUrl(reqAws AwsReq, presign PresignUrlReq) (string, error) {
	var (
		url string
		err error
	)
	clientS3 := CreateS3Client(reqAws)
	req, _ := clientS3.GetObjectRequest(&s3.GetObjectInput{
		Bucket: aws.String(presign.Bucket),
		Key:    aws.String(presign.KeyPath),
	})
	url, err = req.Presign(presign.UrlTtl)
	if err != nil {
		return url, fmt.Errorf("presign url err %s", err)
	}
	return url, err
}
  • Trong func có thuộc tính TTL là giới hạn thời gian access đến image.

Test API

  • Request:
{
    "requestId":"",
    "requestTime":"",
    "data": {
        "pathImage": "image-blog/0335288888landucnp5-test1"
    }
}
  • Response:
{
    "data": {
        "url": "https://images-dev-serverle****blog/0335288888landucnp5-test1?X-Amz-***mz-Credential=ASIAQ52H7T***"
    },
    "responseCode": "00",
    "responseId": "8b2f9168-4ad4-4f6c-b0b6-8b78f3d7c99d",
    "responseMessage": "successfully",
    "responseTime": "2023-02-05T01:28:17Z"
}

==> copy url và mở bằng browser để xem.

  • Image hết hạn:

Contact


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí