Android 文件读写以及assets操作

时间:2022-09-04 19:19:15

Android 文件读写以及assets操作

参考:

保存文件https://developer.android.com/training/basics/data-storage/files.html
存储选项https://developer.android.com/guide/topics/data/data-storage.html
android getExternalStorageDirectory() 和 getExternalStorageState()http://blog.csdn.net/u012005313/article/details/49556487
Android 将asserts文件夹内文件写入SD卡中http://blog.csdn.net/u012005313/article/details/49358911


今天有任务,需要从 assets 文件夹中读取文件到外部私有存储路径上。本以为很简单的事情,没想到费了好久才完成,之前关于这方面的知识都忘记了,重新查阅资料,较为详细的记录下如何进行文件读写,以及如何从 assets 中读取文件


主要内容:

  1. 文件读写需要的权限
  2. 内部存储和外部存储的区别
  3. 访问外部存储的相应函数
  4. 如何向外部存储读写文件
  5. 如何读取 assets 文件夹中的文件
  6. 文件删除
  7. 相关代码

文件读写需要的权限

读写 外部存储 中的文件时,需要 读写权限;读取 内部存储 中的文件 不需要 读写权限

向外部存储写入文件需要 WRITE_EXTERNAL_STORAGE 权限

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.zj.filedemo">

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

...

</manifest>

从外部存储读取文件需要 READ_EXTERNAL_STORAGE 权限

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.zj.filedemo">

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

...

</manifest>

API 等级 19 开始,读取函数 getExternalFilesDirgetExternalCacheDir 得到的外部存储路径中的文件时,不再需要读写权限


内部存储和外部存储的区别

File 对象适合按照从开始到结束的顺序不跳过地读取或写入大量数据。 例如,它适合于图片文件或通过网络交换的任何内容

内部存储特点:

  1. 始终可用
  2. 只有本应用可以访问此处保存的文件
  3. 当用户卸载应用时,系统会从内部存储中移除应用的所有文件

适用场景:确保用户或其他应用均无法访问您的文件时,内部存储是最佳选择

外部存储特点:

  1. 它并非始终可用,因为用户可采用 USB 存储设备的形式装载外部存储,并在某些情况下会从设备中将其移除(不过目前大部分的智能手机都不再支持可拆卸式的 SD 卡形式了
  2. 它是全局可读的,因此此处保存的文件可能不受您控制地被读取
  3. 当用户卸载您的应用时,只有在您通过 getExternalFilesDir() 将您的应用的文件保存在目录中时,系统才会从此处移除您的应用的文件

适用场景:对于无需访问限制以及您希望与其他应用共享或允许用户使用计算机访问的文件,外部存储是最佳位置

提示:尽管应用默认安装在内部存储中,但您可在您的清单文件中指定 android:installLocation 属性,这样您的应用便可安装在在外部存储中。
当 APK 非常大且它们的外部存储空间大于内部存储时,用户更青睐这个选择。

如需了解详细信息,请参阅应用 安装位置

简单的说,就是当文件不想让其它应用访问时,可以选择内部存储;当文件想要长期保存时,可以选择外部存储(不过外部存储中也有私有位置,即存储在该路径下的文件会随 apk 的卸载而自动移除


访问外部存储的相应函数

经常用到的就是对于外部存储的操作,所以接下来的文件操作都是针对外部存储的

首先,在进行文件操作之前最好先测试一下外部存储的状态,使用函数
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;
}

正如上面所言,外部存储也分为两大块,一块是完全的公共区域,在里面的文件需要显式的移除;另一块就是相对私有的区域,虽然在该路径下也可以被其他应用访问,不过该路径下的文件会随应用的卸载而自动移除

访问公共区域

  1. 可以直接返回外部存储主目录:
    Environment.getExternalStorageDirectory

    代码如下:

    String mainDir = Environment.getExternalStorageDirectory().getAbsolutePath();

    结果:

    /storage/emulated/0
  2. 访问专用目录: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).getAbsolutePath();

    结果:

    /storage/emulated/0/Pictures  
    /storage/emulated/0/DCIM

访问私有区域

使用函数
getExternalFilesDir(String)

  1. 参数为空表示返回外部存储上应用的专用目录的根目录

    代码:

    String fileDir = getExternalFilesDir(null).getAbsolutePath();

    结果:

    /storage/emulated/0/Android/data/com.zj.filedemo/files
  2. 也可以创建标准目录

    代码:

    String movieDir = getExternalFilesDir(Environment.DIRECTORY_MOVIES).getAbsolutePath();
    String documentDir = getExternalFilesDir(Environment.DIRECTORY_DOCUMENTS).getAbsolutePath();

    结果:

    /storage/emulated/0/Android/data/com.zj.filedemo/files/Movies
    /storage/emulated/0/Android/data/com.zj.filedemo/files/Documents

