C# close stream and delete file if something failed - c#

I'm working with a file stream in C#. It's a storage cache, so if something goes bad writing the file (corrupted data, ...), I need to delete the file and rethrow the exception to report the problem. I'm thinking on how to implement it in the best way. My first attempt was:
Stream fileStream = null;
try
{
fileStream = new FileStream(GetStorageFile(),
FileMode.Create, FileAccess.Write, FileShare.Write);
//write the file ...
}
catch (Exception ex)
{
//Close the stream first
if (fileStream != null)
{
fileStream.Close();
}
//Delete the file
File.Delete(GetStorageFile());
//Re-throw exception
throw;
}
finally
{
//Close stream for the normal case
if (fileStream != null)
{
fileStream.Close();
}
}
As you will see, if something goes bad writing the file, the fileStream will be closed twice. I know that it works, but I don't think that is the best implementation.
I think that I could remove the finally block, and close the stream in the try block, but I have posted this here because you guys are experts and I want to hear the voice of an expert.

If you put the fileStream in a using block you don't need to worry about closing it, and then just leave the cleaning up (deleting of the file in the catch block.
try
{
using (FileStream fileStream = new FileStream(GetStorageFile(),
FileMode.Create, FileAccess.Write, FileShare.Write))
{
//write the file ...
}
}
catch (Exception ex)
{
File.Delete(GetStorageFile());
//Re-throw exception
throw;
}

I believe what you want is this:
var fs = new FileStream(result.FilePath, FileMode.Open, FileAccess.Read, FileShare.None, 4096, FileOptions.DeleteOnClose);
I've used it with ASP.Net to have the web server return a result to a temp file that's on disk, but to make sure it's cleaned up after the web server finishes serving it to the client.
public static IActionResult TempFile(string tempPath, string mimeType, string fileDownloadName)
{
var fs = new FileStream(tempPath, FileMode.Open, FileAccess.Read, FileShare.None, 4096, FileOptions.DeleteOnClose);
var actionResult = new FileStreamResult(fileStream: fs, contentType: mimeType)
{
FileDownloadName = fileDownloadName
};
return actionResult;
}

Related

How to execute next line code if current line throwing exception

I have a certain requirement. When current line of code throwing exception, I want to move to next line
FileStream fs = new FileStream("D:/temp/product.xml", FileMode.Open, FileAccess.Read);
sometimes D:/ drive don't have xml file, it throwing FileNotFoundException and jumping control out of scope. but then in next line I want to check another location
FileStream fs = new FileStream("//letp.rf.servername.com/products/product.xml", FileMode.Open, FileAccess.Read);
How can I fix this issue?
Use defensive check and check whether the file exists first using File.Exists(String) method before actually accessing it. Again, wherever possible we should use Defensive Check rather Exception Handling since exception handling is expensive operation. How expensive are exceptions in C#?
Finally, you can wrap this entirely in a try .. catch block to make sure catching any other exception down the line and logging them.
try
{
if (File.Exists("D:/temp/product.xml"))
{
FileStream fs = new FileStream("D:/temp/product.xml", FileMode.Open, FileAccess.Read);
}
else
{
// check another location
}
}
catch (Exception ex)
{
// perform logging
}
All you need to do is wrap your code in a try-catch block, for example:
FileStream fs = null;
try
{
fs = new FileStream("D:/temp/product.xml", FileMode.Open, FileAccess.Read);
}
catch (FileNotFoundException e)
{
// Retry another file,
}
If the retry can also fail, you'll have to wrap it also.
(Btw, Rahul's answer is better and easier)
To use this in a loop:
FileSystem fs = null;
foreach (var file in files) // files contains the file paths
{
// Solution #1
try
{
fs = new FileStream(file, FileMode.Open, FileAccess.Read);
break;
}
catch (FileNotFoundException e) { }
// Or you can use File.Exists as per Rahul's answer
// Solution #2
if (File.Exists(file))
{
fs = new FileStream(file, FileMode.Open, FileAccess.Read);
break;
}
}
Don't use exceptions to check whether a file exists, but check whether the file exists via File.Exists:
string defaultPath = "D:/temp/product.xml";
string alternativePath = "//letp.rf.servername.com/products/product.xml";
string path = File.Exists(defaultPath) ? defaultPath : alternativePath;
FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read);
If you want to check for another path if the second one is not found, you might want to use the following approach with an array of paths. With that, you are totally flexible how many paths you want to check.
string[] paths = new string[] { #"C:\first\path\product.xml", #"C:\second\path\product.xml", #"C:\third\path\product.xml"};
string path = paths.FirstOrDefault(p => File.Exists(p));
if(path == null)
{
Console.WriteLine("None of the files exists!");
}
else
{
FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read);
}
Just use try and catch and loop:
foreach (var file in files)
{
try
{
FileStream fs = new FileStream(file , FileMode.Open, FileAccess.Read);
}
catch(Exception ex)
{}
}

