รู้จัก Cloud Storage for Firebase ตั้งแต่ Zero จนเป็น Hero

Cloud Storage for Firebase บริการที่ให้คุณสามารถ Upload และ Download ไฟล์ได้อย่างมีประสิทธิภาพและปลอดภัยบน Google Cloud Storage พร้อมรองรับการขยายขนาดอัตโนมัติระดับ petabyte ที่ให้คุณสามารถอัพโหลด รูปภาพ, ไฟล์เสียง, วิดีโอ และไฟล์อื่นๆได้ ซึ่งรองรับทั้ง Android, iOS และ Web

วิดีโอแนะนำการทำงานของ Cloud Storage for Firebase

ในการพัฒนา Cloud Storage for Firebase ขอแยกออกเป็น 5 parts ดังนี้

  1. การ Set up Firebase และ Realtime Database SDK
  2. การอ้างอิงถึงแหล่งที่เก็บข้อมูล (Create a Reference)
  3. การอัพโหลดไฟล์ (Upload Files)
  4. การดาวน์โหลดไฟล์ (Download Files)
  5. การลบไฟล์ (Delete Files)
  6. การใช้ Metadata

เมื่อพร้อมแล้ว…ก็เปิด Android Studio ขึ้นมา โดยจะสร้างโปรเจคใหม่ หรือจะใช้โปรเจคเดิมก็ได้

Part 1 การ Set up Firebase และ Storage SDK

ถ้าสร้างโปรเจคใหม่ ให้ไปดูการ Set up Firebase ที่บทความนี้ก่อน

เมื่อ Set up Firebase เรียบร้อยแล้ว ก็ให้เพิ่ม Storage SDK ใน build.gradle ของ app-level แล้วกด Sync ก็เป็นอันจบส่วนที่ 1 ละ

dependencies {
compile 'com.google.firebase:firebase-storage:11.8.0'
}

การเข้าถึงไฟล์ใน Cloud Storage for Firebase ทั้ง read และ write โดยปกติ เราจะต้องทำการ Authentication ผ่าน Firebase Authentication ซะก่อน แต่เพื่อให้เราสามารถเข้าใจบทความนี้ได้โดยไม่ต้องอ่าน Firebase Authentication เราจะมาทำให้มันเข้าถึงได้แบบ public กัน โดยให้เข้าไปที่ Firebase Console เข้าไปที่โปรเจค จากนั้นเลือกเมนู Storage แล้วเลือก tab ที่ชื่อว่า RULES จะพบหน้าตาของประมาณนี้

Default Rules

จากนั้นก็ปรับ code ให้เข้าถึงไฟล์ได้แบบ public กันตามนี้

Public Rules

Part 2 การอ้างอิงถึงแหล่งที่เก็บข้อมูล (Create a Reference)

ไฟล์ทั้งหมดที่เราอัพโหลดไว้จะถูกเก็บไว้ใน Bucket ซึ่งโครงสร้างก็เหมือนกับการจัดเก็บไฟล์ใน hard disk โดยการอ้างอิงนั้นเราสามารถอ้างอิงได้ทั้ง โฟลเดอร์และไฟล์ เพื่อใช้ในการ upload, download, delete และการจัดการ metadata

เริ่มด้วยการประกาศตัวแปร StorageReference รับค่า Instance และอ้างถึง bucket

StorageReference storageRef = FirebaseStorage.getInstance().getReference();

เราสามารถอ้างอิงถึง โฟลเดอร์ และ ไฟล์ ในลำดับต่างๆลงมาได้ด้วย child()

StorageReference folderRef = storageRef.child("photos");
StorageReference imageRef = storageRef.child("photos/firebase.png");

นอกจาก child() ก็ยังมี getParent() ที่ใช้อ้างอิงลำดับที่อยู่เหนือตัวที่อ้างอิง และ getRoot() ที่ใช้อ้างอิงถึง bucket

folderRef = imageRef.getParent();
storageRef = imageRef.getRoot();

และหากเราต้องการดูค่าต่างๆกับตัวแปรที่เราอ้างอิงอยู่ก็สามารถ getPath(), getName(), และ getBucket() เพื่อดึงค่า String มาแสดง

