モバイルデバイスの外部ストレージは、適切な権限を持つアプリケーションがアクセスできる取り外し可能なデータメディアです。Android Open Source Project、2026によると、Android 10以降、GoogleはScoped Storageを導入しました。これはファイルシステムへの直接アクセスを制限し、共有メディアファイルを操作するためにMediaStore APIの使用を必要とするモデルです。このアプローチにより、ユーザーデータのセキュリティが向上し、アプリケーション間の情報漏洩が防止されます。
重要ポイント
READ_EXTERNAL_STORAGEおよびWRITE_EXTERNAL_STORAGEはAndroid 9以下で必須です外部ストレージは、アプリケーションの保護されたサンドボックスの一部ではなく、他のプログラムがアクセスできるメモリ領域です。Androidでは、この用語は通常SDカードを指しますが、外部ストレージにはOTGアダプターを介して接続されたUSBドライブや、ファイルシステムレベルで統合されたクラウドドライブも含まれます。内部ストレージとの主な違いは、分離性の欠如です。適切な権限を持つアプリケーションは、外部ストレージ内のファイルを読み取り、変更できます。
Androidでは、外部ストレージには2つの主要モードがあります:ポータブルとアダプタブルです。SDカードをポータブルストレージとしてフォーマットすると、取り外し可能なままで、FAT32またはexFATでフォーマットされます。アダプタブルストレージモードでは、カードはext4でフォーマットされ、AES-128を使用して暗号化され、システムはそれを内部メモリの拡張として扱います。アダプタブルモードはAndroid 6.0 Marshmallow以降で利用可能ですが、デバイスメーカーはファームウェアでこの機能を無効にすることがよくあります。Google Play Consoleの統計によると、アダプタブルストレージをサポートするAndroidデバイスは約12%のみであり、SDカードによる内部メモリの拡張を唯一のオプションとして依存することは推奨されません。
iOSには従来のSDカードはありません。Appleは、すべてのストレージが内部フラッシュメモリとiCloud上に構築されたクローズドアーキテクチャを使用しています。外部ドライブはLightningまたはUSB-Cポートを介してサポートされていますが、それらへのアクセスはシステムファイルマネージャーのFilesとDocument Pickerと統合するアプリケーションに制限されています。ユーザーが外部ソースからどのファイルを開くかを決定します — アプリケーションは明示的な同意なしに接続されたドライブをスキャンできません。
外部メディアの選択は、速度、容量、ポータビリティの要件によって異なります。メディアの種類によって、モバイルアプリケーションでの速度特性、ファイルシステム、および使用例が異なります。
| メディアの種類 | 最大容量 | 読み取り速度 | ファイルシステム |
|---|---|---|---|
| SD UHS-I | 2 TB | 最大104 MB/s | exFAT / FAT32 |
| SD UHS-II | 2 TB | 最大312 MB/s | exFAT / FAT32 |
| USB 3.0 OTG | 2 TB | 最大400 MB/s | exFAT / NTFS / FAT32 |
| USB-C SSD | 4 TB | 最大1000 MB/s | exFAT / APFS / NTFS |
SDカードは書き込み速度によって分類されます:Class 10(10 MB/s)、U1(10 MB/s)、U3(30 MB/s)、V30(30 MB/s)、V90(90 MB/s)。4Kビデオの録画には、少なくともU3またはV30のカードが必要です。大きなメディアファイル(写真処理、ビデオ編集)を扱うモバイルアプリケーションでは、U3クラス以上のカードを使用することをお勧めします。カードの速度はアプリケーションのパフォーマンスに直接影響します:Class 10カードでの4Kビデオ録画は、帯域幅が不十分なためフレーム落ちを引き起こす可能性があります。アプリケーション用のSDカードを選択する際は、速度クラスだけでなく、アプリケーションパフォーマンスクラスの評価にも注意してください:A1およびA2は、データベースおよびアプリケーションキャッシュ操作に重要な最小ランダム読み取りおよび書き込みパフォーマンスを示します。
USB-OTG(On-The-Go)は、アダプターを介してモバイルデバイスに外部USBドライブを接続できるようにします。Androidはバージョン3.1からOTGをサポートしていますが、ドライブをマウントするにはファイルシステムをサポートするカーネルが必要です。FAT32とexFATは標準でサポートされており、NTFSはカスタムカーネルを持つデバイスまたはParagonライブラリを介してのみサポートされます。iOSでは、USBドライブのサポートはiOS 13のFilesアプリケーションで登場しました。ドライブを操作するには、アプリケーションはUIDocumentPickerViewControllerを使用する必要があり、ユーザーが選択したファイルへのアクセスのみを提供します — iOSはドライブのファイルシステムへの完全なアクセスを許可しません。
クラウドサービス — iCloud、Google Drive、Dropbox — はシステムプロバイダーを介してデバイスのファイルシステムに統合できます。iOSでは、iCloud DriveはアプリケーションのSandboxコンテナの一部であり、ファイルはユーザーのデバイス間で自動的に同期されます。Androidでは、Google Driveはファイルの読み書きのためのAPIを提供しますが、ファイルシステムへの直接マウントは行われません。大量のデータを扱うモバイルアプリケーションの場合、クラウドストレージはSDカードの代替として機能します。特に拡張スロットのないデバイスで有効です。
Androidでの外部ストレージの操作は、オペレーティングシステムのバージョンとScoped Storageモデルによって異なります。ファイルアクセスのプロセスは、Android 9以下、Android 10–12、Android 13+で異なります。
Android 10以降、共有メディアファイルにアクセスする主な方法はMediaStore APIです。このAPIは、共有ストレージ内の画像、動画、音声ファイルを読み書きするための統一インターフェースを提供します。アプリケーションは自分のファイルにアクセスするためにREAD_EXTERNAL_STORAGE権限を必要としませんが、他のアプリケーションのファイルを読み取るには、依然として明示的なユーザー権限が必要です。MediaStoreはメディアファイルを自動的にインデックス化し、それらを照会するためのContentResolverを提供します。
val collection = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
MediaStore.Images.Media.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY)
} else {
MediaStore.Images.Media.EXTERNAL_CONTENT_URI
}
val projection = arrayOf(
MediaStore.Images.Media._ID,
MediaStore.Images.Media.DISPLAY_NAME
)
val cursor = contentResolver.query(collection, projection, null, null, null)
クエリを実行すると、ファイルのURIを含むCursorが返されます。ファイルを読み取るには、contentResolver.openInputStream(uri)を介してInputStreamを開きます。書き込みも同様にcontentResolver.openOutputStream(uri)を介して行われます。MediaStoreは名前の競合を自動的に処理し、contentResolver.insert(uri, values)を介して新しいファイルを挿入する機能を提供し、作成されたファイルのURIを返します。
Storage Access Framework(SAF)は、Android 10+で任意のファイルにアクセスするための推奨方法です。SAFはシステムファイルピッカーダイアログを提供し、ユーザーがアプリケーションに特定のファイルやディレクトリへのアクセスを許可します。アプリケーションは、デバイスが再起動されるか、設定で明示的に権限が取り消されるまで有効なアクセスURIを受け取ります。SAFはREAD_EXTERNAL_STORAGEやWRITE_EXTERNAL_STORAGEの権限を必要としないため、Google Playへの公開が簡素化され、権限リクエストの数が減少します。
val requestCode = 42
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE)
intent.addFlags(
Intent.FLAG_GRANT_READ_URI_PERMISSION
or Intent.FLAG_GRANT_WRITE_URI_PERMISSION
)
startActivityForResult(intent, requestCode)
iOSでは、外部ファイルの操作はUIDocumentPickerViewControllerを中心に構築されており、ユーザーが特定のファイルやディレクトリを選択するために開きます。アプリケーションは外部ドライブのファイルシステムに直接アクセスできません — ユーザーが明示的に選択したファイルのみにアクセスできます。ファイル選択後、アプリケーションはSandboxコンテナ内の一時的なURLを受け取り、FileManagerを介してファイルを操作できます。
UIDocumentPickerViewControllerを使用すると、ユーザーはiCloud Drive、接続されたUSBドライブ、サードパーティのクラウドサービスなど、利用可能なソースから1つまたは複数のファイルを選択できます。選択後、コントローラーはアプリケーションがセッション中に読み取り可能なURLの配列を返します。アプリケーション終了後もアクセスを維持するには、startAccessingSecurityScopedResource()を呼び出し、UserDefaultsにセキュリティスコープのブックマークを保存する必要があります。
let picker = UIDocumentPickerViewController(
forOpeningContentTypes: [.data, .image],
asCopy: true
)
picker.delegate = self
func documentPicker(
controller: UIDocumentPickerViewController,
didPickDocumentsAt urls: [URL]
) {
guard let url = urls.first else { return }
let data = try Data(contentsOf: url)
}
パラメータasCopy: trueは、iOSがファイルをアプリケーションのSandboxコンテナにコピーし、アプリケーションが元のファイルから独立した独自のコピーを取得することを意味します。asCopy: falseを指定すると、アプリケーションは元のファイルへの参照を受け取りますが、アクセスするにはurl.startAccessingSecurityScopedResource()を呼び出す必要があります。この呼び出しがないと、ファイルの読み取りで例外が発生します。ファイルの操作が終了したら、リソースを解放してディスクリプタリークを防ぐために、必ずurl.stopAccessingSecurityScopedResource()を呼び出してください。
外部ストレージのセキュリティは、ファイルが他のアプリケーションからアクセス可能であるため、内部ストレージよりも低くなります。READ_EXTERNAL_STORAGE権限を持つアプリケーションは、SDカード上のすべてのファイルを読み取ることができます。これには、機密データを一時的に保存するためにアプリケーションが作成したファイルも含まれます。悪意のあるアプリケーションがSDカードをスキャンし、暗号化されていないユーザーデータを抽出する可能性があります。したがって、認証トークン、パスワード、またはユーザーの個人データを外部ストレージに平文で保存することは強く推奨されません。
Android 10+では、Scoped Storageは共有ファイルシステムへのアプリケーションのアクセスを大幅に制限します。アプリケーションは権限なしに自分で作成したファイルのみを読み取ることができ、他のアプリケーションのファイルにアクセスするにはSAFダイアログを介した明示的なユーザーの同意が必要です。ただし、この制限はターゲットSDKバージョンが29未満のアプリケーションには適用されません — これらは古いモデルの下で動作し続け、アプリケーションを更新していないユーザーにリスクをもたらします。Google Playは2021年以降、すべての新規および更新アプリケーションにtargetSdkVersion 29+を要求しています。
フォーマットと暗号化はセキュリティのもう一つの重要な側面です。ポータブルストレージとしてフォーマットされたSDカードは、デフォルトではシステムによって暗号化されません。アダプタブルストレージを使用する場合でも、デバイスメーカーがファームウェアでこの機能を有効にしている場合にのみ暗号化がアクティブになります。外部ストレージに保存された機密データを保護するには、AndroidX SecurityのEncryptedFileライブラリを使用します。これは、システムの暗号化に関係なく、AES256-GCMを使用して各ファイルを個別に暗号化します。
外部ストレージの効果的な使用には、データの可用性とセキュリティのバランスが必要です。ユーザーファイルの信頼性の高いストレージを確保するために、次のルールに従ってください。
Environment.getExternalStorageState()を使用してSDカードのステータスを確認します。カードが取り外されている、破損している、または書き込み不可の場合があります外部ストレージは障害時にデータの整合性を保証しないことを覚えておくことが重要です。SDカードには書き換えサイクルに制限があり、特にClass 4やClass 6の低価格モデルでは顕著です。集中的な書き込み(ロギングやストリーミングデータのキャッシュなど)を行うと、カードは数ヶ月で故障する可能性があります。このようなシナリオでは、内部メモリまたはクラウドストレージを使用し、外部ストレージにはユーザーが損失を許容できると考えるファイルのみを保存してください。StatFsを介してSDカードの状態を定期的に確認し、重大なファイルシステムエラーをユーザーに通知してください。
よくある質問
Android 13+では、READ_EXTERNAL_STORAGE権限はより細かい権限に置き換えられました:READ_MEDIA_IMAGES、READ_MEDIA_VIDEO、READ_MEDIA_AUDIO。その他のファイルタイプにはSAFを使用してください。
はい、アダプタブルストレージ機能はAndroid 6.0+で利用可能です。カードはAES-128暗号化でext4にフォーマットされます。ただし、多くのメーカーがこのオプションを無効にしており、約88%のデバイスがアダプタブルストレージをサポートしていません。
Environment.getExternalStorageState()メソッドを使用します。これはステータス文字列を返します。MEDIA_MOUNTED値は、カードが読み取りと書き込みに利用可能であることを意味します。その他の値はエラーまたはカードがないことを示します。
Scoped Storageはアプリケーションを自身のコンテナと共有MediaStoreメディアギャラリーに制限します。完全なアクセスはデバイス上の任意のファイルを読み取ることを許可します。Scoped Storageはユーザーデータのセキュリティを向上させ、新しいアプリケーションには必須です。
ACTION_OPEN_DOCUMENT_TREEアクションでIntentを起動します。これによりシステムディレクトリピッカーダイアログが開きます。選択後、再起動まで有効なURI権限を取得します。このURIを後で使用するためにSharedPreferencesに保存します。
まとめ
ターンキー方式のモバイルアプリケーションを開発します
IT Sectrは2017年からスタートアップや企業向けにiOS・Androidアプリケーションを開発しています。私たちがご相談に乗り、最適なソリューションをご提案します。