为啥有时会抛出 FileNotFoundException

Posted

技术标签:

【中文标题】为啥有时会抛出 FileNotFoundException【英文标题】:why sometime it throws FileNotFoundException为什么有时会抛出 FileNotFoundException 【发布时间】:2016-03-04 05:19:54 【问题描述】:

代码大部分时间都能正常工作,但有时会抛出异常。不知道是什么原因造成的。

要做的是在

处创建一个文件

/storage/emulated/0/Download/theFileName.jpg

并向其写入数据(来自确实存在的 sourceFile),但新创建的文件出现“文件不存在”异常。

(它确实有 uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE", and uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" 在清单中)。

File sourceFile = new File(theSourceFileFullPath);
if (sourceFile.exists()) 
    File downloadDirectory = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS);
    String downloadPath = downloadDirectory.getPath();
    String newFilePath = (downloadPath + "/" + fileName);
    File newFile = new File(newFilePath);
    try 
        FileInputStream in = new FileInputStream(sourceFile);

        // ava.io.FileNotFoundException: 
        //     /storage/emulated/0/Download/theFileName.jpg: open failed: ENOENT (No such file or directory) 
        // exception at this line       
        FileOutputStream out = new FileOutputStream(newFile);
        //......
      catch (Exception e) 

【问题讨论】:

可能没有完成它的创建,您尝试访问它。每次应用打开时,它都会尝试创建一个文件并从中读取? 尝试替换:File newFile = new File(downlaodDirectory, fileName); Lispas,应用程序一直在运行。 mdtuyen, File newFile = new File(downlaodDirectory, fileName);没有帮助,如果同时具有 downlaodDirectory 和 fileName,则它与 new File(downlaodDirectory.getPath()+"/"+ fileName); 相同 FileInputStream 不创建文件。那么为什么会有这样的声明呢?你在做什么?从您的帖子中不清楚该异常是来自输入流还是输出流。 此错误是否出现在同一设备上?还是有些设备出错,有些设备没有? 【参考方案1】:

我可以想到这种行为的以下可能原因:

    无法创建新文件,因为 SD 卡上没有可用空间。下次遇到此问题时,请尝试通过文件管理器或“设置”->“存储”查看是否至少有一些可用空间。如果这是在您的用户设备上发生的情况,我怀疑您可以做些什么。 SD 卡由于某些操作系统故障或物理上未连接而无法使用,因此无法创建文件。再说一次 - 除了显示一些带有错误消息的 Toast 之外,您无能为力。 如何生成文件路径的fileName 部分? Sometimes 无法创建文件,因为文件名包含(此特定设备)不支持的字符。例如。我有 Android 4.1.2 的 HTC Desire X,如果文件名包含冒号,则无法使用与您的代码相似的代码创建文件。尝试另一种方法来生成文件名,看看是否有区别。

    在创建文件之前,您确定Downloads 目录存在吗?写如下内容:

    if (!downloadDirectory.exists()) 
        downloadDirectory.mkdirs();
    
    
    WRITE_EXTERNAL_STORAGE 权限在 Android 6 上被视为dangerous,因此用户可以随时撤销它。 Make sure 用户在写入文件之前没有撤销此权限。

    Always close the I/O stream 当你完成它的工作时。将 finally 子句添加到您的 try-catch 块并关闭其中的 inout 流:

    finally 
        if (in != null) 
            try 
                in.close();
             catch (Exception e)  
        
    
        if (out != null) 
            try 
                out.close();
             catch (Exception e)  
        
    
    
    最后一个 - 最后我别无选择。 :) 我想如果您从多个线程写入同一个文件,也可能会出现此问题。如果是这种情况,请尝试同步对您的文件编写代码的访问。

【讨论】:

再次感谢!崩溃是从 crashlytics 报告的,并且实例显示设备似乎没有内存不足。这可能是一个小故障,但有数百个报告。文件名就一个字就可以了。它不喜欢系统下载目录不存在,如果存在,那么同一个用户将报告多个实例,但事实并非如此。用户可以撤销 write_external_storage,但实例分布来自所有不同的设备。再次感谢您列出可能性。 @lannyf 我又忘记了一个机会:当您完成与inout 流的合作时,您并没有关闭它们。也试试这个。 再次感谢您。它确实关闭了流,但没有显示在代码 sn-p 中。 @lannyf 检查您是否不会同时从不同线程写入同一个文件。这是我能想到的最后一个选择。 :) 想不出是否有多个线程试图在这里写入同一个文件。当它到达这里时,生成的输出文件名是唯一的,并检查可能正在进行的文件以获得这里唯一的新名称。感谢您指出这一点!【参考方案2】:

