文件存储
这是之前用java写的一个文件工具类,区分了内部存储、外部存储,公共目录三个路径,有详细的注释,希望能帮助到大家。
import android.content.ContentResolver;
import android.content.ContentValues;
import android.graphics.Bitmap;
import android.net.Uri;
import android.os.Build;
import android.os.Environment;
import android.provider.MediaStore;
import android.text.TextUtils;
import com.qdgbr.commodlue.api.BaseConstant;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.text.DecimalFormat;
import java.util.Objects;
/**
* @author ThinkAgains
* @date 2019/10/24
* Description: 文件管理类
* APP内部文件(不需要申请权限): (当用户卸载app时,文件会删除)
* 1.cache 不可见
* 2.getExternalFilesDir 包名下
* APP外部文件(需要申请权限): (当用户卸载app时,文件会保留,安卓11已废弃两个方法)
* 1.getExternalStorageDirectory 外置SD卡可以指定访问的文件夹名
* 2.getStoragePath 外置SD卡路径
* APP外部公共目录文件(需要申请权限)
* 1.getExternalStoragePublicDirectory 外置SD卡公共目录(例如:Environment.DIRECTORY_PICTURES,路径固定)
* <p>
* 安卓11上外部存储不允许访问,只能访问公共目录,所以没有提供存储外部文件相关的处理,只提供了获取路径方法
* 注意:外部存储需要判断是否有SD卡权限,Util方法中已经提供
* if (Util.isExternalStorageWritable()) {
* } else {
* return null;
* }
*/
public class FileUtils {
/**
* 这个是fileProvider相关
*/
private final static String FILE_PUBLIC = BaseConstant.APP_PACKAGE;
public static final String FILE_PROVIDER = FILE_PUBLIC + ".selfFileProvider";
/**
* 图片缓存的文件名
*/
public final static String FILE_MOVIES = "Movies";
public final static String FILE_FILE = "File";
public final static String FILE_PICTURES = "Pictures";
public final static String FILE_MUSIC = "Music";
public enum StorageInEnum {
//APP内部cache目录
APP_IN_CACHE,
//APP内部包名目录
APP_IN_PACKAGE
}
public enum FileInEnum {
//文件
FILE,
//图片
PICTURES,
//录音
MUSIC,
//视频
MOVIES
}
/**
* 获取APP内部存储文件
*
* @param fileEnum 存储类型
*/
public static File getAppStorageInFile(StorageInEnum storageEnum, FileInEnum fileEnum) {
if (storageEnum == StorageInEnum.APP_IN_PACKAGE) {
return FileInUtils.getPrivatePackAgeDir(fileEnum);
} else if (storageEnum == StorageInEnum.APP_IN_CACHE) {
return FileInUtils.getPrivateCacheDir(fileEnum);
} else {
return null;
}
}
/**
* 获取外部公共目录
*
* @param fileEnum 存储类型
* @return Uri
*/
public static Uri getPublicOutUri(FileInEnum fileEnum) {
if (Util.isExternalStorageWritable()) {
return getPublicOutUri(fileEnum, "");
} else {
return null;
}
}
/**
* 获取外部公共目录
*
* @param fileEnum 存储类型
* @param fileName 文件名,不传默认为时间戳
* @return Uri
*/
public static Uri getPublicOutUri(FileInEnum fileEnum, String fileName) {
if (Util.isExternalStorageWritable()) {
return FilePublicOutUtils.getPublicDir(fileEnum, fileName);
} else {
return null;
}
}
/**
* 工具方法
*/
public static class Util {
private static final int BYTE = 1024;
//在你申请储存权限并确认储存可用后,你可以储存下列档案:
//公共文件:应该免费提供给其他应用程序和用户的文件。当用户卸载应用程序时,这些文件应该对用户可用。例如,应用程序捕获的照片应该保存为公共文件。
//尽管这些文件在技术上可以由用户和其他应用程序,因为它们在外部存储上,他们不向用户提供价值以外的应用程序。使用这个目录文件,你不想与其他应用程序共享。
//警告:如果用户删除或断开外部存储设备(如SD卡),存储在外部存储上的文件可能不可用。如果你的应用程序的功能依赖于这些文件,你应该把你的文件写到内部存储。
//检查外部存储是否可用来读写
private static boolean isExternalStorageWritable() {
String state = Environment.getExternalStorageState();
if (Environment.MEDIA_MOUNTED.equals(state)) {
return true;
}
return false;
}
//检查外部存储是否至少可读
private static boolean isExternalStorageReadable() {
String state = Environment.getExternalStorageState();
if (Environment.MEDIA_MOUNTED.equals(state) ||
Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) {
return true;
}
return false;
}
/**
* 获取应用数据存储的大小
*/
public static String getFilSizeText() {
//cache目录
//long folderSize = getFolderSize(FileInUtils.getRealCacheDir());
//包名目录
long folderSize = getFolderSize(FileInUtils.getRealPackAgeDir("").getParentFile());
return getFormatSize(folderSize);
}
private static long getFolderSize(File file) {
long size = 0;
try {
File[] fileList = file.listFiles();
if (null != fileList) {
for (int i = 0; i < fileList.length; i++) {
if (fileList[i].isDirectory()) {
size = size + getFolderSize(fileList[i]);
} else {
size = size + fileList[i].length();
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
return size;
}
/**
* 清除缓存(删除应用中所有的文件)
*/
public static void clearCache() {
// 你应该总是删除你的应用程序不再需要的文件。删除文件最直接的方法是调用delete()对文件对象:
//如果文件保存在内部存储器中,也可以通过调用deleteFile()来请求上下文查找和删除文件:
deleteDirWithFile(FileInUtils.getRealPackAgeDir("").getParentFile());
}
private static void deleteDirWithFile(File dir) {
if (dir == null || !fileIsExists(dir) || !dir.isDirectory()) {
return;
}
for (File file : Objects.requireNonNull(dir.listFiles())) {
if (file.isFile()) {
file.delete();
} else if (file.isDirectory()) {
deleteDirWithFile(file);
}
}
dir.delete();
}
/**
* 格式化单位
*/
private static String getFormatSize(long size) {
DecimalFormat df = new DecimalFormat("#.00");
double kiloByte = size / BYTE;
if (kiloByte < 1) {
return size + "B";
}
double megaByte = kiloByte / BYTE;
if (megaByte < 1) {
return df.format(kiloByte) + "KB";
}
double gigaByte = megaByte / BYTE;
if (gigaByte < 1) {
return df.format(megaByte) + "MB";
}
double teraBytes = gigaByte / BYTE;
if (teraBytes < 1) {
return df.format(gigaByte) + "GB";
}
return df.format(teraBytes) + "TB";
}
/**
* 文件是否存在
*
* @param file
* @return true false
*/
public static boolean fileIsExists(File file) {
return file.exists();
/* if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
//检查公共目录是否存在
AssetFileDescriptor afd = null;
ContentResolver cr = ContextUtils.getApp().getContentResolver();
try {
Uri uri = Uri.parse(file.getAbsolutePath());
afd = cr.openAssetFileDescriptor(uri, "r");
if (afd == null) {
return false;
}
} catch (FileNotFoundException e) {
return false;
} finally {
try {
if (null != afd) {
afd.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
return true;
} else {
return file.exists();
}*/
}
}
/**
* 将一个文件保存到APP内部相关目录
* 私有文件
*/
private static class FileInUtils {
//APP内部存储缓存文件(不可见的那部分存储)
public static File getRealCacheDir() {
return ContextUtils.Companion.getApp().getCacheDir();
}
//APP内部存储包名files目录文件
public static File getRealPackAgeDir(String fileDirType) {
return ContextUtils.Companion.getApp().getExternalFilesDir(fileDirType);
}
//效果同 getRealPackAgeDir(fileDirType)
public static File getFilesDir() {
return ContextUtils.Companion.getApp().getFilesDir();
}
//获取APP内部存储文件系统上的目录(文件名为:cache,该文件不可见)
public static File getPrivateCacheDir(FileInEnum fileEnum) {
//getCacheDir()返回一个File对象,该对象表示文件系统上与应用程序唯一关联的缓存目录。该目录用于临时文件,应该定期清理。
//如果磁盘空间不足,系统可能会删除那里的文件,所以请确保在读取缓存文件之前检查它们的存在性
//Uri.parse(url).getLastPathSegment():从URL对象中提取文件名,并在应用程序的内部缓存目录中创建一个同名文件
//File.createTempFile(FileUtils.FILE_NAME, null, context.getCacheDir());创建临时文件
String fileDirName;
if (fileEnum == FileInEnum.MOVIES) {
fileDirName = FILE_MOVIES;
} else if (fileEnum == FileInEnum.PICTURES) {
fileDirName = FILE_PICTURES;
} else if (fileEnum == FileInEnum.MUSIC) {
fileDirName = FILE_MUSIC;
} else {
fileDirName = FILE_FILE;
}
File file = new File(getRealCacheDir(), fileDirName);
if (!Util.fileIsExists(file)) {
file.mkdirs();
}
return file;
}
//获取APP内部存储文件系统上的目录(文件名一般默认为:files,该文件可见)
public static File getPrivatePackAgeDir(FileInEnum fileEnum) {
//mContext.getFilesDir()(与缓存目录在同一目录下,同一级别,文件名为:files):
//如果希望将应用程序私有的文件保存在外部存储器上,可以通过调用getExternalFilesDir()获取特定于应用程序的目录
//并传递一个表示目录类型的名称。以这种方式创建的每个目录都被添加到一个父目录中,
//该目录封装了应用程序的所有外部存储文件,当用户卸载应用程序时,系统会清除这些文件。
//如果预定义的子目录名都不适合您的文件,那么可以调用getExternalFilesDir()并传递null或者“”。这将返回应用程序在外部存储上的私有目录的根目录。
String fileDirName;
if (fileEnum == FileInEnum.MOVIES) {
fileDirName = FILE_MOVIES;
} else if (fileEnum == FileInEnum.PICTURES) {
fileDirName = FILE_PICTURES;
} else if (fileEnum == FileInEnum.MUSIC) {
fileDirName = FILE_MUSIC;
} else {
fileDirName = ""; //默认为files文件
}
File externalFilesDir = getRealPackAgeDir(fileDirName);
if (TextUtils.isEmpty(fileDirName)) {
File file = new File(externalFilesDir, FILE_FILE);
if (!Util.fileIsExists(file)) {
file.mkdirs();
}
return file;
} else if (!Util.fileIsExists(externalFilesDir)) {
externalFilesDir.mkdirs();
}
return externalFilesDir;
}
}
/**
* 将一个文件保存到APP外部公共相关目录
*/
private static class FilePublicOutUtils {
//保存到公共目录
public static Uri getPublicDir(FileInEnum fileEnum, String fileName) {
//如果你想将文件保存在外部存储器,其他应用程序应该可以访问,使用以下api之一:
//如果要保存照片、音频文件或视频剪辑,请使用MediaStore API。
//如果要保存任何其他文件,比如PDF文档,请使用ACTION_CREATE_DOCUMENT intent,它是存储访问框架的一部分。
//如果您想要在媒体扫描器中隐藏文件,请在特定于应用程序的目录中包含一个名为.nomedia的空文件(请注意文件名中的点前缀)。
// 这可以防止媒体扫描器读取您的媒体文件并通过MediaStore API将它们提供给其他应用程序。
//Primary directory sdf not allowed for content://media/external/images/media; allowed directories are [DCIM, Pictures]
ContentValues contentValues = new ContentValues();
ContentResolver resolver = ContextUtils.Companion.getApp().getContentResolver();
long time = System.currentTimeMillis() / 1000;
String name = TextUtils.isEmpty(fileName) ? String.valueOf(System.currentTimeMillis()) : fileName;
if (fileEnum == FileInEnum.MOVIES) {
contentValues.put(MediaStore.Video.Media.MIME_TYPE, "video/mp4");
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
contentValues.put(MediaStore.Video.Media.IS_PENDING, 0);
contentValues.put(MediaStore.Video.Media.RELATIVE_PATH, FILE_MOVIES + File.separator + FILE_PUBLIC);
} else {
contentValues.put(MediaStore.Video.VideoColumns.IS_PRIVATE, 0);
}
contentValues.put(MediaStore.Video.Media.DISPLAY_NAME, name);
contentValues.put(MediaStore.Video.Media.DATE_ADDED, time);
return resolver.insert(MediaStore.Video.Media.EXTERNAL_CONTENT_URI, contentValues);
} else if (fileEnum == FileInEnum.PICTURES) {
contentValues.put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg");
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
contentValues.put(MediaStore.Images.Media.IS_PENDING, 0);
contentValues.put(MediaStore.Images.Media.RELATIVE_PATH, FILE_PICTURES + File.separator + FILE_PUBLIC);
} else {
contentValues.put(MediaStore.Images.ImageColumns.IS_PRIVATE, 0);
}
contentValues.put(MediaStore.Images.Media.DISPLAY_NAME, name);
contentValues.put(MediaStore.Images.Media.DATE_ADDED, time);
return resolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues);
} else if (fileEnum == FileInEnum.MUSIC) {
contentValues.put(MediaStore.Audio.Media.MIME_TYPE, "audio/mp3");
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
contentValues.put(MediaStore.Audio.Media.IS_PENDING, 0);
contentValues.put(MediaStore.Audio.Media.RELATIVE_PATH, FILE_MUSIC + File.separator + FILE_PUBLIC);
}
contentValues.put(MediaStore.Audio.Media.DISPLAY_NAME, name);
contentValues.put(MediaStore.Audio.Media.DATE_ADDED, time);
return resolver.insert(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, contentValues);
} else {
throw new NoSuchMethodError("No corresponding type was found");
}
}
}
/**
* 保存文件
*/
public static class FileWriteUtils {
//将图片bitmap保存到本地
public static boolean writeBitmapFromUri(Uri uri, Bitmap bitmap) {
if (null == uri || null == bitmap) {
return false;
}
OutputStream outputStream;
try {
outputStream = ContextUtils.Companion.getApp().getContentResolver().openOutputStream(uri);
if (null != outputStream) {
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, outputStream);
outputStream.close();
}
} catch (Exception e) {
e.printStackTrace();
return false;
}
return true;
}
//将一个InputStream里面的数据写入文件中
public static boolean writeFileFromInput(File file, InputStream input) {
OutputStream output = null;
try {
output = new FileOutputStream(file);
byte[] buffer = new byte[1024];
int offset;
while ((offset = input.read(buffer)) != -1) {
output.write(buffer, 0, offset);
}
output.flush();
} catch (Exception e) {
e.printStackTrace();
} finally {
if (null != output) {
try {
output.close();
return true;
} catch (IOException e) {
e.printStackTrace();
}
}
}
return false;
}
public static void writeInFile(File file, String fileContents) {
FileOutputStream outputStream;
try {
outputStream = new FileOutputStream(file);
outputStream.write(fileContents.getBytes());
outputStream.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
使用方法如:
//glide缓存配置
override fun applyOptions(
context: Context,
builder: GlideBuilder
) {
val diskCacheSizeBytes = 1024 * 1024 * 100
val appStorageFile = getAppStorageInFile(
FileUtils.StorageInEnum.APP_IN_PACKAGE,
FileUtils.FileInEnum.FILE
)
if (null != appStorageFile) {
builder.setDiskCache(
DiskLruCacheFactory(
appStorageFile.absolutePath,
"GlideCache",
diskCacheSizeBytes.toLong()
)
)
}
}
/**
* 保存图片到公共文件的调用方法
*/
fun saveImage(view: View) {
val bitmap = QMUIDrawableHelper.createBitmapFromView(view)
val publicUri = FileUtils.getPublicOutUri(FileUtils.FileInEnum.PICTURES)
publicUri?.apply {
val isWriteComplete = FileUtils.FileWriteUtils.writeBitmapFromUri(this, bitmap)
if (isWriteComplete) {
showToast("保存成功!")
} else {
showToast("保存失败,请重新尝试!")
}
}
}
/**
*获取缓存大小
*/
val cacheSize = FileUtils.Util.getFilSizeText()
tvText.text = cacheSize
/**
*清除缓存
*/
FileUtils.Util.clearCache()
tvText.text = cacheSize//或直接设置为OB
showTipDialog("清理干净了")