一、文件读写权限
(1)权限
读写 外部存储 中的文件时,需要 读写权限;
读写 “ 内部存储 ” 中的文件,“ 不需要 ” 读写权限
- 向外部存储 写入 文件 【write】
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
- 从外部存储 读取 文件 【read】
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
(2)提醒
从 API 等级 19 开始,读取函数 getExternalFilesDir
、getExternalCacheDir
得到的外部存储路径中的文件时,不再需要读写权限
二、内部存储和外部存储的区别
File
对象适合按照从开始到结束的顺序不跳过地读取或写入大量数据。
例如,它适合于图片文件或通过网络交换的任何内容
(1)内部存储的特点
- 始终可用
- 只有本应用可以访问到此保存的文件
- 用户卸载应用时,系统会从内部存储中移除应用的所有文件
适用场景:确保用户或其他应用均无法访问您的文件时,内部存储是最佳选择
(2)外部存储的特点
- 并非始终可用,因为用户可采用 USB 存储设备的形式装载外部存储,并在某些情况下会从设备中将其移除(不过目前大部分的智能手机都不再支持可拆卸式的 SD 卡形式了)
- 全局可读的,因此此处保存的文件可能不受您控制地被读取
- 当用户卸载您的应用时,只有在您通过 getExternalFilesDir() 将您的应用的文件保存在目录中时,系统才会从此处移除您的应用的文件
适用场景:对于无需访问限制以及您希望与其他应用共享或允许用户使用计算机访问的文件,外部存储是最佳位置
(3)提示
提示:尽管应用默认安装在内部存储中,但您可在您的清单文件中指定 android:installLocation 属性,这样您的应用便可安装在在外部存储中。
当 APK 非常大且它们的外部存储空间大于内部存储时,用户更青睐这个选择。
当文件不想让其它应用访问时,可以选择内部存储;
当文件想要长期保存时,可以选择外部存储(不过外部存储中也有私有位置,即存储在该路径下的文件会随 apk 的卸载而自动移除)
三、访问外部存储的相应函数
(1)事前工作:测试是否允许文件读写【针对外部存储】
在进行文件操作之前最好先测试一下外部存储的状态,使用函数
getExternalStorageState
,当返回结果为MEDIA_MOUNTED
,表示可以继续下面的文件读写
/* Checks if external storage is available for read and write */
/* 检查外部存储器是否可用于读和写 */
public boolean isExternalStorageWritable(){
String state = Environment.getExternalStorageState();
if(Environment.MEDIA_MOUNTED.equals(state)){
return true;
}
Log.e(TAG, "isExternalStorageWritable: " + state);
return false;
}
正如上面所言,外部存储也分为两大块,一块是完全的公共区域,在里面的文件需要显式的移除;另一块就是相对私有的区域,虽然在该路径下也可以被其他应用访问,不过该路径下的文件会随应用的卸载而自动移除
(2)访问公共区域
- 直接返回外部存储主目录
Environment.getExternalStorageDirectory
代码:
String mainDir = Environment.getExternalStorageDirectory().getAbsolutePath();
结果:
/storage/emulated/0
- 访问专用目录
Android 在外部存储中将一些相同文件类型的文件放置在了同一个目录下,比如图片,视频,文档,音乐等。
如果想要得到专用目录,可以使用函数Environment.getExternalStoragePublicDirectory
标准的存储目录如下
public static final String[] STANDARD_DIRECTORIES = {
DIRECTORY_MUSIC, //目录_音乐
DIRECTORY_PODCASTS,//目录_播客 博客 精采广播
DIRECTORY_RINGTONES,//目录_来电铃声 手机铃声 随机铃声
DIRECTORY_ALARMS,//目录_警告 闹钟铃声 闹钟
DIRECTORY_NOTIFICATIONS,//目录_通知
DIRECTORY_PICTURES,//目录_相关图片
DIRECTORY_MOVIES,//目录_电影
DIRECTORY_DOWNLOADS,//目录_下载
DIRECTORY_DCIM,//目录_数据中心基础设施管理
DIRECTORY_DOCUMENTS//目录_单据 证件 文件
};
代码:
String dcimDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM)
.getAbsolutePath();
String pictureDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES).getAbsoultPath();
结果:
/storage/emulated/0/Pictures
/storage/emulated/0/DCIM
- 访问私有区域
使用函数
getExternalFilesDir(String)
- 参数为空表示返回外部存储上应用的专用目录的 “ 根 ” 目录
String fileDir = getExternalFilesDir(null).getAbsolutePath();
结果:
/storage/emulated/0/Android/data/com.penglaoxi.filedemo/files
- 创建标准目录
String movieDir = getExternalFilesDir(Environment.DIRECTORY_MOVIES).getAbsolutePath();
String documentDir = getExternalFilesDir(Environment.DIRECTORY_DOCUMENTS).getAbsolutePath();
结果:
/storage/emulated/0/Android/data/com.penglaoxi.filedemo/files/Movies
/storage/emulated/0/Android/data/com.penglaoxi.filedemo/Documents
四、向外部存储读写文件
使用
java.io
类即可
(1)向外部存储【写入】字符串、图片
- 写入字符串
private void writeStringToFile(String str){
//判断外部存储是否允许写入操作
if(!isExternalStorageWritable()){
//如果不允许,退出方法
return;
}
//如果允许写入操作,执行以下操作
File dir = getExternalFilesDir(Environment.DIRECTORY_DOCUMENTS);
//日志输出路径
Log.e("TAG","writeStringToFile: dir = " + dir.getAbsolutePath());
//如果目标不存在
if(!dir.exists()){
//创建多级目录
dir.mkdirs();
}
//创建一个txt的文件保存字符串,"str"为自定义的文件名称
File file = new File(dir,"str.txt");
//如果文件存在
if(file.exists()){
//如果存在命名相同的文件,就删除
file.delete();
}
FileOutputStream fos = null;
BufferedOutputStream bos = null;
try{
//创建文件
file.createNewFile();
//文件输出流(file)
fos = new FileOutputStream(file);
//缓冲输出流(fos)
bos = new BufferedOutputStream(fos);
bos.write(str.getBytes());
//关闭缓存流
bos.close();
//关闭输入流
fos.close();
} catch (IOException e) {
e.printStackTrace();
}finally{
try{
if(bos != null){
bos.close();
}
if(fos != null){
fos.close();
}
}catch (IOException e) {
e.printStackTrace();
}
}
}
/* Checks if external storage is available for read and write */
public boolean isExternalStorageWritable() {
String state = Environment.getExternalStorageState();
if (Environment.MEDIA_MOUNTED.equals(state)) {
return true;
}
Log.e(TAG, "isExternalStorageWritable: " + state);
return false;
}
- 写入图片
private void writeImageToFile(Bitmap bitmap) {
//判断外部存储是否允许写入操作
if (!isExternalStorageWritable()) {
return;
}
//获取外部文件目录
File dir = getExternalFilesDir(Environment.DIRECTORY_PICTURES);
//输出相片路径【外部文件目录】
Log.e(TAG, "writeImageToFile: dir = " + dir.getAbsolutePath());
if(!dir.exists()){
//创建多级目录
dir.mkdirs();
}
File file = new File(dir,"penglaoxi.png")
//如果文件存在一致
if(file.existe()){
//删除目标文件
file.delete();
}
FileOutputStream fos = null;
BufferedOutputStream bos = null;
try{
//创建文件
file.createNewFile();
fos = new FileOutputStream(file);
bos = new BufferedOutputStream(fos);
//压缩图片
bitmap.compress(Bitmap.CompressFormat.PNG, 100, bos);
//刷新
bos.flush();
bos.close();
fos.close();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (bos != null) {
bos.close();
}
if (fos != null) {
fos.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
writeStringToFile(String)
函数用于将字符串写入本地,选择本地路径为外部存储私有路径中的文档专用目录
/storage/emulated/0/Android/data/com.penglaoxi.filedemo/files/Documents
writeImageToFile(Bitmap)
函数用于将图片写入本地,选择本地路径为外部存储私有路径中的图片专用目录
/storage/emulated/0/Android/data/com.penglaoxi.filedemo/files/Pictures
进行写入操作前,先判断外部存储是否可用;然后判断文件目录是否存在,不存在则新建;最后写入文件
(2)向外部存储【读取】文本文件,图片文件
读取外部存储中的文本文件 - readStringFromFile
:
private String readStringFromFile() {
//判断是否允许读取操作
if (!isExternalStorageWritable()) {
return null;
}
//获取外部存储目录
File dir = getExternalFilesDir(Environment.DIRECTORY_DOCUMENTS);
Log.e(TAG,"readStringFromFile: dir = " + dir.getAbsolutePath());
if(!dir.exists()){
return null;
}
File file = new File(dir,"str.txt");
if(!file.exists()){
return null;
}
//文件输入流
FileInputStream fis = null;
//字符流 转换流 输入流
InputStreamReader isr = null;
//缓冲区读取 缓冲流 输入流
BufferedReader br = null;
//字符串缓冲
StringBuffer sb = new StringBuffer();
try{
fis = new FileInputStream(file);
isr = new InputStreamReader(fis);
br = new BufferedReader(isr);
String line;
//字符串缓冲 写入缓冲流 br (逐行读取)
sb.append(br.readLine());
//如果文件的字符串不为空
while((line = br.readLine()) != null){
//循环写入每一行的数据
sb.append("\n" + line);
}
br.close();
isr.close();
fis.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (br != null) {
br.close();
}
if (isr != null) {
isr.close();
}
if (fis != null) {
fis.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
//返回读取的文字
return sb.toString();
}
读取外部存储中的图片文件 - readBitmapFromFile
:
private Bitmap readBitmapFromFile() {
if (!isExternalStorageWritable()) {
return null;
}
File dir = getExternalFilesDir(Environment.DIRECTORY_PICTURES);
Log.e(TAG, "readBitmapFromFile: dir = " + dir.getAbsolutePath());
if(!dir.existe()){
return null;
}
File file = new File(dir,"penglaoxi.png");
if(!file.exists()){
return null;
}
FileInputStream fis = null;
BufferedInputStream bis = null;
Bitmap bitmap = null;
try{
fis = new FileInputStream(file);
bis = new BufferedInputStream(fis);
//位图压缩
bitmap = BitmapFactory.decodeStream(bis);
bis.close();
fis.close();
}catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (bis != null) {
bis.close();
}
if (fis != null) {
fis.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
return bitmap;
}