如何向外部存储读写文件

使用 java.io 类即可

向外部存储写入字符串、图片

直接贴代码,Main2Activity.java 如下:

public class Main2Activity extends AppCompatActivity {
private static final String TAG = Main2Activity.class.getSimpleName();

private TextView textView;
private ImageView imageView;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main2);

textView = (TextView) findViewById(R.id.tv_string);
imageView = (ImageView) findViewById(R.id.iv_img);

String str = "Hello World";

Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher);

textView.setText(str);
imageView.setImageBitmap(bitmap);

writeStringToFile(str);
writeImageToFile(bitmap);
}

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();
}

File file = new File(dir, "str.txt");
if (file.exists()) {
file.delete();
}

FileOutputStream fos = null;
BufferedOutputStream bos = null;
try {
file.createNewFile();

fos = new FileOutputStream(file);
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();
}
}
}

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, "ic.png");
if (file.exists()) {
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();
}
}
}

/* 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;
}
}

writeStringToFile(String) 函数用于将字符串写入本地,选择本地路径为外部存储私有路径中的文档专用目录

/storage/emulated/0/Android/data/com.zj.filedemo/files/Documents

writeImageToFile(Bitmap) 函数用于将图片写入本地,选择本地路径为外部存储私有路径中的图片专用目录

/storage/emulated/0/Android/data/com.zj.filedemo/files/Pictures

进行写入操作前,先判断外部存储是否可用;然后判断文件目录是否存在,不存在则新建;最后写入文件

读取外部存储中的文本文件,图片文件

读取外部存储中的文本文件 - 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;
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.exists()) {
return null;
}

File file = new File(dir, "ic.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;
}

如何读取 assets 文件夹中的文件

参考:

AssetManager

assets 文件夹用于存储应用需要的文件,在安装后可直接从其中读取使用或者写入本地存储中

Android Studio 默认不建立该文件夹,可以手动新建 : app -> src -> main -> assets

或者,右键 main -> New -> Folder -> Assets Folder

Android 文件读写以及assets操作

AssetManager 对象可以直接访问该文件夹:

获取方法:

AssetManager assetManager = this.getApplicationContext().getAssets();

使用函数 open 可以打开 assets 文件夹中对象,返回一个 InputStream 对象:

open

获取方法:

inputStream = assetManager.open("label.txt");

读取assets中文本文件

   private String readStringFromAssets() {
AssetManager assetManager = this.getApplicationContext().getAssets();

InputStream inputStream = null;
InputStreamReader isr = null;
BufferedReader br = null;

StringBuffer sb = new StringBuffer();
try {
inputStream = assetManager.open("label.txt");
isr = new InputStreamReader(inputStream);
br = new BufferedReader(isr);

sb.append(br.readLine());
String line = null;
while ((line = br.readLine()) != null) {
sb.append("\n" + line);
}

br.close();
isr.close();
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (br != null) {
br.close();
}
if (isr != null) {
isr.close();
}
if (inputStream != null) {
inputStream.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}

return sb.toString();
}

读取assets中文件,并写入本地

private boolean readDataFromAssets() {
if (!isExternalStorageWritable()) {
return false;
}

InputStream inputStream = null;
FileOutputStream fos = null;
BufferedOutputStream bos = null;

File dir = getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS);
Log.e(TAG, "readDataFromAssets: dir = " + dir.getAbsolutePath());

if (!dir.exists()) {
dir.mkdirs();
}

try {
inputStream = getAssets().open("app-debug.apk");

File file = new File(dir, "app-debug.apk");
if (file.exists()) {
file.delete();
}
file.createNewFile();

fos = new FileOutputStream(file);
bos = new BufferedOutputStream(fos);

byte[] bytes = new byte[1024];
while (inputStream.read(bytes) > 0) {
bos.write(bytes, 0, bytes.length);
}

inputStream.close();
bos.close();
fos.close();
} catch (IOException e) {
e.printStackTrace();
return false;
} finally {
try {
if (inputStream != null) {
inputStream.close();
}
if (bos != null) {
bos.close();
}
if (fos != null) {
fos.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}

return true;
}

文件删除

当应用卸载时,Android 系统自动删除以下项目:

  • 保存在内部存储中的所有文件
  • 使用 getExternalFilesDir() 保存在外部存储中的所有文件

手动删除,即打开该文件,调用 delete 方法


相关代码

https://git.oschina.net/zjZSTU/filedemo.git