2

I am trying to write a byte array to a file, multiple times. The FileMode is set to Append, to create the file if it doesn't exist, otherwise open the file and seek to the end of the file as described. The problem is that when writing to the existing file, it gets overwritten rather than have the new byte array appended to it. That's all there is to it.

void WriteToFile()
{
    byte[] buffer = new byte[16 * 1024];
    int num;

    using (FileStream dest_stream = new FileStream(filename, FileMode.Append, FileAccess.Write))
    {
        while (num = ReadFromAnotherStream(my_other_stream, ref buffer) > 0)
            dest_stream.Write(buffer, 0, num);
    }
}

This function will be called occasionally. If the file already exists, seek to the end of the file and continue writing from there, otherwise create a new file and write data.


When it should append, it overwrites... It does not append.

It should append to the file instead of overwrite it.

There is no error thrown.

Using Seek for the FileStream does nothing.

When it overwrites, the data is correct, however, it needs to be appended at the end of the previous data, and not overwrite.


UPDATE: Well, I had no choice but to divide each call into multiple "temp" files then at the end merge them into the main file. Such worked flawlessly, no Seeking was required leading to non-corrupted files.

A downside would be extra processing for multiple temp files (especially large ones) being merged into one.

Pseudo-code:

string filename;
List<string> tmp_files = new List<string>();
int __i = 0;
do
{
    filename = $"my_file.tmp{__i++}";
    tmp_files.Add(filename);
}
while (File.Exists(filename));
// Every time WriteToFile() gets called... This should be on top.
// Instead of writing directly to the file, add more of them until the input stream has been read fully.
using (FileStream out_fs = new FileStream("my_file.bin", FileMode.Create))
{
    foreach (string tmp_file in tmp_files)
    {
        using (FileStream in_fs = new FileStream(tmp_file, FileMode.Open))
        {
            in_fs.CopyTo(out_fs);
        }
        File.Delete(tmp_file);
    }
}

First of all, thank you everyone who took part in this thread. Part of the code could not be shared and that is beyond me. I understand that there is no magic ball out there to read minds from pseudo-codes, but I was in desperate to solve this unethical mistake from the API, so I wanted to get as many possibilities.

I still don't know what the issue is with Append, but the input stream has absolutely nothing to do with it and that's out of the way for now.

IOviSpot
  • 358
  • 3
  • 19
  • 1
    How do you use `Seek` ? – Poul Bak Nov 24 '21 at 19:04
  • 1
    Please include the code for `ReadFromAnotherStream` – Nigel Nov 24 '21 at 19:05
  • @PoulBak `dest_stream.Seek(0L, SeekOrigin.End);` – IOviSpot Nov 24 '21 at 19:13
  • @NigelBess That part of the code is irrelevant. dest_stream is of concern here, I would know. – IOviSpot Nov 24 '21 at 19:14
  • Well what is the value of num? What is the type of my_other_stream? These are important details – Nigel Nov 24 '21 at 19:17
  • You must debug your code and assert that you are writing to the expected files. The FileStream API is definitely working as you would expect it to. You probably encounter a permission issue: *"[...] This requires Append permission."*. You should check that too. Did you know that FileStream exposes an async API? You can use ReadAsync and WriteAsync to improve the performance. – BionicCode Nov 24 '21 at 19:31
  • @BionicCode You mean that this has to be implemented explicitly? https://learn.microsoft.com/en-us/dotnet/api/system.security.permissions.fileiopermissionattribute.append?view=dotnet-plat-ext-6.0 – IOviSpot Nov 24 '21 at 20:23
  • "normally" you don't have to. It depends on the user account rights of the user who runs the application. If you can, try to run the code on a different machine and try to run it giving it administrator rights. That's just a suggestion. Setup a test environment with two test files an run the read/write code alone. Also check the security settings of the original files. Maybe the files are located in a special directory??? In this case try to give the required permission explicitly. – BionicCode Nov 24 '21 at 20:49
  • Note, that the correct way would be to prompt the user to give the application the required permission instead of all him to execute the application as administrator. The application should not silently override user security settings. This is malicious behavior. – BionicCode Nov 24 '21 at 20:49
  • If you need more specific help, please update your question to provide a minimal viable example code that reproduces the issue. This way we can properly investigate the implementation. – BionicCode Nov 24 '21 at 21:01
  • 1
    Seems there is a solution to your problem here: https://stackoverflow.com/questions/15722365/append-byte-array-at-the-end-of-an-file by using BinaryWriter... - for your approach, as the `FileStream.Write(Byte[], Int32, Int32)` mode defines an explicit offset, your issue is there probably? Might be worth trying the `Write(ReadOnlySpan)` method. – ChriPf Nov 24 '21 at 23:22
  • No. The offset is for the input byte array. – IOviSpot Nov 25 '21 at 00:47
  • I’m voting to close this question because I couldn't provide unnecessary details and had no way to properly output every diagnostic. I simply wanted to get as many opinions as possible and I am grateful to everyone who participated. I've solved my issue and this thread has absolutely no reason to continue with discussions. – IOviSpot Nov 25 '21 at 01:18