// path ที่อ้างอิง: "photos/firebase.png"
imageRef.getPath();

// ตำแหน่งสุดท้ายของ path: "firebase.png"
imageRef.getName();

// ชื่อ bucket ที่เราเก็บไฟล์: "xxx-xxxx-xxxxx.appspot.com"
imageRef.getBucket();

ข้อจำกัดของการอ้างอิง มีอะไรบ้างมาดูกัน

  1. ความยาวของ path ที่เราอ้างอิง เมื่อเป็น UTF-8 แล้ว ขนาดจะต้องอยู่ระหว่าง 1 ถึง 1024 bytes
  2. จะต้องไม่มี \r หรือ \n อยู่ใน path ของการอ้างอิง
  3. หลีกเลี้ยงการอ้างอิงที่มี #, [, ], *, และ ? เพราะอาจทำให้มีปัญหากับ product ตัวอื่นใน Firebase ได้เช่น Firebase Realtime Database

Part 3 การอัพโหลดไฟล์ (Upload Files)

ในการ upload ไฟล์ เราจะต้องอ้างอิงไปยัง path และระบุชื่อของไฟล์ซะก่อน ตัวอย่าง จะ upload ไฟล์ชื่อ firebase.png ไปที่โฟลเดอร์ photos

imageRef = storageRef.child("photos/firebase.png");

สำหรับการ upload จะมีด้วยกัน 3 ประเภท

3.1. การอัพโหลดจากข้อมูลในหน่วยความจำ (Upload from data in memory)

วิธีการนี้จะต้องทำการแปลงไฟล์ให้เป็น byte และนำไปเก็บไว้ใน memory ก่อน จากนั้นจึงค่อยส่ง byte ที่แปลงมาให้กับ Cloud Storage for Firebase

จากตัวอย่าง เราจะเอารูปภาพจาก ImageView มาแปลงเป็น byte() แล้วใช้คำสั่ง putBytes() ในการ upload นอกจากนี้เรายังสามารถเช็คผลลัพธ์ได้จากการ เพิ่ม addOnSuccessListener และ addOnFailureListener เพื่อที่จะได้ callback และ handle ต่อไป

3.2. การอัพโหลดจาก Stream (Upload from a stream)

วิธีนี้คือการเอาไฟล์มาแปลงให้อยู่ในรูปแบบของ stream แล้ว upload ไปยัง Cloud Storage for Firebase

จากตัวอย่างผมดึง path ที่ได้จากการเลือกรูปใน gallery มาแปลงให้เป็น stream ด้วยคำสั่ง FileInputStream แล้วใช้คำสั่ง putStream() ในการ upload

ดึงรูปจาก gallery อย่าลืมประกาศ permission กันด้วยหละ ผมเองขอ WRITE_EXTERNAL_STORAGE ไปเลย เพราะจะได้ทั้ง READ และ WRITE เพื่อเอาไว้ใช้ตอน download ต่อไป <uses-permission android:name=”android.permission.WRITE_EXTERNAL_STORAGE”/>

3.3. อัพโหลดจากไฟล์ในเครื่อง (Upload from a local file)

วิธีนี้เป็นการเลือกไฟล์ที่อยู่ในเครื่องมา upload ไปยัง Cloud Storage for Firebase

จากตัวอย่างผมดึง path ที่ได้จากการเลือกรูปใน gallery มาแปลงให้อยู่ในรูปแบบของ File และเอามาทำเป็น Uri แล้วก็ใช้คำสั่ง putFile() ในการอัพโหลด ซึ่งหากสำเร็จก็จะนำ URL จาก Cloud Storage for Firebase มาแสดง ดังรูป

และเราสามารถเข้าไปดูข้อมูลไฟล์ทั้งหมดใน Firebase Console ได้ด้วย โดยไปที่เมนู Storage จากนั้นเลือก tab แรกชื่อ Files ก็จะเห็นโฟลเดอร์ photos และไฟล์ firebase.png ภายในโฟลเดอร์

