Работа с файлами (External Storage)

В этой статье мы поговорим как в Kotlin работать с External Storage файлами. Рассмотрим следующие вопросы: как записать файл в Kotlin External Storage, как прочитать файл в Kotlin External Storage, как фильтровать и найти файл в Kotlin External Storage директории, в том числе и рекурсивный поиск файлов.

Разрешение на запись в External Storage

Для того, чтобы работать с внешней памятью на Android устройствах нужно получить разрешение на запись, для этого необходимо сделать несколько шагов.

  1. Добавим разрешение в AndroidManifest.xml
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

И для Android устройств ниже версии 10, в AndroidManifest.xml пропишем авто получение разрешения.

<application
        android:requestLegacyExternalStorage="true"
...

2. Проверим наличие разрешения на запись следующей функцией:

// Получить разрешение на ЗАПИСЬ
fun  checkWritePermissions(){
   when{
       ContextCompat.checkSelfPermission(APP, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED -> {
          //РАЗРЕШЕНИЕ УЖЕ ПОЛУЧЕНО
       }
       else -> {
           //РАЗРЕШЕНИЕ НЕ ПОЛУЧЕНО
           // Запрашиваем разрешение у пользователя
           permisLauncher.launch(Manifest.permission.WRITE_EXTERNAL_STORAGE)
       }
   }
}

3. Установим слушатель разрешения на запись следующей функцией:

// Слушатель разрешения
fun  registerPermissionListener(){
    permisLauncher = APP.registerForActivityResult(ActivityResultContracts.RequestPermission()){
        if(it) {
            //РАЗРЕШЕНИЕ ПОЛУЧЕНО
        }else{
            //ПОЛЬЗОВАТЕЛЬ ОТКАЗАЛ В РАЗРЕШЕНИИ
            // Запустим диалог поясняющий пользователю, зачем нужно это разрешение
            showDialog()
        }
    }
}

4. Запустим диалог поясняющий пользователю, зачем нужно это разрешение

// Диалог поясняющий необходимость разрешения
fun showDialog() {
   AlertDialog.Builder(APP)
      .setTitle("Разрешение на запись")
      .setMessage("Дайте разрешение на запись. В противном случае приложение работать не будет!")
      .setPositiveButton("Разрешить") { dialog, which ->
          // Снова запрашиваем разрешение
          checkWritePermissions()
      }
      .setNegativeButton("Отклонить") { dialog, which ->
          // Закрываем приложение
          APP.finish()
      }
      .show()
}

5. Теперь в MainActivity в методе onCreate вызовем методы созданные ранее:

...
override fun onCreate(savedInstanceState: Bundle?) {
...
        registerPermissionListener()
        checkWritePermissions()
...

Теперь при старте, приложение будет проверять и при необходимости запрашивать разрешение на запись в External Storage. Можно приступать к работе с файлами.

Запись файла

Задача
Создадим метод, который создает файл “hello.txt” и записывает в него текст “Запись 1“.

Решение
Входными параметрами функции будут: путь (filePath), имя файла (fileName), текст (text). Все действия обернём в try catch для исключения ошибок.

    // Записать файл
    fun writeFile(filePath: String, fileName: String, text: String) {
        try {
            //Создается объект файла, при этом путь к файлу находиться методом Environment
            val myFile = File(Environment.getExternalStorageDirectory().toString() + "/" + filePath + fileName)
            // Создается файл, если он не был создан
            myFile.createNewFile()
            // После чего создаем поток для записи
            val outputStream = FileOutputStream(myFile)
            // Производим непосредственно запись
            outputStream.write(text.toByteArray())
            // Закрываем поток
            outputStream.close()

            // Просто для удобства визуального контроля исполнения метода в приложении
            Toast.makeText(this, "File is write", Toast.LENGTH_SHORT).show()
        } catch (e: Exception) {
            e.printStackTrace()
            Toast.makeText(this, "File is not write", Toast.LENGTH_SHORT).show()
        }
    }

Вызвать функцию можно следующим образом:

writeFile("download/", "hello.txt", "Запись 1")

Эта функция создаст файл “hello.txt” в папке Download и запишет в него следующий текст “Запись 1“.

Чтение файла

Теперь получим текст из ранее созданного файла.

Входными параметрами функции будут: путь (filePath), имя файла (fileName). Выходной параметр типа String. То есть на выходи функции получим строку с текстом из файла.

    // Чтение файла
    fun readFile(filePath: String, fileName: String): String {

        // Аналогично создается объект файла
        val myFile = File(Environment.getExternalStorageDirectory().toString() + "/" + filePath + fileName)
        try {
            val inputStream = FileInputStream(myFile)

            // Буферизируем данные из выходного потока файла
            val bufferedReader = BufferedReader(InputStreamReader(inputStream))

            // Класс для создания строк из последовательностей символов
            val stringBuilder = StringBuilder()
            var line: String?

            try {
                // Производим построчное считывание данных из файла в конструктор строки,
                while (bufferedReader.readLine().also { line = it } != null) {
                    stringBuilder.append(line)
                }
                return stringBuilder.toString()
            } catch (e: IOException) {
                e.printStackTrace()
                return ""
            }
        } catch (e: FileNotFoundException) {
            e.printStackTrace()
            return ""
        }
    }

Вызвать функцию можно следующим образом:

val text = readFile("download/", "hello.txt")

Находим файлы в директории с нужным расширением

Создадим функцию для поиска фалов в главной директории с расширением “.txt” рекурсивно (то есть включая все подкаталоги)

Входными параметрами функции будут: путь (filePath), фильтр (filter). Выходной параметр типа ArrayList<File>. То есть на выходи функции получим список файлов.

fun allFilesInDir(filePath: String, filter: String): ArrayList<File>{
    var filesFromDir = ArrayList<File>()
    val path = Environment.getExternalStorageDirectory().toString() + "/" + filePath
    val directory = File(path)
    val files = directory.walkTopDown()
    // Перебираем все файлы
    for (file in files) {
       // Отсеиваем файлы подходящие под фильтр
       if(file.name.endsWith(filter)) {
          filesFromDir.add(file)
       }
    }
        return filesFromDir
}

Вызовем эту функцию и получим результат её работы:

val text = allFilesInDir("", ".txt").map { it.name }.toString()

Заключение

Работа с External Storage файлами в Kotlin очень лаконичная и удобная. С типом данных File вы можете делать много операций, таких как: удалить, переименовать, получить имя, расширение, размер файла и многое другое.

Поделись с друзьями:
Если вам понравилась статья, подписывайтесь на наши социальные сети.

Оставьте комментарий

семнадцать − 8 =