Android:如何将文件写入内部存储

54

我正在开发一个简单的安卓应用程序,需要在内部存储设备中写入文本文件。我知道有许多关于这个问题的问题(和答案),但我真的不明白我做错了什么。

这是我在活动中使用的代码片段,以便写入文件:

public void writeAFile(){
    String fileName = "myFile.txt";
    String textToWrite = "This is some text!";
    FileOutputStream outputStream;

   try {
      outputStream = openFileOutput(fileName , Context.MODE_PRIVATE);
      outputStream.write(textToWrite.getBytes());
      outputStream.close();
   } catch (Exception e) {
     e.printStackTrace();
   }
}

我真的不明白我犯了哪个错误。此外,我已经尝试在Android Studio模拟器和手机上运行这个项目,以便了解我做错了什么,但即使使用那个项目,也没有文件被写入到手机或模拟器中。

编辑: 我知道我的内部存储器中没有写入任何文件,因为我使用以下代码尝试读取文件内容(在我写入文件之后):

public void ReadBtn(View v) {
    //reading text from file
    try {
        FileInputStream fileIn=openFileInput("myFile.txt");
        InputStreamReader InputRead= new InputStreamReader(fileIn);

        char[] inputBuffer= new char[READ_BLOCK_SIZE];
        String s="";
        int charRead;

        while ((charRead=InputRead.read(inputBuffer))>0) {
            String readstring=String.copyValueOf(inputBuffer,0,charRead);
            s +=readstring;
        }
        InputRead.close();
        textmsg.setText(s);
    } catch (Exception e) {
        e.printStackTrace();
    }
}

完全没有显示任何内容。


6
@undervoter:在投票前请阅读!我知道这个问题已经被发布过了,但我有我的理由请求你的帮助。 - delca85
4
没必要点踩 - Moritz Schmidt
你的结果里有没有出现任何错误?Logcat 里有没有任何信息?另外,你是在哪个版本的 Android 上尝试做这件事情? - Vladyslav Matviienko
你期望运行这段代码后文件会被保存在哪里? - Vladyslav Matviienko
@VladMatvienko 在logcat中没有错误或消息。我使用的版本是25。 我期望在/data/data/[package-name]中找到该文件。 - delca85
9个回答

66

使用以下代码将文件写入内部存储:

public void writeFileOnInternalStorage(Context mcoContext, String sFileName, String sBody){      
    File dir = new File(mcoContext.getFilesDir(), "mydir");
    if(!dir.exists()){
        dir.mkdir();
    }

    try {
        File gpxfile = new File(dir, sFileName);
        FileWriter writer = new FileWriter(gpxfile);
        writer.append(sBody);
        writer.flush();
        writer.close();
    } catch (Exception e){
        e.printStackTrace();
    }
}

从API 19开始,您必须请求写入存储的权限。

您可以通过将以下代码添加到AndroidManifest.xml中来添加读取和写入权限:

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

您可以使用以下方式提示用户进行读写权限:

requestPermissions(new String[]{WRITE_EXTERNAL_STORAGE,READ_EXTERNAL_STORAGE}, 1);

之后您可以在从中调用的活动中的onRequestPermissionsResult()中处理权限请求的结果。


