Android文件存储和文件提供程序

介绍 (Introduction)

In the last article we have gone through the basics of file storage in android and how an app is provided with few API’s to save its file in its respective directories. Please take a look at Part 1 of this series so that you don’t miss the track. In today’s article we shall experiment what we have learnt in the previous article.

在上一篇文章中,我们介绍了android中文件存储的基础知识,以及如何为应用程序提供很少的API来将其文件保存在各自的目录中。 请看一下本系列的第1部分 ,以确保您不会错过这条路。 在今天的文章中,我们将尝试在上一篇文章中学到的东西。

实验清单 (List of Experiments)

  1. Saving file in app files directory

    将文件保存在应用程序文件目录中
  2. Saving file in app’s external files directory

    将文件保存在应用程序的外部文件目录中
  3. File Provider API — Securely share your app files to external system via content URI.

    文件提供商API-通过内容URI安全地将您的应用程序文件共享到外部系统。

实验1 (Experiment 1)

In both first and second experiments we don’t have to declare any run permission because app access only its own storage allocated by the system. Let’s create a function which will save file in directory as follows,

在第一个和第二个实验中,我们不必声明任何运行许可,因为应用程序只能访问系统分配的自己的存储。 让我们创建一个将文件保存在目录中的函数,如下所示:

fun saveFileInAppDirectory() {
val file = FileUtils.createFileInStorage(appContext,
"test.jpeg"
)
Timber.d("file path %s", file!!.absolutePath)
if (!file.exists()) {
file.createNewFile()
}
val assetManager = appContext.assets
val inputStream: InputStream
val bitmap: Bitmap
try {
inputStream = assetManager.open(SAMPLE_FILE_NAME)
bitmap = BitmapFactory.decodeStream(inputStream)
saveBitmap(bitmap, file)
} catch (e: Exception) {
Timber.e(e)
}
}

Our use case is to read a file from the assets folder and save it in the app files directory. We will also create FileUtils class and have all our files related utility functions. Copy both the functions mentioned below to FileUtils.kt file.

我们的用例是从资产文件夹中读取文件并将其保存在应用程序文件目录中。 我们还将创建FileUtils类,并使我们所有与文件相关的实用程序功能。 将下面提到的两个功能复制到FileUtils.kt文件。

fun createFileInStorage(context: Context, fileName: String): File? {
val timeStamp: String = System.currentTimeMillis().toString() + Constants.JPEG
val
name = if (fileName.isBlank()) timeStamp else fileName
return File(getAppFilesDir(context), name)
}fun createFileInExternalStorage(context: Context, fileName: String): File? {
val timeStamp: String = System.currentTimeMillis().toString() + Constants.JPEG
val
name = if (fileName.isBlank()) timeStamp else fileName
return File(getAppExternalFilesDir(context), name)
}private fun getAppFilesDir(context: Context): File? {
val file = context.filesDir
if (file != null && !file.exists()) {
file.mkdirs()
}
return file
}private fun getAppExternalFilesDir(context: Context): File? {
val file = context.getExternalFilesDir(Environment.DIRECTORY_PICTURES)
if (file != null && !file.exists()) {
file.mkdirs()
}
return file
}

When you call saveFileInAppDirectory() function, it will the save the file in the app specific directory and it cannot by accessed by the other apps or file manager because it is saved in our app’s internal storage directory. In my case after running this function in the Pixel 3 API 28 emulator, the path where the file stored was,

当您调用saveFileInAppDirectory()函数时,它将文件保存在应用程序特定目录中,并且其他应用程序或文件管理器无法访问该文件,因为它已保存在我们应用程序的内部存储目录中。 就我而言,在Pixel 3 API 28仿真器中运行此功能后,文件存储的路径为:

/data/user/0/com.example.android.boilerplate/files/test.jpeg

实验2 (Experiment 2)

Likewise we can save the file in app’s external files directory as well. To do so call the below given function.

同样,我们也可以将文件保存在应用程序的外部文件目录中。 为此,请调用以下给定的函数。