เรายังสามารถเพิ่ม Metadata ไปกับไฟล์ได้ด้วย ซึ่งเราจะสามารถอัพเดท หรือดึงข้อมูลนี้มาได้ภายหลัง
StorageMetadata metadata = new StorageMetadata.Builder()
.setCustomMetadata("country", "Thailand")
.setContentType("image/png")
.build();
UploadTask uploadTask = imageRef.putFile(file, metadata);

เมื่อคลิกที่ไฟล์ firebase.png ใน Firebase Console ก็จะพบ metadata ด้วย ซึ่งปกติจะไม่มีโผล่มา

ในการอัพโหลดไม่ว่าจะเป็นการ putBytes(), putStream() หรือ putFile() มันจะ return ค่าที่อยู่ในรูปของ UploadTask กลับมา เพื่อให้เอาเอาไว้ใช้กับ Listener ในการดูสถานะความเปลี่ยนแปลงต่างๆ ซึ่งตัว UploadTask ยังสามารถเอามาจัดการการ upload ได้อีกดังนี้

  • uploadTask.pause() ใช้เพื่อสั่งหยุดการอัพโหลดชั่วคราว และยังคงจดจำสิ่งที่ได้อัพโหลดไปแล้ว
  • uploadTask.resume() ใช้เพื่อสั่งให้ทำการ upload ต่อจากการที่ถูกหยุดไป ซึ่งจะ upload ต่อจากที่ได้ upload ไปแล้ว
  • uploadTask.cancel() ใช้เพื่อสั่งให้หยุดการ upload

และนอกจาก addOnSuccessListener และ addOnFailureListener ที่เป็น Listener ให้เราเอาไว้ดูผลงานแล้วก็ยังมี addOnProgressListener และ addOnPausedListener เพื่อไว้คอยเฝ้าดูสถานะการ upload อีก โดยเฉพาะ addOnProgressListener ผมชอบมากเพราะเอามาใช้กับ ProgressDialog เพื่อดู % การ upload ได้ละ และเพื่อความเข้าใจที่ชัดเจนมากขึ้นเราจะ implement แบบครบเลย

จากตัวอย่างที่ทำให้ไปหากมีการกด pause ที่ ProgressDialog จะมีปุ่ม resume โผล่ขึ้นมาให้กด upload ต่อไปได้ด้วยนะเออ


Part 4 การดาวน์โหลดไฟล์ (Download Files)

ในการ download ไฟล์ เราจะต้องอ้างอิงไปยัง path และระบุชื่อของไฟล์ซะก่อน ตัวอย่าง จะ download ไฟล์ชื่อ kaizen.png จากโฟลเดอร์ photos

imageRef = storageRef.child("photos/kaizen.png");

สำหรับการ download ไฟล์จะมีด้วยกัน 3 ประเภท

4.1. ดาวน์โหลดไฟล์ลงในหน่อยความจำ (Download in memory)

วิธีนี้จะ download ไฟล์มาจาก Cloud Storage for Firebase แล้วแปลงเป็น byte เก็บลง memory เครื่อง วิธีนี้จะต้องระวังว่าถ้าโหลดไฟล์ใหญ่มาแล้วแปลงไปเก็บในหน่วยความจำที่ไม่เพียงพอในขณะนั้น จะทำให้แอพ crash ได้ ซึ่งเราสามารถจำกัดขนาดไฟล์ที่เราจะโหลดมาได้เพื่อหลีกเลี่ยงการ Crash (หรือไปใช้ method อื่นเหอะ)

4.2. ดาวน์โหลดไฟล์ลงไปเก็บในเครื่อง (Download to a local file)

วิธีนี้จะทำให้เราสามารถเข้าถึงไฟล์เมื่อเรา offline ได้ และสามารถส่งไฟล์ต่อไปให้แอพอื่นได้อีก และวิธีนี้คือสาเหตุที่ขอ permission WRITE_EXTERNAL_STORAGE จากข้อ 3.2 นั่งเอง

วิธีการ download แบบนี้จะสามารถเพิ่ม Listener ในการดู progress ได้ด้วย เราก็เลยทำ ProgressDialog ซะเลย