10
不要仅仅捕获异常而不记录它。 - CommonsWare
70
为何在读/写内部存储时要使用外部存储权限? - Dan
2
那么你的意思是说,EXTERNAL 是唯一写入磁盘的方式。这个名称有些误导。 - Dan
5
“Context”是指一个词、短语或语句所处的背景和环境,包括周围的文字、语言风格、主题等因素。 - john k
5
根据官方的Android参考页面(https://developer.android.com/reference/android/Manifest.permission#WRITE_EXTERNAL_STORAGE):从API 19级开始,在使用Context.getExternalFilesDir(String)Context.getExternalCacheDir()返回的应用程序专用目录中读写文件时不再需要此权限。 - Neph
显示剩余3条评论

20
没有文件被写入到手机或模拟器上。
是的,有文件被写入到Android SDK所称的内部存储中。这不是你作为用户所认为的“内部存储”,除非设备已经被root,否则你无法查看设备上内部存储的内容。
如果你想要写入一个用户可以看到的文件,请使用外部存储
这种基本的Android开发主题在任何一本像样的Android应用开发书籍中都有涉及。

我猜测这个应用程序写入的内部存储是根目录(我期望在/data/data/[包名]中找到一个文件)。从您提供的链接中,我了解到这并不总是正确的。 无论如何,我正在尝试从应用程序中读取此文件,但没有显示任何内容。 我将编辑我的问题以澄清这一点。 - delca85
1
@delca85:嗯,你的流I/O代码有点丑陋,而且实现不是并行的。使用一个包装在你的OutputStream周围的OutputStreamWriter来write()这个String,而不需要将其转换为字节。然后,使用一个InputStreamReader以较大的块(例如8192字节)读取文本。参考链接:https://dev59.com/UHVC5IYBdhLWcg3wZwJT#309718 - CommonsWare
@CommonsWare:感谢您的建议。我想我的问题与openFileOutput的使用有关,使用Sandeep Dipham发布的代码,我能够写入此文件。根据您的说法,这是唯一的解决方案吗? 现在我正在尝试使用FileInputStream读取,但我仍然遇到一些问题。 - delca85
1
@delca85:我也倾向于使用getFilesDir(),因为它更加灵活。当然,使用openFileInput()/openFileOutput()也没有问题。 - CommonsWare
@CommonsWare 对不起,如果我看起来很蠢,请问为什么我使用 openFileInput() / openFileOutput() 时无法检索文件,但是我可以使用 getFilesDir() 呢? - delca85
@delca85:我不知道。 - CommonsWare

4

保存到内部存储

data="my Info to save";

try {

    FileOutputStream fOut = openFileOutput(file,MODE_WORLD_READABLE);
    fOut.write(data.getBytes());
    fOut.close();   

    Toast.makeText(getBaseContext(), "file saved", Toast.LENGTH_SHORT).show();
}
catch (Exception e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
}

从内部存储读取

try {

    FileInputStream fin = openFileInput(file);
    int c;
    String temp="";

    while( (c = fin.read()) != -1){
        temp = temp + Character.toString((char)c);
    }
    tv.setText(temp);
    Toast.makeText(getBaseContext(), "file read", Toast.LENGTH_SHORT).show();
}
catch(Exception e){
}

1
使用 MODE_WORLD_READABLE 是一个不好的做法... 而且它在 Android >= 7 中不再受支持。 - Yousha Aleayoub
3
文件,是空的吗? - Acauã Pitta

4

Android 11更新

通过Android 11的新存储策略,您不能使用Environment.getExternalStorageDirectory()在主外部存储的根目录(即storage/emulated/0)或文件管理器中的内部存储中创建任何内容。这是因为此可能性导致外部存储成为一个随机内容的大杂烩。您可以在您自己应用程序保留的文件夹storage/emulated/0/Android/data/[PACKAGE_NAME]/files中创建您的文件,使用getFilesDir()方法,但其他应用程序(例如用户的文件管理器)无法访问该文件夹!请注意,这仅对您的应用程序可用。

最终解决方案(不建议使用)

顺便提一下,有一个解决方案,那就是将您的应用程序转换为文件管理器(不建议使用)。要做到这一点,您需要向您的应用程序添加此权限,并请求用户允许该权限:

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

Thoriya Prahalad描述了如何在这篇stackoverflow帖子中完成此工作。


3

要查看设备上的文件,您可以记录通过File.getAbsolutePath()等方法提供的文件位置,然后使用Android Studio的设备文件浏览器浏览设备文件。

我遇到了类似的问题,文件已经写入但我从未看到它。我使用了设备文件浏览器,它就在那里等着我。

Device File Manager

    String filename = "filename.jpg";
    File dir = context.getFilesDir();
    File file = new File(dir, filename);

    try {
        Log.d(TAG, "The file path = "+file.getAbsolutePath());
        file.createNewFile();
    } catch (Exception e) {
        e.printStackTrace();
    }
    return true;

0

抱歉,刚刚才注意到您说的是内部存储而不是外部存储。其他点仍然可能适用。您确定文件没有被创建吗?默认路径相当复杂,您可能只是在错误的位置查找。 - Kuffs
使用openFileOutput时不需要路径规范。我将使用getFilesDir()来发现是否在路径方面出了什么问题。 - delca85

0

要将文件写入内部存储器,您可以使用以下代码:

val filename = "myfile"
    val fileContents = "Hello world!"
    context.openFileOutput(filename, Context.MODE_PRIVATE).use {
            it.write(fileContents.toByteArray())
    }

0
我在这里找到了一个有帮助的答案,但它没有说明如何读取字符串,所以我在我的代码中解决了这个问题,并将其转换为Kotlin。
package com.happynicetime.reviewliferepeat

import android.content.Context
import java.io.File
import java.io.FileReader
import java.io.FileWriter

fun writeFileOnInternalStorage(mcoContext: Context, sFileName: String, sBody: String){
    var dir: File = File(mcoContext.getFilesDir(), "ReviewLifeRepeat")
    if(!dir.exists()){
        dir.mkdir();
    }
    try {
        var gpxFile: File = File(dir, sFileName)
        var gpxFileDir: File = gpxFile.parentFile
        if(!gpxFileDir.exists()){
            gpxFileDir.mkdir()
        }
        if(!gpxFile.exists()){
            gpxFile.createNewFile()
        }

        println("saving file: $gpxFile");

        var writer: FileWriter = FileWriter(gpxFile)
        writer.append(sBody)
        writer.flush()
        writer.close()
    }catch(e: Exception){
        e.printStackTrace()
    }
}

fun readFileOnInternalStorage(mcoContext: Context, sFileName: String): String{
    var dir: File = File(mcoContext.getFilesDir(), "ReviewLifeRepeat")
    try{
        var gpxFile: File = File(dir,sFileName)
        if(gpxFile.exists()) {
            var reader: FileReader = FileReader(gpxFile)
            val stringBuilder = StringBuilder()
            var readByte: Int
            while (reader.read().also { readByte = it } != -1) {
                stringBuilder.append(readByte.toChar())
            }
            return stringBuilder.toString()
        }else{
            return "";
        }
    }catch(e: Exception){
        e.printStackTrace()
        return ""
    }
}

-2

虽然这个链接可能回答了问题,但最好在此处包含答案的基本部分并提供参考链接。如果链接页面更改,仅有链接的答案可能会失效。-【来自审查】 - Robert
你的回答可以通过提供更多支持信息来改进。请编辑以添加进一步的细节,例如引用或文档,以便他人可以确认你的答案是正确的。您可以在帮助中心中找到有关如何编写良好答案的更多信息。 - Community

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接