fun saveFileInAppExternalDirectory() {
val file = FileUtils.createFileInExternalStorage(appContext,
"test.jpeg"
)
Timber.d("file path %s", file!!.absolutePath)
if (!file.exists()) {
file.createNewFile()
}
val assetManager = appContext.assets
val inputStream: InputStream
val bitmap: Bitmap
try {
inputStream = assetManager.open(SAMPLE_FILE_NAME)
bitmap = BitmapFactory.decodeStream(inputStream)
saveBitmap(bitmap, file)
} catch (e: Exception) {
Timber.e(e)
}
}

Note: SAMPLE_FILE_NAME refers to name of the file in assets folder.

注意SAMPLE_FILE_NAME是指资产文件夹中文件的名称。

So here the file saved path while running Pixel 3 API 28 Emulator is,

因此,在这里运行Pixel 3 API 28 Emulator时文件保存的路径是:

/storage/emulated/0/Android/data/com.example.android.boilerplate/files/Pictures/test.jpeg

In this case, you can check the saved file in the emulator via file manager app. Open file manager app and in the top right corner of the settings click on show internal storage. Now go into internal storage folder then navigate to Android/data/<your_app_package_name>/files

在这种情况下,您可以通过文件管理器应用检查在模拟器中保存的文件。 打开文件管理器应用程序,然后在设置的右上角单击显示内部存储。 现在进入内部存储文件夹,然后导航到Android / data / <your_app_package_name> / files

The same approach can be followed for storing the files in app specific cache directories also.

也可以采用相同的方法将文件存储在应用程序特定的缓存目录中。

文件提供者 (File Provider)

In few cases you may want to share the files saved in your app’s specific directories to the other apps. In such scenario you can share it as a content URI. Android File Provider exposes few API’s in-order to achieve that. By sharing content URI, we allow other apps to be granted read and write access for that particular file you are sharing using temporary access permissions.

在少数情况下,您可能希望将应用程序特定目录中保存的文件共享给其他应用程序。 在这种情况下,您可以将其作为内容URI共享。 Android File Provider很少公开API来实现此目的。 通过共享内容URI,我们允许使用临时访问权限为其他应用授予您正在共享的特定文件的读写访问权限。

To generate a content URI for a file, three steps are involved:

要为文件生成内容URI,涉及三个步骤:

  1. Declaring the File Provider in Android Manifest.

    在Android Manifest中声明文件提供程序。
  2. Declaring the paths in xml folder under res directory.

    res目录下的xml文件夹中声明路径。

  3. Generate the content URI using file provider API.

    使用文件提供程序API生成内容URI。

在清单中声明文件提供者 (Declaring File Provider in Manifest)

In the below snippet mydomain can be replaced by your own domain name of your choice.

在下面的代码段中,可以将mydomain替换为您自己选择的域名。

<manifest>
...
<application>
...
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="com.mydomain.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths" />
</provider>
...
</application>
</manifest>

在资源目录下的XML文件夹中声明路径 (Declaring Paths in XML folder under resources directory)

A File Provider can only generate a content URI for files in directories that you specify beforehand. So we have to create file named file_paths.xml under the xml folder inside the res directory. It goes as follows,

文件提供者只能为您预先指定的目录中的文件生成内容URI。 因此,我们必须创建名为file_paths的文件 res目录下xml文件夹下的xml 。 它如下

<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-files-path name="pictures" path="Pictures/" />
...
</paths>

Here we mentioned the path as external-files-path because in our example we are going access a file from the app’s external storage directory and then generate a content URI for that particular file. This indicates our Experiment 2 discussed above since we save the file in app’s external files directory and path refers under which folder that particular file is present. In our case for Experiment 2 its under Pictures folder under app’s external files directory.

在这里,我们将路径称为external-files-path,因为在我们的示例中,我们将从应用程序的外部存储目录访问文件,然后为该特定文件生成内容URI。 这表明我们在上面讨论了实验2 ,因为我们将文件保存在应用的外部文件目录中,并且路径引用了该文件所在的文件夹。 在我们的实验2中,它位于应用程序外部文件目录下的Pictures文件夹下。

可选的 (Optional)

Suppose if you want to follow Experiment 1 for this File Provider concept then you have to declare the path as,

假设您要针对此文件提供者概念遵循实验1 ,则必须将路径声明为:

