package com.anyun.h264.util; import android.content.Context; import android.os.storage.StorageManager; import android.os.StatFs; import java.io.File; import java.lang.reflect.Array; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.Date; import java.util.List; import java.util.Locale; import timber.log.Timber; public class FileUtil { //获取插入的TFCard目录路径 public static String getStoragePath(Context mContext, boolean is_removale) { if (mContext != null) { StorageManager mStorageManager = (StorageManager) mContext.getSystemService(Context.STORAGE_SERVICE); Class storageVolumeClazz = null; try { storageVolumeClazz = Class.forName("android.os.storage.StorageVolume"); Method getVolumeList = mStorageManager.getClass().getMethod("getVolumeList"); Method getPath = storageVolumeClazz.getMethod("getPath"); Method isRemovable = storageVolumeClazz.getMethod("isRemovable"); Object result = getVolumeList.invoke(mStorageManager); final int length = Array.getLength(result); for (int i = 0; i < length; i++) { Object storageVolumeElement = Array.get(result, i); String path = (String) getPath.invoke(storageVolumeElement); boolean removable = (Boolean) isRemovable.invoke(storageVolumeElement); if (is_removale == removable) { return path; } } } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } return null; } else { return null; } } /** * 清理 TF 卡上的 h264 文件 * 当 h264 文件总大小超过指定阈值或 TF 卡剩余空间小于指定值时,删除日期最早的文件夹 * * @param context Context 对象,用于获取 TF 卡路径和剩余空间 * @param h264RootDir h264 根目录路径,例如 "/sdcard/h264" * @param maxTotalSizeGB 最大总大小(GB),默认 5GB * @param minFreeSpaceGB 最小剩余空间(GB),默认 1GB */ public static void cleanupH264Files(Context context, String h264RootDir, long maxTotalSizeGB, long minFreeSpaceGB) { if (context == null || h264RootDir == null || h264RootDir.trim().isEmpty()) { Timber.w("Context or h264 root directory is null, skip cleanup"); return; } File h264Root = new File(h264RootDir); if (!h264Root.exists() || !h264Root.isDirectory()) { Timber.d("H264 root directory does not exist: %s, skip cleanup", h264RootDir); return; } try { // 获取 TF 卡路径 String tfCardPath = getStoragePath(context, true); if (tfCardPath == null || tfCardPath.trim().isEmpty()) { Timber.w("TF card path not available, skip cleanup"); return; } // 获取 TF 卡剩余空间(GB) long freeSpaceGB = getFreeSpaceGB(tfCardPath); Timber.d("TF card free space: %d GB", freeSpaceGB); // 扫描所有日期文件夹 File[] dateDirs = h264Root.listFiles(File::isDirectory); if (dateDirs == null || dateDirs.length == 0) { Timber.d("No date directories found in h264 root: %s", h264RootDir); return; } // 按日期排序(最早的在前) List dateDirList = new ArrayList<>(); SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd", Locale.CHINA); for (File dateDir : dateDirs) { String dirName = dateDir.getName(); // 只处理符合日期格式的文件夹(yyyyMMdd) if (dirName.length() == 8 && dirName.matches("\\d{8}")) { try { Date date = dateFormat.parse(dirName); long totalSize = calculateH264FilesSize(dateDir); dateDirList.add(new DateDirInfo(dateDir, date, totalSize)); } catch (ParseException e) { Timber.w("Invalid date directory name: %s", dirName); } } } // 按日期排序(最早的在前) Collections.sort(dateDirList, new Comparator() { @Override public int compare(DateDirInfo o1, DateDirInfo o2) { return o1.date.compareTo(o2.date); } }); // 计算所有 h264 文件的总大小(GB) long totalSizeGB = 0; for (DateDirInfo info : dateDirList) { totalSizeGB += info.totalSize / (1024L * 1024L * 1024L); } Timber.d("Total h264 files size: %d GB, Max allowed: %d GB", totalSizeGB, maxTotalSizeGB); Timber.d("TF card free space: %d GB, Min required: %d GB", freeSpaceGB, minFreeSpaceGB); // 检查是否需要清理 boolean needCleanup = (totalSizeGB > maxTotalSizeGB) || (freeSpaceGB < minFreeSpaceGB); if (!needCleanup) { Timber.d("No cleanup needed"); return; } // 删除最早的日期文件夹,直到满足条件 int deletedCount = 0; while (!dateDirList.isEmpty() && ((totalSizeGB > maxTotalSizeGB) || (freeSpaceGB < minFreeSpaceGB))) { DateDirInfo oldestDir = dateDirList.remove(0); Timber.d("Deleting oldest date directory: %s (size: %d GB, date: %s)", oldestDir.dir.getName(), oldestDir.totalSize / (1024L * 1024L * 1024L), dateFormat.format(oldestDir.date)); // 删除文件夹及其所有内容 if (deleteDirectory(oldestDir.dir)) { deletedCount++; totalSizeGB -= oldestDir.totalSize / (1024L * 1024L * 1024L); // 重新获取剩余空间 freeSpaceGB = getFreeSpaceGB(tfCardPath); Timber.d("After deletion - Total size: %d GB, Free space: %d GB", totalSizeGB, freeSpaceGB); } else { Timber.e("Failed to delete directory: %s", oldestDir.dir.getAbsolutePath()); break; // 删除失败,停止清理 } } Timber.i("Cleanup completed. Deleted %d date directories. Final total size: %d GB, Free space: %d GB", deletedCount, totalSizeGB, freeSpaceGB); } catch (Exception e) { Timber.e(e, "Error during h264 files cleanup"); } } /** * 清理 TF 卡上的 h264 文件(使用默认参数:最大5GB,最小剩余空间1GB) */ public static void cleanupH264Files(Context context, String h264RootDir) { cleanupH264Files(context, h264RootDir, 5, 1); } /** * 计算目录下所有 .h264 文件的总大小(字节) */ private static long calculateH264FilesSize(File dir) { long totalSize = 0; File[] files = dir.listFiles(); if (files != null) { for (File file : files) { if (file.isFile() && file.getName().toLowerCase().endsWith(".h264")) { totalSize += file.length(); } } } return totalSize; } /** * 获取指定路径的剩余空间(GB) */ private static long getFreeSpaceGB(String path) { try { StatFs statFs = new StatFs(path); long blockSize = statFs.getBlockSizeLong(); long availableBlocks = statFs.getAvailableBlocksLong(); long freeBytes = availableBlocks * blockSize; return freeBytes / (1024L * 1024L * 1024L); // 转换为 GB } catch (Exception e) { Timber.e(e, "Error getting free space for path: %s", path); return 0; } } /** * 递归删除目录及其所有内容 */ private static boolean deleteDirectory(File dir) { if (dir == null || !dir.exists()) { return true; } if (dir.isDirectory()) { File[] children = dir.listFiles(); if (children != null) { for (File child : children) { if (!deleteDirectory(child)) { return false; } } } } return dir.delete(); } /** * 日期文件夹信息 */ private static class DateDirInfo { File dir; Date date; long totalSize; // 字节 DateDirInfo(File dir, Date date, long totalSize) { this.dir = dir; this.date = date; this.totalSize = totalSize; } } }