Why does FileOutputStream throw FileNotFoundException?
Asked Answered
F

4

14

The Android Developer reference (this page) says:

Throws FileNotFoundException

But at the very start, it says:

Open a private file associated with this Context's application package for writing. Creates the file if it doesn't already exist.

If that is the case, why would the FileNotFoundException ever be thrown?

I just want to make sure I'm properly handling all cases. I am using the default functionality, so can I just wrap this in a try..catch block with nothing in the catch block since it is not possible for a FileNotFoundException to ever be thrown in the default functionality?

Edit: example of 'default functionality':

String FILENAME = "hello_file";
String string = "hello world!";
FileOutputStream fos = context.openFileOutput(FILENAME, Context.MODE_PRIVATE);
fos.write(string.getBytes());
fos.close();
Frow answered 23/11, 2013 at 12:44 Comment(2)
Is it possible that your app will not have permission to create file in place you point?Grade
@Grade I should point out, I'm not actually getting the Exception, just preparing for the possibility it might be thrown on another device. But folder permission is a very valid point (though it should not be an issue on the root dir, right?)Frow
A
12

This might happen for example if you try to open a Folder or if the file you try to open does not exist, but you don't have permissions to create it either.

Aintab answered 23/11, 2013 at 12:53 Comment(4)
This seems like the most likely situation where the Exception would be thrown; thank you for your answerFrow
Makes sense, although an incredibly poor choice of exception name!Bunch
'Does not exist' is correct, but 'but you don't have permissions to create it either' is not. FileOutputStream does not attempt to create directories.Dimercaprol
why do not have permissions sometimes?Footboy
D
3

The ContextImpl.openFileOutput is implemented as following on ICS:

@Override
public FileOutputStream openFileOutput(String name, int mode)
    throws FileNotFoundException {
    final boolean append = (mode&MODE_APPEND) != 0;
    File f = makeFilename(getFilesDir(), name);
    try {
        FileOutputStream fos = new FileOutputStream(f, append);
        setFilePermissionsFromMode(f.getPath(), mode, 0);
        return fos;
    } catch (FileNotFoundException e) {
    }

    File parent = f.getParentFile();
    parent.mkdir();
    FileUtils.setPermissions(
        parent.getPath(),
        FileUtils.S_IRWXU|FileUtils.S_IRWXG|FileUtils.S_IXOTH,
        -1, -1);
    FileOutputStream fos = new FileOutputStream(f, append);
    setFilePermissionsFromMode(f.getPath(), mode, 0);
    return fos;
}

The function makeFileName will make sure you cannot specify any directory structure here:

private File makeFilename(File base, String name) {
        if (name.indexOf(File.separatorChar) < 0) {
            return new File(base, name);
        }
        throw new IllegalArgumentException(
                "File " + name + " contains a path separator");
    }

Although this seems not possibly to see the Fnf exception, however, you can see that it is not thread safe, so f.getParentFile() might still throw that exception if some other thread is removing the /data/data/com.yourpkg.name/files directory.

Delphiadelphic answered 23/11, 2013 at 12:58 Comment(5)
I see references to 'append mode', which is not default functionality, however thank you for illustrating a case where the Exception can be thrownFrow
Is Boda's answer really the case you are talking about?Delphiadelphic
Yes, since a missing source folder may also cause the issue, and his answer is the most likely and relevant for anyone else who raises this question. I take this opportunity however to thank you for the amount of depth and research you put into your answerFrow
Curious that it bothers to create the parent directory but not the grandparent etc. directories.Dimercaprol
Android want to limit all your 'misc' files to be stored in the 'files' folder in your package directory.Delphiadelphic
D
2

The most common cause is that an intermediate directory doesn't exist. FileOutputStream won't create those. It can also be a permissions problem: the entire path exists but you don't have create permissions in the final directory, or overwrite permissions on the actual file if it already exists.

Dimercaprol answered 24/11, 2013 at 23:40 Comment(2)
You are talking on generic Linux, for this special Android 'files' folder case, the 'files' folder is normally created by framework APIs. So the situation mentioned in your answer will happen when user intentionally manual create the 'file' folder without assign the write permission?Delphiadelphic
@Rohan I am talking about generic operating systems. The word 'Linux' does not appear in my answer, which applies equally to Unix, Windows, AIX, HP-UX, Solaris, MS.DOS, OS/2, IX/370, and any number of other systems. I've already addressed the matter of permissions in the final directory.Dimercaprol
S
2

In my case the reason was that the name of the file was not OK. Obviously, colons in the filename are disliked.

Not working (-> FileNotFoundException):

    SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd_HH:mm:ss.SSS");
    String fileName = simpleDateFormat.format(new Date());
    FileOutputStream fileOutputStream = new FileOutputStream(new File(context.getFilesDir(), fileName), false);

Working:

    SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd_HH.mm.ss.SSS");
    [...]

(Difference is the "." instead of ":" between HH, mm and ss in the SimpleDateFormat)

Sometimes answered 9/1, 2016 at 21:0 Comment(1)
Thanks! "." instead of ":" is what I was looking for!Derwin

© 2022 - 2024 — McMap. All rights reserved.