<files-path path="/" name="pictures" />

Note: In Experiment 1 we are not creating any folder inside the files directory like we do it for Experiment 2

注意 :在实验1中,我们不会像在实验2中那样在files目录内创建任何文件夹

生成文件的内容URI (Generate content URI for a file)

So to generate a content URI for a file, I am gonna follow the Experiment 2 mentioned above. In this case we would be reading the file from the assets and saving it in the app’s external files directory and finally creating a content URI for the file we saved.

因此,要为文件生成内容URI,我将遵循上述实验2 。 在这种情况下,我们将从资产中读取文件并将其保存在应用程序的外部文件目录中,最后为我们保存的文件创建内容URI。

fun getContentUri(): String? {
val file = FileUtils.createFileInExternalStorage(appContext,
Constants.FILE_NAME
)
Timber.d("file path %s", file!!.absolutePath)
if (!file.exists()) {
file.createNewFile()
}
val assetManager = appContext.assets
val inputStream: InputStream
val bitmap: Bitmap
try {
inputStream = assetManager.open(SAMPLE_FILE_NAME)
bitmap = BitmapFactory.decodeStream(inputStream)
saveBitmap(bitmap, file)
return provideContentUri(file)
} catch (e: Exception) {
Timber.e(e)
}
return null}private fun provideContentUri(file: File): String {
val contentUri: Uri = getUriForFile(appContext,
Constants.FILE_PROVIDER_AUTHORITY, file)
return contentUri.toString()
}

The above function will return a content URI as given below:

上面的函数将返回内容URI,如下所示:

content://com.mydomain.fileprovider/pictures/test.jpeg

Cheers 🍺🍺🍺

干杯🍺🍺🍺

In the final article of the series we will learn about the Scoped Storage with few experiments like we followed in this article.

在本系列的最后一篇文章中,我们将通过本文所进行的一些实验来了解范围存储。

翻译自: https://medium.com/swlh/android-file-storage-file-provider-5990c9baf52d

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,以下是一个简单的示例程序,可以实现文件存储和读取功能。 1. 创建一个新的 Android Studio 项目,并在 MainActivity.java 文件中添加以下代码: ```java import android.content.Context; import android.os.Bundle; import android.os.Environment; import android.support.v7.app.AppCompatActivity; import android.view.View; import android.widget.EditText; import android.widget.Toast; import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStreamReader; public class MainActivity extends AppCompatActivity { private EditText mEditText; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mEditText = (EditText) findViewById(R.id.edit_text); // 加载上次保存的文本内容 String text = loadText(); if (text != null) { mEditText.setText(text); } } // 保存文本内容到文件中 public void saveText(View view) { String text = mEditText.getText().toString(); if (text != null && !text.equals("")) { try { FileOutputStream fos = openFileOutput("data.txt", Context.MODE_PRIVATE); fos.write(text.getBytes()); fos.close(); Toast.makeText(this, "保存成功", Toast.LENGTH_SHORT).show(); } catch (IOException e) { e.printStackTrace(); } } } // 从文件中加载文本内容 private String loadText() { try { FileInputStream fis = openFileInput("data.txt"); BufferedReader br = new BufferedReader(new InputStreamReader(fis)); StringBuilder sb = new StringBuilder(); String line; while ((line = br.readLine()) != null) { sb.append(line); } br.close(); return sb.toString(); } catch (IOException e) { e.printStackTrace(); } return null; } } ``` 2. 在 layout 目录下创建一个新的布局文件 activity_main.xml,添加以下代码: ```xml <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent"> <EditText android:id="@+id/edit_text" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_margin="16dp" android:hint="输入要保存的文本内容" /> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_below="@id/edit_text" android:layout_centerHorizontal="true" android:layout_marginTop="16dp" android:onClick="saveText" android:text="保存" /> </RelativeLayout> ``` 3. 运行程序,输入要保存的文本内容,点击保存按钮即可将文本保存到文件中。下次运行程序时,程序会自动加载上次保存的文本内容。 这个示例程序只是一个简单的文件存储示例,实际应用中可能需要更复杂的文件操作,如创建文件夹,删除文件等。同时,要注意 Android 系统对文件存储的限制,如存储权限等。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值