How to append to a file after File.CreateText? - c#

I am writing a C# app that needs to create a CSV file.
The problem is after issuing a File.CreateText({file path}), I cannot immediately write to it. If the file already exists, I can write no problem. Here is the code:
if (!File.Exists(file_name))
{
File.CreateText(file_name);
File.WriteAllText(file_name, String.Format("Old Name,Short Name{0}", this.old_name_txt.Text, this.new_name_txt.Text, Environment.NewLine));
}
else
{
File.AppendAllText(file_name, String.Format("{0},{1}{2}", this.old_name_txt.Text, this.new_name_txt.Text, Environment.NewLine));
}
After creating the file, I have also tried "File.AppendAllText..."
The error it is producing says that it cannot write because the file is being used by another process.

File.CreateText will create a text file and open the file. It then returns a StreamWriter which you could use to access that created file. Because you opened the file like this already, you will get an error when trying to open that file again.
When reading the docs for File.CreateText make sure to look at the return value as well, to understand what the function does.
Please read the documentation for File.AppendAllText as well and then change your code accordingly.
Here's the most important part in the docs:
AppendAllText(String, String)
Description
Opens a file, appends the specified string to the file, and then closes the file. If the file does not exist, this method creates a file, writes the specified string to the file, then closes the file.
You should be able to solve your problem by yourself now.

Related

rest api output to json file in c# [duplicate]