根据 Java 文档 - Class FileNotFoundException:

public class FileNotFoundException extends IOException

表示尝试打开由指定路径名表示的文件失败的信号。

这个异常会被FileInputStream抛出, FileOutputStreamRandomAccessFile 构造函数,当文件包含 指定的pathname 不存在。也会被这些抛出 构造函数,如果文件确实存在但由于某种原因是 不可访问,例如当试图打开一个只读的 写文件。

要恢复,在三种情况下可能会抛出 FileNotFoundException

    指定的文件不存在。 命名文件实际上是一个目录。 由于某种原因,无法打开指定文件进行读取。

新创建的文件出现“文件不存在”异常。

在你的代码中我没有看到两件事

    成功写入后删除文件。我一点也不在开玩笑。如果我有这样和那样的错误,我会做一个测试,在从源文件复制内容后删除downlaodDirectory。在这种情况下,它会通过该方法,或者您会得到另一个但更具体的错误 - 就像我的话:无法删除文件。正在使用中 写入数据后关闭文件。我也没有看到关闭文件的方法。在您的代码中,您的文件似乎仍处于打开状态,因此您再次运行一个应用程序,您会因为错误而无法预料。

要解决此问题,只需在使用 file.close() 方法处理数据后关闭这两个文件即可。

希望对你有帮助

【讨论】:

