タイトル

執筆:2023.10.28
編集:2023.10.28


Android11は仕様がかわり

画像は 各ストレージ直下の[ DICM と Pictures ]フォルダ内(その子フォルダ)でしか保存できません。
アプリの権限は、[ READ_EXTERNAL_STORAGE , WRITE_EXTERNAL_STORAGE ]が必要です。

またすべてのフォルダで画像を保存できるようにするには、'android.permission.MANAGE_EXTERNAL_STORAGE'権限を取得する必要があります。

マニフェストとコード(権限リクエスト)と両方記述が必要です

ContentResolver.insert
に渡す Uri は、
.getContentUri([定義済み定数|ボリューム名]) から取得します。
定義済みの Uri 変数定数を使うと メインストレージにしか書き込めません。(ここが皆がわからずに SDcradに書き込めないと はまっている箇所です)

使える 外部 volumeは、
"external_primary" , MediaStore.VOLUME_EXTERNAL_PRIMARY
"xxxx-xxxx"; // SDカードの場合は数値に置き換える
です。

MediaStore.VOLUME_EXTERNAL_PRIMARYの値は "external_primary" です。
DelphiなどJAVA以外から操作する場合は、定数は定義されていないので 文字列を使ったほうがいいでしょう。

'xxxx-xxxx'は MediaStore.getExternalVolumeNames(context)で一覧を取得できます。
uri = MediaStore.Images.Media.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY);
uri = MediaStore.Images.Media.getContentUri("external_primary");
uri = MediaStore.Images.Media.getContentUri("xxxx-xxxx");
uri = MediaStore.Files.getContentUri(volume);
のようになります。

SDCardは ファイルパスが
/strage/xxxx-xxxx/
になっています。
xxxx-xxxxは、メディアごとの識別子なので、別のカードやフォーマットすると変わってしまいます

ファイルパスは
メインストレージ: /strage/emulated/[ユーザーID:通常は0]/相対パス(可視領域)
外付けメディア: /strage/xxxx-xxxx/相対パス(可視領域)
ファイル名から取得する場合は、正規表現で ^/strage/([^/]+)/ にファイル名がマッチするか確認すればいいと思います
MediaStore.getExternalVolumeNames(context)のリスト値にないなら、メインストレージとか仮想メディアとか。
仕様が規格外の端末は相手にする必要はないと思います。

マッチしたらそれが ファイルの volumeです。getContentUriに渡す値になります!

ContentResolver.insert関数の結果で得た URIを
ContentResolver.openOutputStream関数に渡してオープンすると パスとファイルが作成されるので
存在しないフォルダの準備はしなくていいです。

フォルダを指定したい場合は、
ContentResolverに渡す値に, "relative_path" の項目で 相対パス(可視領域) を追加します
Pictures/子フォルダ/テスト.jpg に保存したい場合は
"relative_path"の値は "Pictures/子フォルダ" になります