Home > OS >  C# File.AppendText() right after deleting the file fails
C# File.AppendText() right after deleting the file fails

Time:03-16

So this simple File.AppendText() method fails

using (var writer = File.AppendText(dst)){ ... }

when I call it right after I manually delete the file with the name dst. Manually here means using my own hand in a file explore. Also, I am certain that there is no open StreamWriter in the application scope as I am even using StreamWriter.WriteLine() which is not asynchronous.

I could not find a relevant question in StackOverflow, unfortunately. This situation happened with C# using Xamarin form on Android. I believe this behaviour could be general enough so that can happen in a random platform using C#. Hence, there should be a proper way to deal with the situation by checking if File.AppendText() method can properly open a StreamWriter.

Could anyone give me some advice?

Thanks a lot.

p.s. The full lines of the thrown exception follow.

System.IO.IOException: 
Could not create file "/storage/emulated/0/Documents/something". 
File already exists.

at System.IO.FileStream..ctor (
System.String path, 
System.IO.FileMode mode, 
System.IO.FileAccess access, 
System.IO.FileShare share, 
System.Int32 bufferSize, 
System.Boolean anonymous, 
System.IO.FileOptions options) [0x001aa] in
/Users/builder/jenkins/workspace/archive-mono/2020-02/android/release
/mcs/class/corlib/System.IO/FileStream.cs:239 

at System.IO.FileStream..ctor (
System.String path, 
System.IO.FileMode mode, 
System.IO.FileAccess access, 
System.IO.FileShare share, 
System.Int32 bufferSize, 
System.IO.FileOptions options) [0x00000] in
/Users/builder/jenkins/workspace/archive-mono/2020-02/android/release/
mcs/class/corlib/System.IO/FileStream.cs:106 

at (wrapper remoting-invoke-with-check)
System.IO.FileStream..ctor(
string,System.IO.FileMode,
System.IO.FileAccess,
System.IO.FileShare,
int,
System.IO.FileOptions)

at System.IO.StreamWriter..ctor (
System.String path, 
System.Boolean append, 
System.Text.Encoding encoding, 
System.Int32 bufferSize) [0x00055] in
/Users/builder/jenkins/workspace/archive-mono/2020-02/android/release/
external/corefx/src/Common/src/CoreLib/System/IO/StreamWriter.cs:151 

at System.IO.StreamWriter..ctor (
System.String path, System.Boolean append) [0x00000] in
/Users/builder/jenkins/workspace/archive-mono/2020-02/android/release
/external/corefx/src/Common/src/CoreLib/System/IO/StreamWriter.cs:131 

at (wrapper remoting-invoke-with-check) 
System.IO.StreamWriter..ctor(string,bool)

at System.IO.File.AppendText (System.String path) [0x0000e] in
/Users/builder/jenkins/workspace/archive-mono/2020-02/android/release
/external/corefx/src/System.IO.FileSystem/src/System/IO/File.cs:48 

at MyNameSpace.Droid.MyService.MyMethod () [0x001fd] 
in C:\workspace\my_project\my_code.cs:257 

CodePudding user response:

I had do a simple to test the method.

  string myfile = @"/mnt/sdcard/Android/data/com.companyname.apptest/cache/NewTextFile2.txt";
// Appending the given texts
   using (StreamWriter sw = File.AppendText(myfile))
   {
   sw.WriteLine("some thing");
   sw.WriteLine("some thing else");
   }

If the file's path isn't exited, it will create a file and then append. If the file's path is exited, it will append directly.

CodePudding user response:

So, I believe this is an Android issue. The Android version and the hardware are not important aspects of this discussion. A System.IO.IOException can happen in any situation in any environment unexpectedly. It is an engineer's responsibility to figure out what can be done in such a situation. I came up with a naive solution, and I post it as an answer.

The problem is the following. An application attempts to call the File.AppendText(), File.CreateText(), or File.Copy() method right after the file with the identical name previously existed and a user just deleted. Say, one of the aforementioned methods is called in one second. Then an exception

System.IO.IOException: 
Could not create file "/storage/emulated/0/Documents/something". 
File already exists.

is thrown at the line where the app is calling File.AppendText(), File.CreateText(), or File.Copy(). The destination path was generated using

string public_dst = Path.Combine(
    Android.OS.Environment.GetExternalStoragePublicDirectory(
    Android.OS.Environment.DirectoryDocuments).AbsolutePath,
    "my_file_name");

The source file name was generated using

string private_src = Path.Combine(
    System.Environment.GetFolderPath(
        System.Environment.SpecialFolder.Personal),
        $"my_source_file");

A naive way to alleviate this problem is to try again after a delay. A server-client networking system would try similar, no? The code reads

int CUR_TRIALS =    0;
int MAX_TRIALS =   10;
int INTERVAL   = 1000;  /* in milliseconds */

string str_ex = "";  /* @Console */

while (true)
{
    try
    {
        File.Copy(private_dst, public_dst);
        break;
    }
    catch (Exception ex)
    {
        str_ex = ex.ToString();
        string known = 
            $"System.IO.IOException: Could not create file "  
            $"\"{public_dst}\". File already exists.";

        bool is_known = str_ex.Contains(known);

        if (is_known && (str_ex != ""))
        {
            #if DEBUG
            Log.Info($"{TAG}.PrivateToPublic()", 
                $"System.IO.IOException: ",
                $"{CUR_TRIALS.ToString("D4")}/"  
                $"{MAX_TRIALS.ToString("D4")}");
            #endif

            Task.Delay(INTERVAL).Wait();
        }
    }

    CUR_TRIALS  = 1;
    if (CUR_TRIALS >= MAX_TRIALS)
    {
        #if DEBUG
        Log.Info($"{TAG}.PrivateToPublic()", str_ex);
        #endif

        throw new ApplicationException($"0x00000089");
    }
}

I manually specified how many times it can try and what time delay it suspends until it can try again. I easily can reconstruct this problem by deleting the file at the destination path and call one of the aforementioned methods right after it. The suggested routine definitely encounters the catch block. It suspends for a second and then it retries. It usually succeed in the very next attempt, so this approach can be practical.

  • Related