I have a string with a C# program that I want to write to a file and always overwrite the existing content. If the file isn't there, the program should create a new file instead of throwing an exception.
System.IO.File.WriteAllText (#"D:\path.txt", contents);
If the file exists, this overwrites it.
If the file does not exist, this creates it.
Please make sure you have appropriate privileges to write at the location, otherwise you will get an exception.
Use the File.WriteAllText method. It creates the file if it doesn't exist and overwrites it if it exists.
Generally, FileMode.Create is what you're looking for.
Use the file mode enum to change the File.Open behavior. This works for binary content as well as text.
Since FileMode.Open and FileMode.OpenOrCreate load the existing content to the file stream, if you want to replace the file completely you need to first clear the existing content, if any, before writing to the stream. FileMode.Truncate performs this step automatically
// OriginalFile:
oooooooooooooooooooooooooooooo
// NewFile:
----------------
// Write to file stream with FileMode.Open:
----------------oooooooooooooo
var exists = File.Exists(path);
var fileMode = exists
? FileMode.Truncate // overwrites all of the content of an existing file
: FileMode.CreateNew // creates a new file
using (var destinationStream = File.Open(path, fileMode)
{
await newContentStream.CopyToAsync(destinationStream);
}
FileMode Enum
If your code doesn't require the file to be truncated first, you can use the FileMode.OpenOrCreate to open the filestream, which will create the file if it doesn't exist or open it if it does. You can use the stream to point at the front and start overwriting the existing file?
I'm assuming your using a streams here, there are other ways to write a file.

File.AppendAllText causes access exception to be thrown when program run for the second time

I have a log file that I delete and create every time my application is launched like so:
if (File.Exists(LogPath))
{
File.Delete(LogPath);
File.Create(LogPath);
}
And I'm writing in it using File.AppendAllText like so:
File.AppendAllText(LogPath, logMessage);
My issue is that when I run the program for the second time, the above call causes an exception to be thrown saying file can't be accessed
"because it is being used by another process"
What is wrong with this approach?
It's caused by File.Create(). Remove it and File.AppendAllText creates a new file if it doesn't exist.
Note:
File.Create() returns a FileStream value, if you do not dispose it, then it will cause an error when you want to access it.
This is not because of File.AppendAllText but instead this line of code:
File.Create(LogPath);
As per the documentation of File.Create(string):
Return Value
Type: System.IO.FileStream
A FileStream that provides read/write access to the file specified in path.
It returns an open FileStream object. You need to dispose of this object in order to close the stream and release the file. If you don't then this object will keep the file open until GC finalizes the object at some later indeterminate point in time.
Here's how to write this line of code, either one of the following two alternatives will work:
File.Create(LogPath).Dispose();
using (File.Create(LogPath)) { }
What happened is that the second time your program ran the file exists, so you deleted it and then recreated it, but the "recreated it" part kept the file open, so when it a short time later reached the File.AppendAllText method, the file was still open.
Note: If you always call File.AppendAllText you can simply just delete it, as AppendAllText will create the file if it doesn't already exist, as per the documentation of File.AppendAllText:
Opens a file, appends the specified string to the file, and then closes the file. If the file does not exist, this method creates a file, writes the specified string to the file, then closes the file.
(my emphasis)
You would need to close the file after you create for further processing.
if (File.Exists(LogPath))
{
File.Delete(LogPath);
using(var handler = File.Create(LogPath))
{
}
}
Other way could be to use WriteAllText and you won't need to delete it everytime.
File.WriteAllText(LogPath, "contents");
You, probably, mean
// clear the file (write an empty text to it) if it exists
if (File.Exists(LogPath))
{
File.WriteAllText(LogPath, "");
}
...
File.AppendAllText(LogPath, logMessage);
you can try combining clearing and writing in one call:
File.WriteAllText(LogPath, logMessage);
If the file exists, WriteAllText will clear it and write logMessage; if file doesn't exist, WriteAllText will create it and write logMessage.

Corrupt ZIP file if calling Save twice

I am using DotNetZip 1.9.6 in my application which uses a file structure similar to e.g. *.docx: Zip file containing XML files.
Now every module of the application can store such XML files to my custom file management and on "save" they are serialized to streams which are then saved to the Zip file via DotNetZip.
To update the entries I use ZipFile.UpdateEntry(path, stream).
This works fine and the first time I save my file via calling ZipFile.Save() everything works.
But If I do this a second time (first some UpdateEntrycalls then Save) on the same instance the Zip file is corrupted: The file structure and meta-data (e.g. uncompressed size of each file) is still there, but all files are 0 byte in compressed size.
If I create a new instance from the just saved file after saving everything works fine, but shouldn't it be possible to avoid that and "reuse" the same instance?
The following example (also see https://dotnetfiddle.net/mHxEIy) can be used to reproduce the problem:
using System.IO;
using System.Text;
public class Program
{
public static void Main()
{
var zipFile = new Ionic.Zip.ZipFile();
var content1 = new MemoryStream(Encoding.Default.GetBytes("Content 1"));
zipFile.UpdateEntry("test.txt", content1);
zipFile.Save("test.zip"); // here the Zip file is correct
//zipFile = new Ionic.Zip.ZipFile("test.zip"); // uncomment and it works too
var content2 = new MemoryStream(Encoding.Default.GetBytes("Content 2"));
zipFile.UpdateEntry("test.txt", content2);
zipFile.Save(); // after that it is corrupt
}
}
To run this you need to add the "DotNetZip 1.9.6" NuGet package.
After the first save, this is what you get:
and after the second save:
This looks like it's a bug in the library, around removing an entry. If you just remove an entry and then save again, it correctly removes the file.
However, if you remove an entry and then add another one with the same name - which is what UpdateEntry is documented to do if the entry already exists - the old entry appears to be used instead.
The reason you're ending up with an empty file the second time is that the original MemoryStream is being read again - but by now, it's positioned at the end of the data, so there's no data to read. If you reset the position to the start of the stream (content1.Position = 0;) it will rewrite the original data. If you modify the data within content1, you end up with invalid compressed data.
The only workaround I can immediately think of is to keep your own map from filename to MemoryStream, and replace the contents of each MemoryStream when you want to update it... or just load the file each time, as per your existing workaround.
It's definitely worth filing a bug around this though, as it should work as far as I can tell.
As already suspected this was a bug in DotNetZip up to version 1.9.6.
I think I was able to fix this with THIS change which was just released as version 1.9.7 on NuGet. At least for me the problem does not happen anymore.
Some background what happend as far as I found out:
When you call Save the library sets an internal flag which remembers that the ZIP file was just save and on the second Save call instead of "recompressing" all entries in the ZIP file it copies them from the just saved file.
This works fine for adding/removing entries, but breaks when one of the entries was changed as then it "mixes" the old and the new entry and produces the inconsisten ZIP file.
My fix basically disables that "copy from old file" logic if an entry was changed.

How to read file in C# from POST data from web

Basically, I'm building a website that allows user to upload file.
From the front end (JavaScript), the user will browse a file, I can get the site to send POST data (the parameter "UploadInput" and it's value, which the value is the file)
In the backend (C#), I want to make a copy of the file and save it in a specific path.
Below is the way I did it.
var files = Request.Files;
file[0].SaveAs("\temp\\" + file[0].FileName);
The problem I ran into is that I got the error message saying index out of range. I tried Response.Write(files.Count) and it gives me 0 instead of 1.
I'm wondering where I did wrong and how to fix it, or if there's a better way of doing it.
Thanks!
Edit:
I am using HttpFox to debug. From HttpFox, I can see that under POST data, parameter is "UploadInput" and the value is "test.txt"
Edit 2:
So I tried the way Marc provides, and I have a different problem.
I am able to create a new file, however, the content is not copied over. I tried opening the new created file in notepad and all it says is "UploadInput = test.txt"
If they simply posted the file as the body content, then there will be zero "files" involved here, so file[0] will fail. Instead, you need to look at the input-stream, and simply read from that stream. For example:
using(var file = File.Create(somePath)) {
Request.InputStream.CopyTo(file);
}

The file is being used by another process, I must close it? How?

I am using a text file to have some data there for later purposes. So what I do is to check if file exists, if not I am creating a new file when I need. This gives me error saying that my file is still being used by a different process, but I'm not sure why that is.
This is how I do it. Here, I am checking if file exists which starts when program runs:
private void CreateLastOpenFile()
{
if (!Directory.Exists(directory))
{
Directory.CreateDirectory(directory);
}
if (!File.Exists(file))
{
File.Create(file);
}
}
Now, I am adding some data to it while checking or creating a new file (I am having this in 2 places in my program):
CreateLastOpenFile();
File.WriteAllText(file, data);
What could be wrong here? I have read some examples from the Net, but didn't see anything about closing any files.
Try this. This will close the opened stream during file creation
if (!File.Exists(file))
{
FileStream str = File.Create(file);
str.Close();
}
File.Create is creating a FileStream that locks the file. You should close it. Actually, you don't even need to create a file. File.WriteAlltext will do it for you
You are not closing the stream handle that File.Create() returns.
Suggest you do not use File.Create() in your case. You can just use File.WriteAllText(file, data); - according to MSDN documentation it creates the file if it doesn't exist or overwrites the contents when file exists. After that closes the file stream.
I recommend you to create and fill with data the file in one step, using some class like StreamWriter that allows you to dispose the class, you should not have problem doing it this way, here is an example:
StreamWriter Swr = new StreamWriter(FilePath);
Swr.Write(Data);
Swr.Close();
Swr.Dispose();
//Doing the close and Dispose you get sure the file is not locked anymore
You can also use File.WriteAllText(string Path, string Data), this method does not lock the file.
If you are using below method to write the data into text file, you dont need to check if file exists and if not create it. "WriteAllText" takes cares of all these things by itself. It will create the file if not exists, write the data and close it, or overwrite the file if already exists.
File.WriteAllText(file, data);
if you are using writeAllText() or readAllText() method than close() method is not used as they closed file after reading or writing(above methods)

Categories