尽管文件似乎存在、可写且具有权限,但 FileOutputStream 抛出 FileNotFoundException 异常。

4

虽然我对Android比较新,但我有Java和C编程方面的经验,并且目前正在使用Eclipse进行开发,使用通常的工具集。我已经阅读了大部分关于这个主题的帖子,并且相信我已经包含/应用了所有的建议和测试。我以前曾经使用FileOutputStream写入到内部的应用程序特定文件,适用于L8版本,在没有任何问题的情况下进行构建。现在我正在尝试使用Build L7为Android 2.1在SD卡上写文件。以下代码来自我正在使用来测试基本代码的单个活动,其中包含3个按钮(写入、读取和发送)。虽然在FileOutputStream(FOS)构造函数之前进行的所有包含测试(存在、可写和创建新文件或mkdirs时的IOException)都成功通过,但是FOS构造失败,抛出FileNotFoundException(请参见下文)。我已经通过调试进行了步骤验证并确认了该问题,请参见下面的LogCat。这是通过ADB在Eclipse标准模拟器上运行,构建了256kB的SD卡。

从我的createExternalStorageFile()方法。

File file = null;
        file = new File(Environment.getExternalStorageDirectory(), mfileName);              
        if(file != null) {
            //file.mkdirs();
            try {
                file.createNewFile();
            } catch (IOException e1) {
                Log.e(TAG, "createNewFile() failed!", e1);
                return false; 
            } 
        }
        if(!file.exists()) {
            Log.d(TAG, "file does not exist!");
        }
        if(!file.canWrite()) {
            Log.d(TAG, "cannot write to file!");
        }
        Log.d(TAG, "full file-path is: " + file.toString());

        FileOutputStream fOS = null;
        try {
            //fOS = new FileOutputStream(file);
            fOS = new FileOutputStream(file.toString());
            // *** THIS THROW THE FILENOTFOUNDEXCEPTION ***
        } catch (FileNotFoundException e1) {
            Log.e(TAG, "fOS-FileNotFoundException", e1);
            return false;
        }
        // fOS IS STILL NULL - HOW CAN THIS BE!
        if(fOS != null) {
            try {
                fOS.write(TESTDATA.getBytes());
                fOS.close();
            } catch (IOException e1) {
                Log.e(TAG, "IOException from fOS-write()!", e1);
                return false;
            }
            Log.d(TAG, "fos-fd is: " + fOS.toString());
        }
        return true;

我的清单文件包含以下uses-permission声明:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
      package="org.eddiem.adeveloper.externalfilesend"
      android:versionCode="1" android:versionName="1.0">
    <uses-sdk android:minSdkVersion="7" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

    <application android:icon="@drawable/icon" android:label="@string/app_name" android:debuggable="true">
        <activity android:name=".ExternalFileSendActivity"
                  android:label="@string/app_name">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>
</manifest>

> 这个是在声明之外的,应该没问题吧?

请问有人可以帮忙解决这个谜题吗?为什么FileOutputStream失败了,尽管我所有的测试都表明它应该能工作?

> LogCat: 11-02 15:40:54.754: DEBUG/MediaScannerService(154): 开始扫描外部存储卷 11-02 15:40:54.764: VERBOSE/MediaProvider(154): /sdcard 存储卷 ID:300427547 11-02 15:40:54.894: INFO/System.out(202): debugger has settled (1479) 11-02 15:40:55.024: VERBOSE/MediaProvider(154): 已连接存储卷:external 11-02 15:40:55.904: VERBOSE/MediaScanner(154): 删除死缩略图文件... android.database.sqlite.SQLiteCursor@44c434f0 11-02 15:40:55.915: VERBOSE/MediaScanner(154): /删除死缩略图文件... android.database.sqlite.SQLiteCursor@44c434f0 11-02 15:40:55.925: DEBUG/MediaScanner(154): 预扫描时间:715ms 11-02 15:40:55.955: DEBUG/MediaScanner(154): 扫描时间:4ms 11-02 15:40:55.955: DEBUG/MediaScanner(154): 后扫描时间:55ms 11-02 15:40:55.955: DEBUG/MediaScanner(154): 总时间:774ms 11-02 15:40:55.964: DEBUG/MediaScannerService(154): 完成扫描外部存储卷 11-02 15:44:50.934: DEBUG/KeyguardViewMediator(52): pokeWakelock(5000) 11-02 15:44:51.334: DEBUG/KeyguardViewMediator(52): pokeWakelock(5000) 11-02 15:44:51.384: INFO/ActivityManager(52): 显示 activity org.eddiem.adeveloper.filesendl7/.FileSendL7Activity:239793 ms(总计 255760 ms) 11-02 15:44:51.394: INFO/ARMAssembler(52): 生成 scanline__00000077:03545404_00000A04_00000000 [ 29 ipp] (51 ins)于 [0x46ac60:0x46ad2c],用时 757079 ns 11-02 15:44:51.414: INFO/ARMAssembler(52): 生成 scanline__00000177:03515104_00001A01_00000000 [ 73 ipp] (98 ins)于 [0x46ad30:0x46aeb8],用时 657626 ns 11-02 15:45:05.884: DEBUG/FileSendL7Activity(202): 完整文件路径为:/sdcard/testFile.txt 11-02 15:45:22.484: ERROR/FileSendL7Activity(202): fOS-FileNotFoundException 11-02 15:45:22.484: ERROR/FileSendL7Activity(202): java.io.FileNotFoundException: /sdcard/testFile.txt 11-02 15:45:22.484: ERROR/FileSendL7Activity(202): at org.apache.harmony.luni.platform.OSFileSystem.open(OSFileSystem.java:244) 11-02 15:45:22.484: ERROR/FileSendL7Activity(202): at java.io.FileOutputStream.(FileOutputStream.java:97) 11-02 15:45:22.484: ERROR/FileSendL7Activity(202): at java.io.FileOutputStream.(FileOutputStream.java:168) 11-02 15:45:22.484: ERROR/FileSendL7Activity(202): at java.io.FileOutputStream.(FileOutputStream.java:147) 11-02 15:45:22.484: ERROR/FileSendL7Activity(202): at org.eddiem.adeveloper.filesendl7.FileSendL7Activity.creatExternalStorageFileOS(FileSendL7Activity.java:149) 11-02 15:45:22.484: ERROR/FileSendL7Activity(202): at org.eddiem.adeveloper.filesendl7.FileSendL7Activity