4.3. การดาวน์โหลดข้อมูลด้วย URL (Download Data via URL)

วิธีนี้เป็นการเอาไฟล์ที่เราอ้างอิงไปดึง URL ออกมาแล้วจึงไป download หากสำเร็จจะ return ข้อมูล URL แบบเต็มของ Cloud Storage for Firebase กลับมา ในรูปแบบของ Uri


Part 5 การลบไฟล์ (Delete Files)

ในการลบไฟล์ เราจะต้องอ้างอิงไปยัง path และระบุชื่อของไฟล์เหมือนเคย ตัวอย่าง จะลบไฟล์ชื่อ kaizen.png จากโฟลเดอร์ photos

imageRef = storageRef.child("photos/kaizen.png");

โดยเราสามารถเพิ่ม Listener ไปเพื่อ handle ผลลัพธ์ได้


Part 6 การใช้ Metadata

จากการเพิ่ม metadata ไปในข้อ 3.3 แล้ว เราก็ยังสามารถที่จะ ดึง หรือ อัพเดท ข้อมูลใน metadata ได้อีก

การดึงข้อมูล Metadata จากไฟล์ (Get File Metadata)

ปกติเวลาเรา upload ไฟล์ภาพไป Cloud Storage for Firebase ก็จะมีการเก็บค่า metadata บางอย่างอยู่แล้ว เช่น name, size และ contentType หรือเราก็สามารถ custom ค่าไปเก็บได้เช่นกัน ซึ่งเราสามารถจะดึงข้อมูลทั้งหมดนี้ออกมาได้

การอัพเดทข้อมูล Metadata สู่ไฟล์ (Update File Metadata)

เราสามารถอัพเดท metadata หลังจากอัพโหลดไฟล์ไปแล้วได้ โดยระบบจะอัพเดทเฉพาะ field ที่เราเลือกเท่านั้น ส่วน field ที่ไม่ได้เลือกก็จะไม่มีการอัพเดทใด


บทสรุปอยู่ทางนี้จ้า

Cloud Storage for Firebase เป็นบริการที่มีทั้งฟรี และมีค่าใช้จ่ายในกรณีเกิด limit ซึ่งเราสามารถดูการใช้งานของเราที่ Firebase Console เลือก Storage แล้วเลือก tab ชื่อ USAGE

ส่วนเรื่อง Price plan ของ Cloud Storage for Firebase ก็มีดังนี้

แบบฟรีจะเก็บไฟล์ที่ 5GB, bandwith 30GB และสามารถ upload ได้ 50,000 ครั้ง และ download ได้ 50,000 ครั้ง แต่เอาเข้าจริงกล้องถ่ายรูปบนโทรศัพท์สมัยนี้ก็ละเอียดยิ่งนัก หากเราไม่ทำการ resize หรือ compress ก่อน 5GB ที่เก็บนี้ น่าจะไปถึงได้ไม่ยาก เรามาจำลองการใช้งานดูหน่อยดีกว่า

แบบฟรี จำลองเก็บรูปได้ 2,500 รูป แบบ high-resolution และ bandwidth สามารถ upload และ download ได้ 15,000 รูปแบบ high-resolution

ส่วนสิ่งที่ยังไม่ได้ลงรายละเอียดให้คือ Security & Rules ให้ไปอ่านกันเองก่อนนะครับ เดี๋ยวผมจะทำบทความเรื่องนี้แยกออกมาทั้ง Realtime Database และ Storage

เป็นอย่างไรบ้างครับ เรื่อง Cloud Storage for Firebase ผมว่ามันเจ๋งมากเลย implement ไม่ยากด้วย code สั้นมาก ผมไปลองโหลดตัว quickstart ของ Firebase ใน GitHub มา คิดว่าของผมเข้าใจง่ายกว่านะ 555 Source code ทั้งหมดของบทความนี้ก็ไปโหลดที่ GitHub มาดูได้ครับ

สำหรับวันนี้ขอลาไปก่อน แล้วพบกันใหม่กับบทความถัดไป…ราตรีสวัสดิ์ พี่น้องชาวไทย