การทำงานกับระบบไฟล์เป็นพื้นฐานของแอปพลิเคชันมือถือทุกแพลตฟอร์ม แต่ละแพลตฟอร์มมีโมเดลการเข้าถึงของตัวเอง: Sandbox บน iOS จะแยกแอปพลิเคชันไว้ในคอนเทนเนอร์ที่แยกจากกัน ในขณะที่ Scoped Storage บน Android จะจำกัดการเข้าถึงพื้นที่จัดเก็บร่วมกันโดยตรง ตาม Google Developer Documentation (2026) การนำ Scoped Storage มาใช้กับ Android 10 ทำให้ต้องทบทวนสถาปัตยกรรมการจัดเก็บข้อมูลใหม่อย่างสมบูรณ์ ในคู่มือนี้เราจะกล่าวถึง FileManager, MediaStore API, Storage Access Framework และ DocumentProvider สำหรับทั้งสองแพลตฟอร์ม
ประเด็นสำคัญ
ระบบไฟล์ในแอปพลิเคชันมือถือ คือชุดของ API กฎความปลอดภัย และข้อจำกัดที่กำหนดวิธีการจัดเก็บและเข้าถึงข้อมูลบนอุปกรณ์ iOS และ Android ต่างจากระบบปฏิบัติการเดสก์ท็อป แพลตฟอร์มมือถือจะแยกแต่ละแอปพลิเคชันเพื่อปกป้องข้อมูลผู้ใช้จากการอ่านโดยไม่ได้รับอนุญาตจากโปรแกรมอื่น
iOS ใช้โมเดล Sandbox ซึ่งแต่ละแอปจะอยู่ในคอนเทนเนอร์ของตัวเองโดยมีสิทธิ์ที่จำกัดอย่างเคร่งครัด ก่อนเวอร์ชัน 10 Android ให้สิทธิ์เข้าถึงพื้นที่จัดเก็บภายนอกอย่างสมบูรณ์ แต่ด้วยการนำ Scoped Storage มาใช้ วิธีการก็ใกล้เคียงกับ iOS มากขึ้น ความแตกต่างหลักคือ iOS แยกระบบไฟล์อย่างสมบูรณ์ ในขณะที่ Android เสนอระดับการเข้าถึงหลายระดับ: ไดเรกทอรีส่วนตัว, MediaStore สาธารณะ และการเข้าถึงชั่วคราวผ่าน SAF
แอปพลิเคชันมือถือใช้พื้นที่จัดเก็บข้อมูลสามประเภท พื้นที่จัดเก็บส่วนตัว — ไดเรกทอรีที่เฉพาะแอปเท่านั้นที่เข้าถึงได้สำหรับไฟล์ภายในและแคช พื้นที่จัดเก็บร่วมกัน — ไฟล์มีเดียผ่าน MediaStore (Android) หรือ Files App (iOS) พื้นที่จัดเก็บคลาวด์ — iCloud Drive และ Google Drive สำหรับการซิงค์ระหว่างอุปกรณ์ แต่ละประเภทมีข้อจำกัดด้านขนาด อายุไฟล์ และเงื่อนไขการเข้าถึงของตัวเอง
| ประเภทพื้นที่จัดเก็บ | iOS | Android |
|---|---|---|
| ส่วนตัว | Documents, Library, Caches | getFilesDir(), getCacheDir() |
| มีเดียร่วมกัน | PHPhotoLibrary ผ่านตัวเลือก | MediaStore API (ContentResolver) |
| เอกสารร่วมกัน | Files App ผ่าน UIDocumentPicker | Storage Access Framework (SAF) |
| คลาวด์ | iCloud Drive (UIDocument) | Google Drive API |
| แคช | ไดเรกทอรี Caches ถูกล้างโดยระบบ | getCacheDir(), getExternalCacheDir() |
Sandbox คือสถาปัตยกรรมความปลอดภัยของ iOS ที่แยกแต่ละแอปพลิเคชัน แอปสามารถอ่านและเขียนได้เฉพาะภายในแซนด์บ็อกซ์ของตัวเองเท่านั้น ในการเข้าถึงรายชื่อ รูปภาพ หรือไฟล์จากแอปอื่น ต้องใช้ตัวเลือกระบบ: UIImagePickerController หรือ UIDocumentPickerViewController การเข้าถึง Files App กำหนดค่าผ่านแฟล็ก UIFileSharingEnabled ใน Info.plist ระบบไฟล์ในการพัฒนามือถือบน iOS ต้องการความเข้าใจโครงสร้างไดเรกทอรีและการเลือกตำแหน่งที่เหมาะสมสำหรับข้อมูลแต่ละประเภท
แซนด์บ็อกซ์ iOS ประกอบด้วยไดเรกทอรีมาตรฐานหลายแห่ง Documents — สำหรับไฟล์ผู้ใช้ รวมอยู่ใน iCloud Backup Caches — สำหรับข้อมูลชั่วคราวที่ระบบอาจลบเมื่อพื้นที่จัดเก็บไม่เพียงพอ Temporary — สำหรับไฟล์เซสชันปัจจุบัน ถูกล้างเมื่อรีสตาร์ท Application Support — สำหรับข้อมูลภายในแอปที่ซ่อนจากผู้ใช้ การเลือกไดเรกทอรีผิดนำไปสู่ปัญหา: การบันทึกแคชใน Documents สิ้นเปลืองพื้นที่ iCloud และละเมิดแนวทางปฏิบัติของ Apple เกี่ยวกับระบบไฟล์
import Foundation
let fileManager = FileManager.default
guard let documentsURL = fileManager.urls(
for: .documentDirectory,
in: .userDomainMask
).first else { return }
let fileURL = documentsURL.appendingPathComponent("notes.txt")
let text = "Содержимое файла"
// การเขียนแบบอะตอมมิกพร้อมการเข้ารหัส
try text.write(
to: fileURL,
atomically: true,
encoding: .utf8
)
คลาส FileManager มีชุดเมธอดที่สมบูรณ์สำหรับจัดการไฟล์บน iOS FileManager.default เป็นซิงเกิลตันที่ปลอดภัยต่อเธรด เหมาะสำหรับการดำเนินการส่วนใหญ่ เมธอดเช่น fileExists(atPath:), createDirectory(at:withIntermediateDirectories:attributes:), copyItem(at:to:) และ removeItem(at:) ครอบคลุมสถานการณ์พื้นฐาน การดำเนินการกับไฟล์ที่ใหญ่กว่า 1 MB ควรดำเนินการในเธรดพื้นหลังผ่าน DispatchQueue.global() สำหรับการสตรีมปริมาณมาก ให้ใช้ FileHandle แทนการโหลดไฟล์ทั้งหมดลงในหน่วยความจำ
func readDocumentsFile(named fileName: String) -> String? {
guard let docsURL = FileManager.default.urls(
for: .documentDirectory,
in: .userDomainMask
).first else { return nil }
let fileURL = docsURL.appendingPathComponent(fileName)
return try? String(contentsOf: fileURL)
}
ด้วยการเปิดตัว Android 10 Google ได้นำ Scoped Storage มาใช้ — โมเดลการเข้าถึงระบบไฟล์แบบจำกัด แอปสามารถอ่านและเขียนได้อย่างอิสระเฉพาะในไดเรกทอรีส่วนตัวของตัวเองเท่านั้น สำหรับไฟล์มีเดีย (รูปภาพ วิดีโอ เสียง) จะใช้ MediaStore API ผ่าน ContentResolver สำหรับเอกสารทั่วไป จะใช้ Storage Access Framework ผ่าน Intent ACTION_OPEN_DOCUMENT บน Android 11+ การเข้าถึงโดยตรงไปยังรูทของพื้นที่จัดเก็บภายนอกถูกห้ามโดยสมบูรณ์ และนักพัฒนาทุกคนต้องใช้ API ใหม่
MediaStore คือ ContentProvider ของระบบสำหรับเข้าถึงไฟล์มีเดียบนอุปกรณ์ ผ่าน ContentResolver แอปจะขอ Uri ของไฟล์แทนพาธโดยตรง MediaStore.Files — สำหรับไฟล์ทุกประเภท, Images — สำหรับรูปภาพ, Video — สำหรับวิดีโอ, Audio — สำหรับบันทึกเสียง การเขียนไปยังไดเรกทอรีที่ใช้ร่วมกันทำผ่าน insert() ด้วย DISPLAY_NAME, MIME_TYPE และ RELATIVE_PATH หลังจากการแทรก แอปจะได้รับ Uri ซึ่งใช้เขียนไบต์ ประเภท MIME มีบทบาทสำคัญ — ประเภทที่ไม่ถูกต้องจะทำให้เกิดข้อผิดพลาดเมื่อเปิดไฟล์
val contentValues = ContentValues().apply {
put(MediaStore.MediaColumns.DISPLAY_NAME, "report.pdf")
put(MediaStore.MediaColumns.MIME_TYPE, "application/pdf")
put(MediaStore.MediaColumns.RELATIVE_PATH, "Documents/Reports")
}
val uri = contentResolver.insert(
MediaStore.Files.getContentUri("external"),
contentValues
)
uri?.let {
contentResolver.openOutputStream(it)?.use { stream ->
stream.write(pdfBytes)
}
}
SAF มีอินเทอร์เฟซแบบรวมสำหรับเลือกและสร้างไฟล์โดยไม่ต้องใช้สิทธิ์รันไทม์ Intent ACTION_OPEN_DOCUMENT เปิดตัวจัดการไฟล์ระบบบน Android หลังจากการเลือก แอปจะได้รับ Uri แบบ content:// ที่มีการเข้าชั่วคราวผ่าน FLAG_GRANT_READ_URI_PERMISSION ACTION_CREATE_DOCUMENT อนุญาตให้บันทึกไฟล์ไปยังตำแหน่งพื้นที่จัดเก็บภายนอกที่ผู้ใช้เลือก SAF ทำงานบน Android 5+ และให้การเข้าถึงไฟล์จากผู้ให้บริการคลาวด์ที่เชื่อมต่อผ่าน DocumentsProvider
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT).apply {
addCategory(Intent.CATEGORY_OPENABLE)
type = "*/*"
putExtra(Intent.EXTRA_MIME_TYPES, arrayOf(
"application/pdf",
"text/plain"
))
}
startActivityForResult(intent, REQUEST_CODE)
ทั้งสองแพลตฟอร์มมีกลไกในตัวสำหรับให้ผู้ใช้เลือกไฟล์ เอกสารในแอปพลิเคชันมือถือ จะถูกส่งผ่านตัวเลือกระบบที่ให้การเข้าถึงไฟล์ชั่วคราวโดยไม่ต้องมีสิทธิ์ถาวร บน iOS คือ UIDocumentPickerViewController บน Android คือ ACTION_OPEN_DOCUMENT เอกสารในแอปพลิเคชันมือถือสามารถเลือกได้จากทั้งพื้นที่จัดเก็บในเครื่องและบริการคลาวด์ ผู้ใช้ระบุไฟล์อย่างชัดเจน และแอปจะได้รับ Uri หรือ URL ที่มีระยะเวลามีผลจำกัด
UIDocumentPickerViewController เปิด Files App และอนุญาตให้เลือกเอกสารอย่างน้อยหนึ่งรายการ โหมด: นำเข้า (คัดลอกไปยังแซนด์บ็อกซ์) และเปิด (เข้าถึงผ่าน URL แบบ security-scoped) สำหรับการกรองไฟล์ จะส่งอาร์เรย์ของประเภท UTType — ตัวอย่างเช่น .pdf และ .plainText หลังจากได้รับ URL แอปต้องเรียก startAccessingSecurityScopedResource() ก่อนอ่าน และ stopAccessingSecurityScopedResource() หลังจากเสร็จสิ้น การไม่เรียก stopAccessing ทำให้ทรัพยากรระบบรั่วไหล เอกสารในแอปพลิเคชันมือถือบน iOS ต้องการการปล่อยสิทธิ์ชั่วคราวหลังจากทำงานกับไฟล์เสร็จ
let picker = UIDocumentPickerViewController(
forOpeningContentTypes: [.pdf, .plainText]
)
picker.allowsMultipleSelection = true
picker.delegate = self
present(picker, animated: true)
// ปล่อยการเข้าถึงในตัวแทน
func documentPicker(
_ controller: UIDocumentPickerViewController,
didPickDocumentsAt urls: [URL]
) {
guard let url = urls.first else { return }
url.startAccessingSecurityScopedResource()
defer { url.stopAccessingSecurityScopedResource() }
}
FileProvider คือคลาสย่อยของ ContentProvider สำหรับแชร์ไฟล์อย่างปลอดภัยระหว่างแอป มันสร้าง Uri แบบ content:// ชั่วคราวตามไฟล์จากไดเรกทอรี XML ที่ระบุ แอปอื่นได้รับสิทธิ์เข้าถึงผ่าน Intent ด้วย FLAG_GRANT_READ_URI_PERMISSION DocumentProvider แตกต่างจาก FileProvider ตรงที่เผยแพร่ไฟล์ใน SAF และอนุญาตให้แอปอื่นเรียกดูเนื้อหาของแอปของคุณเป็นส่วนหนึ่งของระบบไฟล์ ในการใช้งาน DocumentsProvider คุณต้องโอเวอร์ไรด์ queryRoots(), queryChildDocuments() และ openDocument() จากนั้นลงทะเบียนใน AndroidManifest.xml
การซิงค์บนคลาวด์ช่วยให้ผู้ใช้เข้าถึงเอกสารบนอุปกรณ์ทั้งหมดของตน ระบบไฟล์ในแอปพลิเคชันมือถือ ได้รับการเสริมด้วยชั้นคลาวด์: UIDocument บน iOS จะติดตามการเปลี่ยนแปลงโดยอัตโนมัติและซิงค์ผ่าน iCloud บน Android ฟังก์ชันการทำงานที่คล้ายกันสร้างขึ้นผ่าน Google Drive API หรือ DocumentsProvider ที่มีรากคลาวด์ การเข้าใจระบบไฟล์ในการพัฒนามือถือเป็นสิ่งสำคัญสำหรับการสร้างการซิงค์ที่เชื่อถือได้ระหว่างอุปกรณ์
UIDocument เป็นคลาสนามธรรมสำหรับทำงานกับเอกสาร iCloud มันบันทึกการเปลี่ยนแปลงโดยอัตโนมัติ อ่านข้อมูล และแจ้งให้ตัวแทนทราบเกี่ยวกับการอัปเดต ในกรณีที่เกิดข้อขัดแย้งในการเขียน NSFileVersion จะแสดงรายการเวอร์ชันที่มีอยู่ — ผู้พัฒนาสามารถเลือกเวอร์ชันล่าสุดหรือแสดงตัวเลือกการแก้ปัญหาข้อขัดแย้งให้ผู้ใช้ การกำหนดค่า Ubiquity Container ในความสามารถของโปรเจกต์เป็นสิ่งจำเป็นสำหรับ iCloud Drive NSFileCoordinator และ NSFilePresenter ป้องกันการแข่งขันข้อมูลระหว่างการเข้าถึงพร้อมกันจากหลายเธรดหรืออุปกรณ์
iOS จะรวม Documents Directory ใน iCloud Backup โดยอัตโนมัติ Android ทำงานกับ Auto Backup for Apps — ระบบบันทึกข้อมูลจาก getFilesDir(), SharedPreferences และฐานข้อมูล SQLite ไปยัง Google Drive แคชและไฟล์ภายนอกไม่รวมอยู่ในการสำรองข้อมูล ทั้งสองแพลตฟอร์มอนุญาตให้กำหนดค่าข้อยกเว้น: บน iOS ผ่าน NSURLIsExcludedFromBackupKey, บน Android ผ่านการกำหนดค่า XML ของกฎการสำรองข้อมูล การเข้ารหัสไฟล์ที่มีข้อมูลส่วนบุคคลเป็นสิ่งจำเป็น — บน iOS ใช้ NSDataWritingFileProtectionComplete, บน Android ใช้ EncryptedFile จากไลบรารี security-crypto
| พารามิเตอร์ | iOS | Android |
|---|---|---|
| การสำรองข้อมูลเริ่มต้น | Documents และ Library | getFilesDir(), SharedPreferences, DB |
| ข้อยกเว้นไฟล์ | isExcludedFromBackupKey | กฎ XML การสำรองข้อมูล (fullBackupContent) |
| การเข้ารหัส | NSDataWritingFileProtectionComplete | EncryptedFile (security-crypto) |
| การซิงค์คลาวด์ | UIDocument + iCloud | Google Drive API + SAF |
| การกู้คืนอัตโนมัติ | iCloud Restore หลังการติดตั้ง | Auto Backup เมื่อติดตั้งใหม่ |
คำถามที่พบบ่อย
Sandbox คือสภาพแวดล้อมแบบแยกส่วนสำหรับแต่ละแอปบน iOS แอปไม่สามารถเข้าถึงไฟล์ของแอปอื่นได้หากไม่ใช้ตัวเลือกระบบ เช่น UIDocumentPickerViewController
Scoped Storage คือโมเดลการเข้าถึงระบบไฟล์แบบจำกัดบน Android 10+ แอปอ่านโดยตรงเฉพาะไฟล์ของตัวเอง ใช้ MediaStore API สำหรับมีเดีย และ Storage Access Framework สำหรับเอกสาร
ใช้ UIDocumentPickerViewController — ตัวเลือกระบบสำหรับเลือกเอกสารจาก Files App หรือ iCloud Drive หลังจากการเลือก คุณจะได้รับ URL แบบ security-scoped ที่มีการเข้าถึงชั่วคราว
สำหรับไฟล์มีเดีย ใช้ MediaStore API ผ่าน ContentResolver โดยระบุประเภท MIME สำหรับเอกสารทั่วไป ใช้ Storage Access Framework กับ Intent ACTION_OPEN_DOCUMENT
FileProvider คือคลาสย่อยของ ContentProvider สำหรับแชร์ไฟล์อย่างปลอดภัยระหว่างแอปผ่าน Uri แบบ content:// ชั่วคราวด้วย FLAG_GRANT_READ_URI_PERMISSION
สรุป
เราจะพัฒนาแอปพลิเคชันบนมือถือแบบครบวงจร
IT Sectr สร้างแอปพลิเคชัน iOS และ Android สำหรับสตาร์ทอัพและธุรกิจตั้งแต่ปี 2017 เราจะให้คำแนะนำและเสนอวิธีแก้ปัญหาที่ดีที่สุดแก่คุณ