Odd file does not exist error

I'm running into an error that I can't catch and it should not be there.
if (System.IO.File.Exists (PathToMyFile))
{
try{
FileStream fs = new FileStream(PathToMyFile, FileMode.Open, FileAccess.Read);
BinaryReader br = new BinaryReader(fs);
Byte[] bytes = br.ReadBytes((Int32)fs.Length);
br.Close();
fs.Close();
myFile =Convert.ToBase64String (bytes) ;
}
catch{}
}
For some reason , sometimes I get a exception error that the file does not exist when It most definitely is there. The very first "If statement" even says it is there yet when trying to open the file I sometimes get a massive app crash that the catch does not "catch" .
Like I said, it's a random error, most of the time the code is perfect but the odd occasion seems to throw an error that the app stops working .
First thing is to make sure you close the file\stream
So you can call fs.Close() or using
if (File.Exists(pathToMyFile))
{
try
{
using (var fs = new FileStream(pathToMyFile, FileMode.Open, FileAccess.Read))
{
BinaryReader br = new BinaryReader(fs);
Byte[] bytes = br.ReadBytes((Int32) fs.Length);
br.Close();
fs.Close();
myFile = Convert.ToBase64String(bytes);
}
}
catch
{
// Log exception
}
}
Second, if you need to read the file as string, simply use
if (File.Exists(pathToMyFile))
{
try
{
myFile = File.ReadAllText(pathToMyFile);
}
catch
{
// Log exception
}
}

Compression level for binary serialized objects as file