感谢 piotrek。流确实关闭了,只是没有显示在代码 sn-p 中。 #1,name文件不存在,需要在这里创建。 #2,它是文件名而不是目录。 #3 这就是试图找出原因并解决它的方法。【参考方案3】:

    由于您创建文件的方式可能会发生此问题:

    String downloadPath = downloadDirectory.getPath();
    String newFilePath = (downloadPath + "/" + fileName);
    File newFile = new File(newFilePath);
    

    危险代码在这里:

    String newFilePath = (downloadPath + "/" + fileName);
    

    请尝试使用以下方法:

    File newFile = new File(downloadDirectory, fileName);
    

    SD 卡可用性 - 查看以下文档 http://developer.android.com/training/basics/data-storage/files.html#WriteExternalStorage。 可能是未安装媒体(例如在某些旧设备上 当用户通过 USB 将设备连接到计算机时会发生这种情况)因此,请尝试 检查媒体是否已安装:

    /* 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;
         
         return false;
     
    

    如果没有则通知用户。

【讨论】:

感谢格雷戈里! if newFilePath = (downloadPath + "/" + fileName);是问题,那么它会有更多的例外。除了这两个版本最终都调用了 fixSlashes()。 aga 已经提到了外部存储的可用性,不要认为这是设备的“下载”文件夹的问题,如果问题是相同的用户/设备会有更多的例外,事实并非如此。 @lannyf MEDIA_MOUNTED 问题有点棘手。它仅在极少数情况下发生。一种可能的情况,因此您可以测试它,当用户的设备有 SD 卡并且用户通过 USB 将设备(使用旧 Android)连接到计算机并选择 USB 连接以传输文件时。 (我的应用程序中遇到过类似的问题,因为我没有 SD 卡,所以从未发生过。) develper.android.com 推荐“isExternalStorageWritable”,它非常容易实现,所以我会研究一下。跨度> 谢谢格雷戈里!它在没有 SD 卡的设备上发生了很多报告的实例(对于相同的异常问题)。【参考方案4】:

您没有发布完整的异常堆栈跟踪,但我发现 SO 解决了确切的问题。

java.io.FileNotFoundException: /storage/emulated/0/bimagechooser/1429031333305.jpg: open failed: ENOENT (No such file or directory)

使用这个库image-chooser-library. & 需要重构代码。

那个家伙coomar2841。一定会帮助你。当他花费数小时解决问题时。所以节省你的时间。

获取 com.kbeanie.imagechooser 的源并将它们放入您的 APP,删除库的 gradle 声明,APP 应该可以工作。

请查看GitHub Issue

我希望它对你有用。 :)

【讨论】:

感谢 LukyBoy -KU!例外是 FileOutputStream out = new FileOutputStream(newFile);正如 sn-p 中所指出的那样。 问题设备是否特定?也许你可以通过 GITHUB 打开这个问题 它不是特定于设备的,它已在不同的设备上报告过,但显然它在大多数情况下也有效。 只有你能解决,只需深入挖掘,投我一票:)【参考方案5】:

我希望你在文件中写入数据的代码是完美的,我只是专注于你的问题。我认为有两种可能性得到 File not found Exception。

    有时文件名不支持某些特殊字符当您提供这种字符以提供文件名时,文件未正确生成,这就是发生此错误的原因所以请确保您的文件名正确。李> 第二个是你的文件扩展名。这里你没有指定你有哪种文件。

这里有一些代码片段,希望能帮助您解决问题。

解决方案

   File file=new File(Environment.getExternalStorageDirectory()+"/"+ConstansClass.FOLDERNAME);
    if(!file.exists())
    
        file.mkdir();
    


    String file=filename.replaceAll("[^a-zA-Z0-9.-]", " ");
    yourDir = new File(file, file+".mp3");
    if(yourDir.exists())
    
        return yourDir;
    

编辑

对不起,lannf 我不知道您的代码到底发生了什么,但我有一个代码 sn-p,它只是从 soundcloude 下载 mp3 文件并存储在我的本地存储中。我认为它与您的要求完全相同。所以我在下面发布了我的代码。

 @Override
protected File doInBackground(Void... params) 


    //File sdCardRoot = Environment.getExternalStorageDirectory()+"/Music";

    File file=new File(Environment.getExternalStorageDirectory()+"/"+ConstansClass.FOLDERNAME);
    if(!file.exists())
    
        file.mkdir();
    


    String filename=soundCloudeResponse.getTitle();//.replaceAll("[^a-zA-Z0-9.-]", " ");
    yourDir = new File(file, filename+".mp3");
    if(yourDir.exists())
    
        return yourDir;
    
    String url=soundCloudeResponse.getStream_url()+"?"+ConstansClass.SOUNDCLOUDE_CLIENT_ID;

    URL u = null;
    try 
        DebugLog.e("Request Url"+ url);
        u = new URL(url);
        URLConnection conn = u.openConnection();
        int contentLength = conn.getContentLength();

        DataInputStream stream = new DataInputStream(u.openStream());

        byte[] buffer = new byte[contentLength];
        stream.readFully(buffer);
        stream.close();

        DataOutputStream fos = new DataOutputStream(new FileOutputStream(yourDir));
        fos.write(buffer);
        fos.flush();
        fos.close();
        DebugLog.d("Download Complete in On Background");


     catch (MalformedURLException e) 
        sucess=false;
        e.printStackTrace();

     catch (IOException e) 
        sucess=false;
        e.printStackTrace();

    
    catch (Exception e)
    
        sucess=false;
        e.printStackTrace();
        DebugLog.e("Error ::"+e.getMessage());
    


    return yourDir;

我认为您是聪明的开发人员,因此您可以根据需要修改此代码。

祝你好运

【讨论】:

感谢奇拉格! '/storage/emulated/0/Download/theFileName.jpg' 是报告的异常之一的文件路径。文件名很好,有适当的分机。【参考方案6】:

还有一种可能:

如果您的代码的一部分正在写入文件而另一部分正在读取,那么最好在写入者完成文件写入之前考虑读取器正在读取。

您可以通过将代码置于调试模式来交叉检查这种情况。如果它在那里工作正常并给你 FileNotFoundException,那么这肯定是这个异常的潜在原因。

现在,如何解决:

您可以使用类似于以下代码块的重试机制

if(!file..exists())
Thread.sleep(200);

在您的代码中并根据您的需要更改睡眠值。

希望对您有所帮助。!!

【讨论】:

以上是关于为啥有时会抛出 FileNotFoundException的主要内容,如果未能解决你的问题,请参考以下文章

为啥我的比较方法有时会抛出 IllegalArgumentException?

为啥我的线程池有时会抛出 `std::bad_function_call` 或 `double free or corruption (!prev)`

为啥 FileOutputStream 会抛出 FileNotFoundException?

为啥“prepareCall”会抛出 NumberFormatException?

为啥 BluetoothSetLocalServiceInfo 会抛出错误 1314?

为啥geoip会抛出异常?