こんにちは、Androidエンジニアの 近藤(ふなち / Hunachi) です☺︎
最近、担当していた大きめの改修業務がひと段落しました。一息つきつつ、もっと成長したいなと思う日々です💻
はじめに(今回紹介したいこと)
この記事で紹介したいことは、Android 保存したいファイル名が既に使われていた場合の対処法…言い換えると
「Androidで、ファイルを保存する場合にファイル名がコンフリクトしないようにする」
方法です。
ファイルには色々ありますが、ここでは、ダウンロードフォルダにファイル(コンテンツ)を保存する場合を例にし紹介します。
ファイルの保存方法
ファイルの保存方法が大きく関わってくるので、Androidでのファイルの保存方法から軽く紹介します。
昔からあるファイルを保存する方法として
getExternalStoragePublicDirectory
を使う方法がありますが、こちらはDeprecated in API level 29なので、API level 29(Q)以降で使うことができません。
API level 29(Q)以降では、どうするのかというとContent providers
を使う必要があります。
Content providers | Android Developers
その他にも、DownloadManager
を使い、サーバー(外部)からデータを取得しそのまま保存する方法もあります。
この場合も上記と同じように内部で実装されています。そのため同様に考えると良いと思います。
ファイル名を一意に
保存方法が、Qより前とQ以降で異なるという話をしました。
Q以降の場合(Content providers
を使った方法)では、実は命名をいい感じにしてくれるので、コンフリクトすることはありません!
Qより前でのgetExternalStoragePublicDirectory
を使う方法では、同じファイル名のファイルがすでに存在した場合、いい感じにしてくれずエラーを吐いて保存できないということが起きます。
よって以下のコードのような対応を行う必要があります。 ファイル名がコンフリクトした場合、ファイル名(1)、ファイル名(2)…になるようにしてみました。
※ ユーザーがアプリ内でファイル名を任意に決定できる場合等は考慮に入れていないコードになります。
/** * ダウンロード用の一意なファイル名を作成する * @param baseName ファイル名のベース * @param extension 拡張子 * @return 一意なファイル名 */ private fun createDownloadUniqueFileName(baseName: String, extension: String): String { var fileName = "$baseName.$extension" // Q以前の場合は、ファイルが存在しないか確認する(いい感じにしてくれないため) if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) { var index = 0 while (true) { if (index != 0) { fileName = "$baseName($index).$extension" } val file = File( Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS), fileName ) if (file.exists().not()) break index++ if (index == Int.MAX_VALUE) { throw FileAlreadyExistsException("Too many files with the same name") // 2147483647個も同じファイル名のファイルをダウンロードしてる人はいないはず } } } return fileName }
これで既に同一ファイル名のファイルがあったとしても、ファイルを保存することができるようになりました。
まとめ
今回は、Android 保存したいファイル名が既に使われていた場合の対処法の一例を紹介しました。
もっといい案あるよ!という方がいましたら X(私のアカウント)や勉強会 で私に話しかけていただけるととっても嬉しいです ⭐️
最後に
ヤプリにご興味がある方いましたら是非カジュアル面談でヤプリ社員と話しませんか?
最後まで読んでいただきありがとうございました!