1 Answers1

1

We can't tell what is wrong with your code because you didn't include the code for ReadFromAnotherStream. But here is some code that does what you want, and it works:

    /// <summary>
    /// Appends the contents of the file at inputFilePath to the file at pathToAppendTo
    /// </summary>
    void Append(string inputFilePath,string pathToAppendTo)
    {

        var buffer = new byte[16];

        using FileStream source = new FileStream(inputFilePath, FileMode.Open, FileAccess.Read);
        using FileStream destinationStream = new FileStream(pathToAppendTo, FileMode.Append, FileAccess.Write);
        while (TryRead(source, buffer, out int bytes))
        {
            destinationStream.Write(buffer, 0, bytes);
        }
    }
    
    private bool TryRead(FileStream source, byte[] buffer, out int bytesRead)
    {
        bytesRead = source.Read(buffer, 0, buffer.Length);
        return bytesRead > 0;
    }

Here is a unit test to verify that it works:

    [TestMethod]
    public void TestWriteFile()
    {
        var inputFileName = "C:/fileYouWantToCopy";
        var outputFileName = "C:/fileYouWantToAppendTo";

        var originalFileBytes = GetFileLength(outputFileName);
        var additionalBytes = GetFileLength(inputFileName);

        Append(inputFileName,outputFileName);
        Assert.AreEqual(GetFileLength(outputFileName), originalFileBytes + additionalBytes);
        
    }

    private long GetFileLength(string path) => new FileInfo(path).Length;
Nigel
  • 2,961
  • 1
  • 14
  • 32
  • FileStream has an async API you should use instead. – BionicCode Nov 24 '21 at 19:31
  • 1
    @BionicCode I don't think that is relevant to the question – Nigel Nov 24 '21 at 19:32
  • You don't have to provide a unit test to prove that the .NET libaray's FileStream API is not buggy. This is obvious. – BionicCode Nov 24 '21 at 19:33
  • Apparently, your whole answer is not relevant to the question. – BionicCode Nov 24 '21 at 19:34
  • @BionicCode the unit test verifies that the code I wrote is not buggy. And my code is effectively no different from OP's code, so really the unit test shows that OP failed to include the buggy code in their question – Nigel Nov 24 '21 at 19:35
  • You don't have to prove that creating a FileStream using FileMode.Append advances the stream position to the end of the existing file, to allow appending data. This is redundant. FileStream works. The reason why the original file content is overwritten has nothing to do with the API. – BionicCode Nov 24 '21 at 19:37
  • Sorry, I'm not salty. Your answer is simply redundant as the FileStream API was never questioned or has any reason to be questioned. The API is not the reason for the behavior. – BionicCode Nov 24 '21 at 19:38
  • 1
    The answer is very much relevant to the question missing features that are beyond me right now. I have acknowledged that the destination files as well as the input stream work when not appending to a file, but writing the whole content to a new fresh. Leading to an obvious conclusion that the input stream has nothing to do with it, however... This answer is still valid, but does not solve my case. – IOviSpot Nov 24 '21 at 20:25
  • 1
    @wEight can you please explain how it doesn't solve your case? What happens when you run the unit test? – Nigel Nov 24 '21 at 20:27