In my application I have a rather large object created from some XML files. The xml file sizes something like 30MB, and my binary serialized object from this xml file will be like 8~9MB. Funny thing is if I compress this binary file with e.g. WinRar, it will be just 1~2MB.
Is there a way to increase compression level of the object itself? Or should I use another level of compression by manually write code for zipping the file after saving or unzip before loading back into the program?
In case, this is the code I use to save my object as file:
public static bool SaveProject(Project proj, string pathAndName)
{
bool success = true;
proj.FileVersion = CurrentFileVersion;
try
{
IFormatter formatter = new BinaryFormatter();
Stream stream = new FileStream(pathAndName, FileMode.Create, FileAccess.Write, FileShare.None);
formatter.Serialize(stream, proj);
stream.Close();
}
catch (Exception e)
{
MessageBox.Show("Can not save project!" + Environment.NewLine + "Reason: ", "Error",
MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
success = false;
}
return success;
}
UPDATE
I tried to change my code by adding a GZIPSTREAM but it seems that it does not do anything! Or maybe my implementation is wrong?
public static bool SaveProject(Project proj, string pathAndName)
{
bool success = true;
proj.FileVersion = CurrentFileVersion;
try
{
IFormatter formatter = new BinaryFormatter();
var stream = new FileStream(pathAndName, FileMode.Create, FileAccess.Write, FileShare.None);
var gZipStream = new GZipStream(stream, CompressionMode.Compress);
formatter.Serialize(stream, proj);
stream.Close();
gZipStream.Close();
}
catch (Exception e)
{
MessageBox.Show("Can not save project!" + Environment.NewLine + "Reason: ", "Error",
MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
success = false;
}
return success;
}
public static Project LoadProject(string path)
{
IFormatter formatter = new BinaryFormatter();
Stream stream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read);
var gZipStream = new GZipStream(stream, CompressionMode.Decompress);
var obj = (Project)formatter.Deserialize(gZipStream);
stream.Close();
gZipStream.Close();
if (obj.FileVersion != CurrentFileVersion)
{
throw new InvalidFileVersionException("File version belongs to an older version of the program.");
}
return obj;
}
Wrap your FileStream in a DeflateStream with CompressionMode.Compress - pass that to the serializer. Then to deserialize, wrap a FileStream in a DeflateStream with CompressionMode.Decompress.
Note that instead of calling Close explicitly, you should use a using statement, e.g.
using (FileStream fileStream = ...)
using (DeflateStream deflateStream = new DeflateStream(fileStream,
CompressionMode.Compress))
{
formatter.Serialize(deflateStream, proj);
}
You can use GZipStream in the same way - try both to see which tends to give you better compression (or better performance, if you care about that).
Note how this approach separates the serialization aspect from the compression aspect, composing the two while keeping good separation of concerns. The serialization code just writes to a stream without caring what happens to the data, and the compression code just compresses what it's given without caring what the data means.

Why is my GZipStream not writeable?

I have some GZ compressed resources in my program and I need to be able to write them out to temporary files for use. I wrote the following function to write the files out and return true on success or false on failure. In addition, I've put a try/catch in there which shows a MessageBox in the event of an error:
private static bool extractCompressedResource(byte[] resource, string path)
{
try
{
using (MemoryStream ms = new MemoryStream(resource))
{
using (FileStream fs = new FileStream(path, FileMode.Create, FileAccess.ReadWrite))
{
using (GZipStream zs = new GZipStream(fs, CompressionMode.Decompress))
{
ms.CopyTo(zs); // Throws exception
zs.Close();
ms.Close();
}
}
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message); // Stream is not writeable
return false;
}
return true;
}
I've put a comment on the line which throws the exception. If I put a breakpoint on that line and take a look inside the GZipStream then I can see that it's not writeable (which is what's causing the problem).
Am I doing something wrong, or is this a limitation of the GZipStream class?
You are plumbing the pipes the wrong way. Fix:
using (FileStream fs = new FileStream(path, FileMode.Create, FileAccess.ReadWrite))
using (MemoryStream ms = new MemoryStream(resource))
using (GZipStream zs = new GZipStream(ms, CompressionMode.Decompress))
{
zs.CopyTo(fs);
}

GZipStream zip file invalid or corrupted

I have a problem when opening a zip file. I am using this code to zip the file:
public static string Zip_File(string soruce , string target)
{
try
{
byte[] bufferWrite;
using (FileStream fsSource = new FileStream(soruce, FileMode.Open, FileAccess.Read, FileShare.Read))
{
bufferWrite = new byte[fsSource.Length];
fsSource.Read(bufferWrite, 0, bufferWrite.Length);
using (FileStream fsDest = new FileStream(target, FileMode.OpenOrCreate, FileAccess.Write))
{
using (GZipStream gzCompressed = new GZipStream(fsDest, CompressionMode.Compress, true))
{
gzCompressed.Write(bufferWrite, 0, bufferWrite.Length);
bufferWrite = null;
fsSource.Close();
gzCompressed.Close();
fsDest.Close();
}
}
}
return "success";
}
catch (Exception ex)
{
return ex.Message;
}
}
When I call this function, I am receiving "success" message, but I can't open the zip file. This is my function call code:
ZipFiles.Zip_File(#"C:\Documents and Settings\ccspl\Desktop\IntegrityDVR.mdb", #"C:\Documents and Settings\ccspl\Desktop\a.zip")
This is the error message I receive:
the compressed(folder) is invalid or corrupted
GZipStream does not create .zip files. It creates .gz files. If you need to create .zip files, you should use something like SharpZipLib.
but, wait a minute, GZipStream doesn't create zip file, it creates gzip files as I know, Zipping files using GZipStream should help
Why not use SharpZipLib? It makes this a lot easier.
sample code for DotNetZip, an open source zip library.
public static string ZipFile(String source, String target)
{
try
{
using (ZipFile zip = new ZipFile()
{
zip.AddFile(source);
zip.Save(target);
}
return "success";
}
catch {}
return "failure";
}

Categories