From b4990806e09908d0b4aa6b3dca9e4bf0d91bee79 Mon Sep 17 00:00:00 2001 From: gedoor Date: Sat, 5 Sep 2020 10:25:32 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/ui/filechooser/FileChooserDialog.kt | 6 +- .../app/ui/filechooser/adapter/FileAdapter.kt | 8 +- .../app/ui/filechooser/utils/FileUtils.kt | 670 ------------------ .../java/io/legado/app/utils/FileUtils.kt | 662 ++++++++++++++++- 4 files changed, 667 insertions(+), 679 deletions(-) delete mode 100644 app/src/main/java/io/legado/app/ui/filechooser/utils/FileUtils.kt diff --git a/app/src/main/java/io/legado/app/ui/filechooser/FileChooserDialog.kt b/app/src/main/java/io/legado/app/ui/filechooser/FileChooserDialog.kt index 79d674d70..d7e6b05d6 100644 --- a/app/src/main/java/io/legado/app/ui/filechooser/FileChooserDialog.kt +++ b/app/src/main/java/io/legado/app/ui/filechooser/FileChooserDialog.kt @@ -168,10 +168,14 @@ class FileChooserDialog : DialogFragment(), fileItem?.path?.let { path -> if (mode == DIRECTORY) { toast("这是文件夹选择,不能选择文件,点击右上角的确定选择文件夹") - } else { + } else if (allowExtensions == null || + allowExtensions?.contains(FileUtils.getExtension(path)) == true + ) { (parentFragment as? CallBack)?.onFilePicked(requestCode, path) (activity as? CallBack)?.onFilePicked(requestCode, path) dismiss() + } else { + toast("不能打开此文件") } } } diff --git a/app/src/main/java/io/legado/app/ui/filechooser/adapter/FileAdapter.kt b/app/src/main/java/io/legado/app/ui/filechooser/adapter/FileAdapter.kt index 766532d61..4458b692e 100644 --- a/app/src/main/java/io/legado/app/ui/filechooser/adapter/FileAdapter.kt +++ b/app/src/main/java/io/legado/app/ui/filechooser/adapter/FileAdapter.kt @@ -11,7 +11,7 @@ import io.legado.app.lib.theme.getPrimaryTextColor import io.legado.app.ui.filechooser.entity.FileItem import io.legado.app.ui.filechooser.utils.ConvertUtils import io.legado.app.ui.filechooser.utils.FilePickerIcon -import io.legado.app.ui.filechooser.utils.FileUtils +import io.legado.app.utils.FileUtils import kotlinx.android.synthetic.main.item_path_filepicker.view.* import org.jetbrains.anko.sdk27.listeners.onClick import java.io.File @@ -60,11 +60,7 @@ class FileAdapter(context: Context, val callBack: CallBack) : data.add(fileParent) } currentPath?.let { currentPath -> - val files: Array? = callBack.allowExtensions?.let { allowExtensions -> - FileUtils.listDirsAndFiles(currentPath, allowExtensions) - } ?: let { - FileUtils.listDirsAndFiles(currentPath) - } + val files: Array? = FileUtils.listDirsAndFiles(currentPath) if (files != null) { for (file in files) { if (file == null || (!callBack.isShowHideDir && file.name.startsWith("."))) { diff --git a/app/src/main/java/io/legado/app/ui/filechooser/utils/FileUtils.kt b/app/src/main/java/io/legado/app/ui/filechooser/utils/FileUtils.kt deleted file mode 100644 index e7e2a4805..000000000 --- a/app/src/main/java/io/legado/app/ui/filechooser/utils/FileUtils.kt +++ /dev/null @@ -1,670 +0,0 @@ -package io.legado.app.ui.filechooser.utils - -import android.webkit.MimeTypeMap -import androidx.annotation.IntDef -import java.io.* -import java.nio.charset.Charset -import java.text.SimpleDateFormat -import java.util.* -import java.util.regex.Pattern - -/** - * 文件处理 - * - * - * - * @author 李玉江[QQ:1023694760] - * @since 2014-4-18 - */ -object FileUtils { - const val BY_NAME_ASC = 0 - const val BY_NAME_DESC = 1 - const val BY_TIME_ASC = 2 - const val BY_TIME_DESC = 3 - const val BY_SIZE_ASC = 4 - const val BY_SIZE_DESC = 5 - const val BY_EXTENSION_ASC = 6 - const val BY_EXTENSION_DESC = 7 - - @IntDef(value = [BY_NAME_ASC, BY_NAME_DESC, BY_TIME_ASC, BY_TIME_DESC, BY_SIZE_ASC, BY_SIZE_DESC, BY_EXTENSION_ASC, BY_EXTENSION_DESC]) - @kotlin.annotation.Retention(AnnotationRetention.SOURCE) - annotation class SortType - - /** - * 将目录分隔符统一为平台默认的分隔符,并为目录结尾添加分隔符 - */ - fun separator(path: String): String { - var path1 = path - val separator = File.separator - path1 = path1.replace("\\", separator) - if (!path1.endsWith(separator)) { - path1 += separator - } - return path1 - } - - fun closeSilently(c: Closeable?) { - if (c == null) { - return - } - try { - c.close() - } catch (ignored: IOException) { - } - - } - - /** - * 列出指定目录下的所有子目录 - */ - @JvmOverloads - fun listDirs( - startDirPath: String, - excludeDirs: Array? = null, @SortType sortType: Int = BY_NAME_ASC - ): Array { - var excludeDirs1 = excludeDirs - val dirList = ArrayList() - val startDir = File(startDirPath) - if (!startDir.isDirectory) { - return arrayOfNulls(0) - } - val dirs = startDir.listFiles(FileFilter { f -> - if (f == null) { - return@FileFilter false - } - f.isDirectory - }) - ?: return arrayOfNulls(0) - if (excludeDirs1 == null) { - excludeDirs1 = arrayOf() - } - for (dir in dirs) { - val file = dir.absoluteFile - if (!excludeDirs1.contentDeepToString().contains(file.name)) { - dirList.add(file) - } - } - when (sortType) { - BY_NAME_ASC -> Collections.sort(dirList, SortByName()) - BY_NAME_DESC -> { - Collections.sort(dirList, SortByName()) - dirList.reverse() - } - BY_TIME_ASC -> Collections.sort(dirList, SortByTime()) - BY_TIME_DESC -> { - Collections.sort(dirList, SortByTime()) - dirList.reverse() - } - BY_SIZE_ASC -> Collections.sort(dirList, SortBySize()) - BY_SIZE_DESC -> { - Collections.sort(dirList, SortBySize()) - dirList.reverse() - } - BY_EXTENSION_ASC -> Collections.sort(dirList, SortByExtension()) - BY_EXTENSION_DESC -> { - Collections.sort(dirList, SortByExtension()) - dirList.reverse() - } - } - return dirList.toTypedArray() - } - - /** - * 列出指定目录下的所有子目录及所有文件 - */ - @JvmOverloads - fun listDirsAndFiles( - startDirPath: String, - allowExtensions: Array? = null - ): Array? { - val dirs: Array? - val files: Array? = if (allowExtensions == null) { - listFiles(startDirPath) - } else { - listFiles(startDirPath, allowExtensions) - } - val dirsAndFiles: Array - dirs = listDirs(startDirPath) - if (files == null) { - return null - } - dirsAndFiles = arrayOfNulls(dirs.size + files.size) - System.arraycopy(dirs, 0, dirsAndFiles, 0, dirs.size) - System.arraycopy(files, 0, dirsAndFiles, dirs.size, files.size) - return dirsAndFiles - } - - /** - * 列出指定目录下的所有文件 - */ - @JvmOverloads - fun listFiles( - startDirPath: String, - filterPattern: Pattern? = null, @SortType sortType: Int = BY_NAME_ASC - ): Array { - val fileList = ArrayList() - val f = File(startDirPath) - if (!f.isDirectory) { - return arrayOfNulls(0) - } - val files = f.listFiles(FileFilter { file -> - if (file == null) { - return@FileFilter false - } - if (file.isDirectory) { - return@FileFilter false - } - - filterPattern?.matcher(file.name)?.find() ?: true - }) - ?: return arrayOfNulls(0) - for (file in files) { - fileList.add(file.absoluteFile) - } - when (sortType) { - BY_NAME_ASC -> Collections.sort(fileList, SortByName()) - BY_NAME_DESC -> { - Collections.sort(fileList, SortByName()) - fileList.reverse() - } - BY_TIME_ASC -> Collections.sort(fileList, SortByTime()) - BY_TIME_DESC -> { - Collections.sort(fileList, SortByTime()) - fileList.reverse() - } - BY_SIZE_ASC -> Collections.sort(fileList, SortBySize()) - BY_SIZE_DESC -> { - Collections.sort(fileList, SortBySize()) - fileList.reverse() - } - BY_EXTENSION_ASC -> Collections.sort(fileList, SortByExtension()) - BY_EXTENSION_DESC -> { - Collections.sort(fileList, SortByExtension()) - fileList.reverse() - } - } - return fileList.toTypedArray() - } - - /** - * 列出指定目录下的所有文件 - */ - fun listFiles(startDirPath: String, allowExtensions: Array?): Array? { - val file = File(startDirPath) - return file.listFiles { _, name -> - //返回当前目录所有以某些扩展名结尾的文件 - val extension = getExtension(name) - allowExtensions?.contentDeepToString()?.contains(extension) == true - || allowExtensions == null - } - } - - /** - * 列出指定目录下的所有文件 - */ - fun listFiles(startDirPath: String, allowExtension: String?): Array? { - return if (allowExtension == null) - listFiles(startDirPath, allowExtension = null) - else - listFiles(startDirPath, arrayOf(allowExtension)) - } - - /** - * 判断文件或目录是否存在 - */ - fun exist(path: String): Boolean { - val file = File(path) - return file.exists() - } - - /** - * 删除文件或目录 - */ - @JvmOverloads - fun delete(file: File, deleteRootDir: Boolean = false): Boolean { - var result = false - if (file.isFile) { - //是文件 - result = deleteResolveEBUSY(file) - } else { - //是目录 - val files = file.listFiles() ?: return false - if (files.isEmpty()) { - result = deleteRootDir && deleteResolveEBUSY(file) - } else { - for (f in files) { - delete(f, deleteRootDir) - result = deleteResolveEBUSY(f) - } - } - if (deleteRootDir) { - result = deleteResolveEBUSY(file) - } - } - return result - } - - /** - * bug: open failed: EBUSY (Device or resource busy) - * fix: http://stackoverflow.com/questions/11539657/open-failed-ebusy-device-or-resource-busy - */ - private fun deleteResolveEBUSY(file: File): Boolean { - // Before you delete a Directory or File: rename it! - val to = File(file.absolutePath + System.currentTimeMillis()) - - file.renameTo(to) - return to.delete() - } - - /** - * 删除文件或目录 - */ - @JvmOverloads - fun delete(path: String, deleteRootDir: Boolean = false): Boolean { - val file = File(path) - - return if (file.exists()) { - delete(file, deleteRootDir) - } else false - } - - /** - * 复制文件为另一个文件,或复制某目录下的所有文件及目录到另一个目录下 - */ - fun copy(src: String, tar: String): Boolean { - val srcFile = File(src) - return srcFile.exists() && copy(srcFile, File(tar)) - } - - /** - * 复制文件或目录 - */ - fun copy(src: File, tar: File): Boolean { - try { - if (src.isFile) { - val `is` = FileInputStream(src) - val op = FileOutputStream(tar) - val bis = BufferedInputStream(`is`) - val bos = BufferedOutputStream(op) - val bt = ByteArray(1024 * 8) - while (true) { - val len = bis.read(bt) - if (len == -1) { - break - } else { - bos.write(bt, 0, len) - } - } - bis.close() - bos.close() - } else if (src.isDirectory) { - tar.mkdirs() - src.listFiles()?.forEach { file -> - copy(file.absoluteFile, File(tar.absoluteFile, file.name)) - } - } - return true - } catch (e: Exception) { - return false - } - - } - - /** - * 移动文件或目录 - */ - fun move(src: String, tar: String): Boolean { - return move(File(src), File(tar)) - } - - /** - * 移动文件或目录 - */ - fun move(src: File, tar: File): Boolean { - return rename(src, tar) - } - - /** - * 文件重命名 - */ - fun rename(oldPath: String, newPath: String): Boolean { - return rename(File(oldPath), File(newPath)) - } - - /** - * 文件重命名 - */ - fun rename(src: File, tar: File): Boolean { - return src.renameTo(tar) - } - - /** - * 读取文本文件, 失败将返回空串 - */ - @JvmOverloads - fun readText(filepath: String, charset: String = "utf-8"): String { - try { - val data = readBytes(filepath) - if (data != null) { - return String(data, Charset.forName(charset)).trim { it <= ' ' } - } - } catch (ignored: UnsupportedEncodingException) { - } - - return "" - } - - /** - * 读取文件内容, 失败将返回空串 - */ - fun readBytes(filepath: String): ByteArray? { - var fis: FileInputStream? = null - try { - fis = FileInputStream(filepath) - val baos = ByteArrayOutputStream() - val buffer = ByteArray(1024) - while (true) { - val len = fis.read(buffer, 0, buffer.size) - if (len == -1) { - break - } else { - baos.write(buffer, 0, len) - } - } - val data = baos.toByteArray() - baos.close() - return data - } catch (e: IOException) { - return null - } finally { - closeSilently(fis) - } - } - - /** - * 保存文本内容 - */ - @JvmOverloads - fun writeText(filepath: String, content: String, charset: String = "utf-8"): Boolean { - try { - writeBytes(filepath, content.toByteArray(charset(charset))) - return true - } catch (e: UnsupportedEncodingException) { - return false - } - - } - - /** - * 保存文件内容 - */ - fun writeBytes(filepath: String, data: ByteArray): Boolean { - val file = File(filepath) - var fos: FileOutputStream? = null - return try { - if (!file.exists()) { - file.parentFile?.mkdirs() - file.createNewFile() - } - fos = FileOutputStream(filepath) - fos.write(data) - true - } catch (e: IOException) { - false - } finally { - closeSilently(fos) - } - } - - /** - * 追加文本内容 - */ - fun appendText(path: String, content: String): Boolean { - val file = File(path) - var writer: FileWriter? = null - return try { - if (!file.exists()) { - - file.createNewFile() - } - writer = FileWriter(file, true) - writer.write(content) - true - } catch (e: IOException) { - false - } finally { - closeSilently(writer) - } - } - - /** - * 获取文件大小 - */ - fun getLength(path: String): Long { - val file = File(path) - return if (!file.isFile || !file.exists()) { - 0 - } else file.length() - } - - /** - * 获取文件或网址的名称(包括后缀) - */ - fun getName(pathOrUrl: String?): String { - if (pathOrUrl == null) { - return "" - } - val pos = pathOrUrl.lastIndexOf('/') - return if (0 <= pos) { - pathOrUrl.substring(pos + 1) - } else { - System.currentTimeMillis().toString() + "." + getExtension(pathOrUrl) - } - } - - /** - * 获取文件名(不包括扩展名) - */ - fun getNameExcludeExtension(path: String): String { - try { - var fileName = File(path).name - val lastIndexOf = fileName.lastIndexOf(".") - if (lastIndexOf != -1) { - fileName = fileName.substring(0, lastIndexOf) - } - return fileName - } catch (e: Exception) { - return "" - } - - } - - /** - * 获取格式化后的文件大小 - */ - fun getSize(path: String): String { - val fileSize = getLength(path) - return ConvertUtils.toFileSizeString(fileSize) - } - - /** - * 获取文件后缀,不包括“.” - */ - fun getExtension(pathOrUrl: String): String { - val dotPos = pathOrUrl.lastIndexOf('.') - return if (0 <= dotPos) { - pathOrUrl.substring(dotPos + 1) - } else { - "ext" - } - } - - /** - * 获取文件的MIME类型 - */ - fun getMimeType(pathOrUrl: String): String { - val ext = getExtension(pathOrUrl) - val map = MimeTypeMap.getSingleton() - return map.getMimeTypeFromExtension(ext) ?: "*/*" - } - - /** - * 获取格式化后的文件/目录创建或最后修改时间 - */ - @JvmOverloads - fun getDateTime(path: String, format: String = "yyyy年MM月dd日HH:mm"): String { - val file = File(path) - return getDateTime(file, format) - } - - /** - * 获取格式化后的文件/目录创建或最后修改时间 - */ - fun getDateTime(file: File, format: String): String { - val cal = Calendar.getInstance() - cal.timeInMillis = file.lastModified() - return SimpleDateFormat(format, Locale.PRC).format(cal.time) - } - - /** - * 比较两个文件的最后修改时间 - */ - fun compareLastModified(path1: String, path2: String): Int { - val stamp1 = File(path1).lastModified() - val stamp2 = File(path2).lastModified() - return if (stamp1 > stamp2) { - 1 - } else if (stamp1 < stamp2) { - -1 - } else { - 0 - } - } - - /** - * 创建多级别的目录 - */ - fun makeDirs(path: String): Boolean { - return makeDirs(File(path)) - } - - /** - * 创建多级别的目录 - */ - fun makeDirs(file: File): Boolean { - return file.mkdirs() - } - - class SortByExtension : Comparator { - - override fun compare(f1: File?, f2: File?): Int { - return if (f1 == null || f2 == null) { - if (f1 == null) { - -1 - } else { - 1 - } - } else { - if (f1.isDirectory && f2.isFile) { - -1 - } else if (f1.isFile && f2.isDirectory) { - 1 - } else { - f1.name.compareTo(f2.name, ignoreCase = true) - } - } - } - - } - - class SortByName : Comparator { - private var caseSensitive: Boolean = false - - constructor(caseSensitive: Boolean) { - this.caseSensitive = caseSensitive - } - - constructor() { - this.caseSensitive = false - } - - override fun compare(f1: File?, f2: File?): Int { - if (f1 == null || f2 == null) { - return if (f1 == null) { - -1 - } else { - 1 - } - } else { - return if (f1.isDirectory && f2.isFile) { - -1 - } else if (f1.isFile && f2.isDirectory) { - 1 - } else { - val s1 = f1.name - val s2 = f2.name - if (caseSensitive) { - s1.compareTo(s2) - } else { - s1.compareTo(s2, ignoreCase = true) - } - } - } - } - - } - - class SortBySize : Comparator { - - override fun compare(f1: File?, f2: File?): Int { - return if (f1 == null || f2 == null) { - if (f1 == null) { - -1 - } else { - 1 - } - } else { - if (f1.isDirectory && f2.isFile) { - -1 - } else if (f1.isFile && f2.isDirectory) { - 1 - } else { - if (f1.length() < f2.length()) { - -1 - } else { - 1 - } - } - } - } - - } - - class SortByTime : Comparator { - - override fun compare(f1: File?, f2: File?): Int { - return if (f1 == null || f2 == null) { - if (f1 == null) { - -1 - } else { - 1 - } - } else { - if (f1.isDirectory && f2.isFile) { - -1 - } else if (f1.isFile && f2.isDirectory) { - 1 - } else { - if (f1.lastModified() > f2.lastModified()) { - -1 - } else { - 1 - } - } - } - } - - } - -} - diff --git a/app/src/main/java/io/legado/app/utils/FileUtils.kt b/app/src/main/java/io/legado/app/utils/FileUtils.kt index a0b7b910f..eef940a86 100644 --- a/app/src/main/java/io/legado/app/utils/FileUtils.kt +++ b/app/src/main/java/io/legado/app/utils/FileUtils.kt @@ -1,9 +1,15 @@ package io.legado.app.utils import android.os.Environment +import android.webkit.MimeTypeMap +import androidx.annotation.IntDef import io.legado.app.App -import java.io.File -import java.io.IOException +import io.legado.app.ui.filechooser.utils.ConvertUtils +import java.io.* +import java.nio.charset.Charset +import java.text.SimpleDateFormat +import java.util.* +import java.util.regex.Pattern @Suppress("unused") object FileUtils { @@ -97,4 +103,656 @@ object FileUtils { return sdCardDirectory } + const val BY_NAME_ASC = 0 + const val BY_NAME_DESC = 1 + const val BY_TIME_ASC = 2 + const val BY_TIME_DESC = 3 + const val BY_SIZE_ASC = 4 + const val BY_SIZE_DESC = 5 + const val BY_EXTENSION_ASC = 6 + const val BY_EXTENSION_DESC = 7 + + @IntDef(value = [BY_NAME_ASC, BY_NAME_DESC, BY_TIME_ASC, BY_TIME_DESC, BY_SIZE_ASC, BY_SIZE_DESC, BY_EXTENSION_ASC, BY_EXTENSION_DESC]) + @kotlin.annotation.Retention(AnnotationRetention.SOURCE) + annotation class SortType + + /** + * 将目录分隔符统一为平台默认的分隔符,并为目录结尾添加分隔符 + */ + fun separator(path: String): String { + var path1 = path + val separator = File.separator + path1 = path1.replace("\\", separator) + if (!path1.endsWith(separator)) { + path1 += separator + } + return path1 + } + + fun closeSilently(c: Closeable?) { + if (c == null) { + return + } + try { + c.close() + } catch (ignored: IOException) { + } + + } + + /** + * 列出指定目录下的所有子目录 + */ + @JvmOverloads + fun listDirs( + startDirPath: String, + excludeDirs: Array? = null, @SortType sortType: Int = BY_NAME_ASC + ): Array { + var excludeDirs1 = excludeDirs + val dirList = ArrayList() + val startDir = File(startDirPath) + if (!startDir.isDirectory) { + return arrayOfNulls(0) + } + val dirs = startDir.listFiles(FileFilter { f -> + if (f == null) { + return@FileFilter false + } + f.isDirectory + }) + ?: return arrayOfNulls(0) + if (excludeDirs1 == null) { + excludeDirs1 = arrayOf() + } + for (dir in dirs) { + val file = dir.absoluteFile + if (!excludeDirs1.contentDeepToString().contains(file.name)) { + dirList.add(file) + } + } + when (sortType) { + BY_NAME_ASC -> Collections.sort(dirList, SortByName()) + BY_NAME_DESC -> { + Collections.sort(dirList, SortByName()) + dirList.reverse() + } + BY_TIME_ASC -> Collections.sort(dirList, SortByTime()) + BY_TIME_DESC -> { + Collections.sort(dirList, SortByTime()) + dirList.reverse() + } + BY_SIZE_ASC -> Collections.sort(dirList, SortBySize()) + BY_SIZE_DESC -> { + Collections.sort(dirList, SortBySize()) + dirList.reverse() + } + BY_EXTENSION_ASC -> Collections.sort(dirList, SortByExtension()) + BY_EXTENSION_DESC -> { + Collections.sort(dirList, SortByExtension()) + dirList.reverse() + } + } + return dirList.toTypedArray() + } + + /** + * 列出指定目录下的所有子目录及所有文件 + */ + @JvmOverloads + fun listDirsAndFiles( + startDirPath: String, + allowExtensions: Array? = null + ): Array? { + val dirs: Array? + val files: Array? = if (allowExtensions == null) { + listFiles(startDirPath) + } else { + listFiles(startDirPath, allowExtensions) + } + val dirsAndFiles: Array + dirs = listDirs(startDirPath) + if (files == null) { + return null + } + dirsAndFiles = arrayOfNulls(dirs.size + files.size) + System.arraycopy(dirs, 0, dirsAndFiles, 0, dirs.size) + System.arraycopy(files, 0, dirsAndFiles, dirs.size, files.size) + return dirsAndFiles + } + + /** + * 列出指定目录下的所有文件 + */ + @JvmOverloads + fun listFiles( + startDirPath: String, + filterPattern: Pattern? = null, @SortType sortType: Int = BY_NAME_ASC + ): Array { + val fileList = ArrayList() + val f = File(startDirPath) + if (!f.isDirectory) { + return arrayOfNulls(0) + } + val files = f.listFiles(FileFilter { file -> + if (file == null) { + return@FileFilter false + } + if (file.isDirectory) { + return@FileFilter false + } + + filterPattern?.matcher(file.name)?.find() ?: true + }) + ?: return arrayOfNulls(0) + for (file in files) { + fileList.add(file.absoluteFile) + } + when (sortType) { + BY_NAME_ASC -> Collections.sort(fileList, SortByName()) + BY_NAME_DESC -> { + Collections.sort(fileList, SortByName()) + fileList.reverse() + } + BY_TIME_ASC -> Collections.sort(fileList, SortByTime()) + BY_TIME_DESC -> { + Collections.sort(fileList, SortByTime()) + fileList.reverse() + } + BY_SIZE_ASC -> Collections.sort(fileList, SortBySize()) + BY_SIZE_DESC -> { + Collections.sort(fileList, SortBySize()) + fileList.reverse() + } + BY_EXTENSION_ASC -> Collections.sort(fileList, SortByExtension()) + BY_EXTENSION_DESC -> { + Collections.sort(fileList, SortByExtension()) + fileList.reverse() + } + } + return fileList.toTypedArray() + } + + /** + * 列出指定目录下的所有文件 + */ + fun listFiles(startDirPath: String, allowExtensions: Array?): Array? { + val file = File(startDirPath) + return file.listFiles { _, name -> + //返回当前目录所有以某些扩展名结尾的文件 + val extension = getExtension(name) + allowExtensions?.contentDeepToString()?.contains(extension) == true + || allowExtensions == null + } + } + + /** + * 列出指定目录下的所有文件 + */ + fun listFiles(startDirPath: String, allowExtension: String?): Array? { + return if (allowExtension == null) + listFiles(startDirPath, allowExtension = null) + else + listFiles(startDirPath, arrayOf(allowExtension)) + } + + /** + * 判断文件或目录是否存在 + */ + fun exist(path: String): Boolean { + val file = File(path) + return file.exists() + } + + /** + * 删除文件或目录 + */ + @JvmOverloads + fun delete(file: File, deleteRootDir: Boolean = false): Boolean { + var result = false + if (file.isFile) { + //是文件 + result = deleteResolveEBUSY(file) + } else { + //是目录 + val files = file.listFiles() ?: return false + if (files.isEmpty()) { + result = deleteRootDir && deleteResolveEBUSY(file) + } else { + for (f in files) { + delete(f, deleteRootDir) + result = deleteResolveEBUSY(f) + } + } + if (deleteRootDir) { + result = deleteResolveEBUSY(file) + } + } + return result + } + + /** + * bug: open failed: EBUSY (Device or resource busy) + * fix: http://stackoverflow.com/questions/11539657/open-failed-ebusy-device-or-resource-busy + */ + private fun deleteResolveEBUSY(file: File): Boolean { + // Before you delete a Directory or File: rename it! + val to = File(file.absolutePath + System.currentTimeMillis()) + + file.renameTo(to) + return to.delete() + } + + /** + * 删除文件或目录 + */ + @JvmOverloads + fun delete(path: String, deleteRootDir: Boolean = false): Boolean { + val file = File(path) + + return if (file.exists()) { + delete(file, deleteRootDir) + } else false + } + + /** + * 复制文件为另一个文件,或复制某目录下的所有文件及目录到另一个目录下 + */ + fun copy(src: String, tar: String): Boolean { + val srcFile = File(src) + return srcFile.exists() && copy(srcFile, File(tar)) + } + + /** + * 复制文件或目录 + */ + fun copy(src: File, tar: File): Boolean { + try { + if (src.isFile) { + val `is` = FileInputStream(src) + val op = FileOutputStream(tar) + val bis = BufferedInputStream(`is`) + val bos = BufferedOutputStream(op) + val bt = ByteArray(1024 * 8) + while (true) { + val len = bis.read(bt) + if (len == -1) { + break + } else { + bos.write(bt, 0, len) + } + } + bis.close() + bos.close() + } else if (src.isDirectory) { + tar.mkdirs() + src.listFiles()?.forEach { file -> + copy(file.absoluteFile, File(tar.absoluteFile, file.name)) + } + } + return true + } catch (e: Exception) { + return false + } + + } + + /** + * 移动文件或目录 + */ + fun move(src: String, tar: String): Boolean { + return move(File(src), File(tar)) + } + + /** + * 移动文件或目录 + */ + fun move(src: File, tar: File): Boolean { + return rename(src, tar) + } + + /** + * 文件重命名 + */ + fun rename(oldPath: String, newPath: String): Boolean { + return rename(File(oldPath), File(newPath)) + } + + /** + * 文件重命名 + */ + fun rename(src: File, tar: File): Boolean { + return src.renameTo(tar) + } + + /** + * 读取文本文件, 失败将返回空串 + */ + @JvmOverloads + fun readText(filepath: String, charset: String = "utf-8"): String { + try { + val data = readBytes(filepath) + if (data != null) { + return String(data, Charset.forName(charset)).trim { it <= ' ' } + } + } catch (ignored: UnsupportedEncodingException) { + } + + return "" + } + + /** + * 读取文件内容, 失败将返回空串 + */ + fun readBytes(filepath: String): ByteArray? { + var fis: FileInputStream? = null + try { + fis = FileInputStream(filepath) + val baos = ByteArrayOutputStream() + val buffer = ByteArray(1024) + while (true) { + val len = fis.read(buffer, 0, buffer.size) + if (len == -1) { + break + } else { + baos.write(buffer, 0, len) + } + } + val data = baos.toByteArray() + baos.close() + return data + } catch (e: IOException) { + return null + } finally { + closeSilently(fis) + } + } + + /** + * 保存文本内容 + */ + @JvmOverloads + fun writeText(filepath: String, content: String, charset: String = "utf-8"): Boolean { + try { + writeBytes(filepath, content.toByteArray(charset(charset))) + return true + } catch (e: UnsupportedEncodingException) { + return false + } + + } + + /** + * 保存文件内容 + */ + fun writeBytes(filepath: String, data: ByteArray): Boolean { + val file = File(filepath) + var fos: FileOutputStream? = null + return try { + if (!file.exists()) { + file.parentFile?.mkdirs() + file.createNewFile() + } + fos = FileOutputStream(filepath) + fos.write(data) + true + } catch (e: IOException) { + false + } finally { + closeSilently(fos) + } + } + + /** + * 追加文本内容 + */ + fun appendText(path: String, content: String): Boolean { + val file = File(path) + var writer: FileWriter? = null + return try { + if (!file.exists()) { + + file.createNewFile() + } + writer = FileWriter(file, true) + writer.write(content) + true + } catch (e: IOException) { + false + } finally { + closeSilently(writer) + } + } + + /** + * 获取文件大小 + */ + fun getLength(path: String): Long { + val file = File(path) + return if (!file.isFile || !file.exists()) { + 0 + } else file.length() + } + + /** + * 获取文件或网址的名称(包括后缀) + */ + fun getName(pathOrUrl: String?): String { + if (pathOrUrl == null) { + return "" + } + val pos = pathOrUrl.lastIndexOf('/') + return if (0 <= pos) { + pathOrUrl.substring(pos + 1) + } else { + System.currentTimeMillis().toString() + "." + getExtension(pathOrUrl) + } + } + + /** + * 获取文件名(不包括扩展名) + */ + fun getNameExcludeExtension(path: String): String { + try { + var fileName = File(path).name + val lastIndexOf = fileName.lastIndexOf(".") + if (lastIndexOf != -1) { + fileName = fileName.substring(0, lastIndexOf) + } + return fileName + } catch (e: Exception) { + return "" + } + + } + + /** + * 获取格式化后的文件大小 + */ + fun getSize(path: String): String { + val fileSize = getLength(path) + return ConvertUtils.toFileSizeString(fileSize) + } + + /** + * 获取文件后缀,不包括“.” + */ + fun getExtension(pathOrUrl: String): String { + val dotPos = pathOrUrl.lastIndexOf('.') + return if (0 <= dotPos) { + pathOrUrl.substring(dotPos + 1) + } else { + "ext" + } + } + + /** + * 获取文件的MIME类型 + */ + fun getMimeType(pathOrUrl: String): String { + val ext = getExtension(pathOrUrl) + val map = MimeTypeMap.getSingleton() + return map.getMimeTypeFromExtension(ext) ?: "*/*" + } + + /** + * 获取格式化后的文件/目录创建或最后修改时间 + */ + @JvmOverloads + fun getDateTime(path: String, format: String = "yyyy年MM月dd日HH:mm"): String { + val file = File(path) + return getDateTime(file, format) + } + + /** + * 获取格式化后的文件/目录创建或最后修改时间 + */ + fun getDateTime(file: File, format: String): String { + val cal = Calendar.getInstance() + cal.timeInMillis = file.lastModified() + return SimpleDateFormat(format, Locale.PRC).format(cal.time) + } + + /** + * 比较两个文件的最后修改时间 + */ + fun compareLastModified(path1: String, path2: String): Int { + val stamp1 = File(path1).lastModified() + val stamp2 = File(path2).lastModified() + return when { + stamp1 > stamp2 -> { + 1 + } + stamp1 < stamp2 -> { + -1 + } + else -> { + 0 + } + } + } + + /** + * 创建多级别的目录 + */ + fun makeDirs(path: String): Boolean { + return makeDirs(File(path)) + } + + /** + * 创建多级别的目录 + */ + fun makeDirs(file: File): Boolean { + return file.mkdirs() + } + + class SortByExtension : Comparator { + + override fun compare(f1: File?, f2: File?): Int { + return if (f1 == null || f2 == null) { + if (f1 == null) { + -1 + } else { + 1 + } + } else { + if (f1.isDirectory && f2.isFile) { + -1 + } else if (f1.isFile && f2.isDirectory) { + 1 + } else { + f1.name.compareTo(f2.name, ignoreCase = true) + } + } + } + + } + + class SortByName : Comparator { + private var caseSensitive: Boolean = false + + constructor(caseSensitive: Boolean) { + this.caseSensitive = caseSensitive + } + + constructor() { + this.caseSensitive = false + } + + override fun compare(f1: File?, f2: File?): Int { + if (f1 == null || f2 == null) { + return if (f1 == null) { + -1 + } else { + 1 + } + } else { + return if (f1.isDirectory && f2.isFile) { + -1 + } else if (f1.isFile && f2.isDirectory) { + 1 + } else { + val s1 = f1.name + val s2 = f2.name + if (caseSensitive) { + s1.compareTo(s2) + } else { + s1.compareTo(s2, ignoreCase = true) + } + } + } + } + + } + + class SortBySize : Comparator { + + override fun compare(f1: File?, f2: File?): Int { + return if (f1 == null || f2 == null) { + if (f1 == null) { + -1 + } else { + 1 + } + } else { + if (f1.isDirectory && f2.isFile) { + -1 + } else if (f1.isFile && f2.isDirectory) { + 1 + } else { + if (f1.length() < f2.length()) { + -1 + } else { + 1 + } + } + } + } + + } + + class SortByTime : Comparator { + + override fun compare(f1: File?, f2: File?): Int { + return if (f1 == null || f2 == null) { + if (f1 == null) { + -1 + } else { + 1 + } + } else { + if (f1.isDirectory && f2.isFile) { + -1 + } else if (f1.isFile && f2.isDirectory) { + 1 + } else { + if (f1.lastModified() > f2.lastModified()) { + -1 + } else { + 1 + } + } + } + } + + } }