Skip to Content

Uploading, Resizing and Serving images from Google Cloud Platform

Posted on

Tản mạn

Từ lúc bắt đầu code Web, các tasks liên quan tới phần upload/resize ảnh thường làm mình khá lấn cấn. Không phải vì tasks khó, không biết làm mà là vì các solution áp dụng vào thường chưa được “hay” cho lắm 😄

Flow thường sẽ là:

  • Step 1: User upload ảnh.
  • Step 2: Server nhận ảnh, cắt versions (thường sẽ là 3 versions: large, medium, small), hoặc resize ảnh (thành ảnh vuông, chữ nhật, … ) hoặc process để giảm quality của ảnh. Sau đó lưu lại.

Việc này theo mình không khó, có thể sử dụng gem carrierwave, paperclip, hoặc active storage để xử lý.

Khi client request ảnh, server sẽ trả về response dạng:

{
	user: {
		name: "Alice",
		avatar: {
			large: "https://example.com/users/alice/avatar/large_avatar.png",
			medium: "https://example.com/users/alice/avatar/medium_avatar.png",
			small: "https://example.com/users/alice/avatar/small_avatar.png"
		}
	}
}

Sau khi nhận được image url, client sẽ tự chọn url nào phù hợp. Ví dụ với web client (viết bằng Vuejs) thì sẽ chọn url avatar large, trên ipad thì chọn url medium, còn điện thoại thì dùng version ảnh small.

=> Có thể tiết kiệm thời gian load ảnh, tiết kiệm băng thông và tăng trải nghiệm người dùng.

Nhưng =))) luôn có cái chữ nhưng ở đây =)))

Vấn đề

Trong flow mình thường dùng đã phát sinh một vài “lỗ hổng”:

  • Server phải handle nhiều việc. Việc xử lý cắt, giảm quality, tạo versions, .. tốn khá nhiều resource của server (CPU, storage, bandwidth, ..). Và giá cả lưu trữ cũng khá cao.
  • Việc tạo versions cho ảnh gif khá khó khăn, ngún rất nhiều resource của server, ngay cả trong trường hợp chạy background job.
  • Người dùng sẽ phải đợi khá lâu do quá trình upload ảnh mất nhiều thời gian. Nếu server tạm thời chỉ lưu ảnh gốc, sau đó đẩy việc cắt versions xuống background job, thì vẫn tốn băng thông load ảnh trong lần đầu upload lên.
  • Khi tạo ra các version fixed size (ví dụ large = 500 * 500, medium = 300 * 300, small = 150px * 150px), các version này sẽ khó tương thích với các màn hình khác nhau, do size màn hình của các dòng điện thoại thay đổi liên tục => Có thể sẽ bị méo hình, hoặc server sẽ phải tạo ra thêm nhiều loại versions ảnh với size khác nữa.

Solution

Thời gian gần đây, join dự án mới, mình có học lỏm được 1 cách làm rất hay của bên phía khách hàng =)). Đó là dùng Goolge App EngineGoogle Storage để xử lý bài toán về lưu trữ, resize, serve ảnh trong trang web của mình =))

Để bắt đầu, hãy thử ngó qua simple tutorial hướng dẫn cách upload files lên cloud nhé :D

The Function

App Engine API có một function rất hữu ích, cho phép lấy ra magic URL của 1 bức ảnh đã upload lên Cloud Storage, để trả về cho phía client.

get_serving_url()

Returns a URL that serves the image in a format that allows dynamic resizing and cropping, so you don’t need to store different image sizes on the server. Images are served with low latency from a highly optimized, cookieless infrastructure.

Ý tưởng sẽ là:

  • Step 1: User upload ảnh lên server.
  • Step 2: Server upload ảnh đó lên Google Cloud Storage. Lưu lại storage_file_path trong DB. Server KHÔNG xử lý lưu, cắt ảnh hay tạo versions gì.

Khi client request lên, từ storage_file_path trong db, server sẽ lấy ra magic URL và trả về cho phía client.

{
	user: {
		name: "Alice",
		avatar: {
			url: "http://lh3.googleusercontent.com/hash_string"
		}
	}
}

Sau đó, client cần dùng size nào, thì sẽ dùng chính url nhận được bên trên để gọi lại. Hãy thử với ví dụ ảnh này nhé:

http://lh3.googleusercontent.com/93uhV8K2yHkRuD63KJxlTi7SxjHS8my2emuHmGLZxEmX99_XAjTN3c_2zmKVb3XQ5d8FEkwtgbGjyYpaDQg

  • Mặc định trả về là 1 bức ảnh, maximum 512px (link).
  • Bằng cách thêm =sXX vào cuối của url trên, với XX là số integer từ 0-2560, bạn sẽ nhận được 1 bức ảnh được scale về đúng size truyền lên. (link=s256)
  • Bằng cách thêm =sXX-c vào cuối url, bạn sẽ nhận được 1 version ảnh đã được crop. (link=s400-c)
  • Muốn lấy ra ảnh gốc, bạn chỉ cần thêm =s0 vào cuối url (link=s0)

Như vậy là ta vẫn giữ được đúng yêu cầu ban đầu. Client thích lấy image size gì, thì cứ gọi lên bình thường thôi. Đặc biệt, tốc độ rất nhanh, ngay cả trong trường hợp upload ảnh gif.

Để đo thử tốc độ resize, bạn có thể thay XX bằng số bạn muốn, append vào link demo bên trên và cảm nhận =))

Còn thêm 1 điều quan trọng nữa. Đó là: Việc sử dụng resize, caching ảnh này hoàn toàn miễn phí :D Bạn sẽ chỉ phải trả xèng cho actual storage (tức là charge cho phần lưu ảnh gốc trên Google Cloud Storage)

Nếu bạn để ý, công nghệ này cũng được áp dụng vào bên Google Photos. (check thử một url bất kì trên google photo, bạn sẽ thấy ngay =)) )

Kết bài

Bài viết không dài, nhưng mình nghĩ thông tin này khá hữu ích, là 1 cách khá hay, góc nhìn mới trong việc xử lý các vấn đề liên quan tới upload/resize/serve images.

Tuy nhiên, cũng có 1 mặt hạn chế, đó là để làm được, bạn cần chạy server trên GCP. Trong khi phần lớn các dự án mình làm đều yêu cầu dùng AWS :v

Nếu là project cá nhân, bạn cũng phải config khá nhiều để có thể deploy lên GCP. Cơ mà, just do it =))

Kiến thức không bao giờ thừa. Ngoài kia luôn có solutions cho các vấn đề mà bạn gặp phải. Quan trọng là chúng ta không bao giờ dừng việc kiếm tìm nó :D

Thank you for reading ❤️❤️❤️

Reference: https://medium.com/google-cloud/uploading-resizing-and-serving-images-with-google-cloud-platform-ca9631a2c556

comments powered by Disqus