谢谢


你在测试哪个设备? - FoamyGuy
导致异常的代码行是哪一行?你能指出来吗? - Zerhinne
抱歉遗漏了。我正在Eclipse模拟器上测试,构建版本为Android 2.1,API级别为7。失败的那行代码是:fOS = new FileOutputStream(file); - Eddie Moxey
我也尝试使用BufferedWrite而不是FileOutputStream,但仍然遇到相同的问题(FIlENOTFOUND)。尽管在DDMS FileExplore工具中看到我的测试文件,但仍然如此。我还尝试在运行Android_2.3.3的手机上运行此测试应用程序,但没有成功。我开始认为Eclipse中的构建有问题。非常感谢您的更多想法。 - Eddie Moxey
2个回答

4
阅读此文,有几个教训需要吸取。首先确保您的Eclipse或其他IDE已完全更新,并且所有软件包和工具都正确加载。下一个教训是确保您知道所有工具的工作原理!请参见下面的DDMS中的File_Explorer。
与这些表面上的编码问题相关的最大问题是,我的编码实验结果(通过Eclipse调试工具提供)显示了与Android文档中描述的完全不同的情况。我的偏执狂创造了其余的问题,我只是包含了太多版本的代码片段和太多测试。
技术问题/答案如下:
1. Android 2.1(API-L7)和2.2(API-L8)实现了不同的sdcard文件结构。因此,getExternalStorageDirectory()对于L7返回/sdcard/,对于L8返回/mnt/sdcard/。
2. Android关于外部存储器的文档是正确的;对于2.1(L7),请使用Environment.getExternalStorageDirectory(),对于2.2(L8),请仅使用Context.getExternalFilesDir(null)。
3. 构建File对象不会创建所提供名称的文件或目录。如果要在根目录之外创建目录树及其中的文件,则必须使用mkdirs()和createNewFile()。但是,当您成功打开FileOutputStream时,实际文件将被创建,因此通常不需要使用createNewFile()。如果要创建自己的目录,则必须使用mkdirs。
4. Eclipse中创建并与模拟器一起使用的Android虚拟设备实际上会创建目录和文件,您可以在调试期间使用DDMS上的File_Explorer选项卡查看这些目录和文件。注意:这些目录和文件是持久的,不会自动删除。在运行或调试代码后,请使用工具自己删除文件。
5. 最后,对于小文件和/或多次写入,BufferedOutputStream使用字节数组是首选选项。FileWrite用于字符数组,在所有情况下,您必须在Manifest.XML中包含权限。
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

一份最后的道歉,因为这个冗长的问答,但我希望很多人都能从我的痛苦中读懂和学到东西。 :o)

1

试试这个

if(!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
        Log.e("TEST", "this is bad");
}

File file = new File(getExternalFilesDir(null), "test.csv");

try {
    FileOutputStream fos = new FileOutputStream(file);
fos.write(...);
fos.close();
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

这个例子对我来说是有效的。然而,我刚刚尝试在创建FileOutputStream之前添加了file.createNewfile();,然后我得到了你遇到的相同错误。 - aleph_null
尽管getExternalFilesDir()方法出现在http://developer.android.com/reference/android/content/Context.html中,但Eclipse声称在Level 7构建的上下文中未定义此方法。因此,我的设置让我更加困惑。 - Eddie Moxey

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