How do I Open A File Asynchronously in C# - c#

What is the best way to execute file opening asynchronously even before copying begins. The exception I get is that the file is not accessible because its being used by another process.
try
{
using (var sourceStream = new FileStream(pathToDownloadedMedia, FileMode.Open, FileAccess.Read, FileShare.None, bufferSize: 4096, useAsync: true))
{
using (var destinationStream = File.Create(pathToProcessedMedia))
{
await sourceStream.CopyToAsync(destinationStream);
}
}
}
catch (Exception)
{
throw new ApplicationException();
}
I Tried an altenative as shown below but with no success. The same error prevails.
using (var sourceStream = await Task.Factory.StartNew(() => File.Open(pathToDownloadedMedia, FileMode.Open, FileAccess.Read, FileShare.None)))
{
using (var destinationStream = File.Create(pathToProcessedMedia))
{
await sourceStream.CopyToAsync(destinationStream);
}
}

No; different threads attempting to open the file at the same time will interfere with each other. You could lock a shared object to force the threads to run one at a time.
Or just use the built-in .NET tracing functionality; you can configure your app.config to write to a file.

I found the solution. Simply switching to Fileshare.ReadWrite solved the problem
using (var sourceStream = await Task.Factory.StartNew(() => File.Open(pathToDownloadedMedia, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)))
{
using (var destinationStream = File.Create(pathToProcessedMedia))
{
await sourceStream.CopyToAsync(destinationStream);
}
}
I modified the solution by taking away the Task being created. It works well now.
using (var sourceStream = File.Open(pathToDownloadedMedia, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
{
using (var destinationStream = File.Create(pathToProcessedMedia))
{
await sourceStream.CopyToAsync(destinationStream);
}
}

Related

Changes are not save when using MemoryStream instead of FileStream

I have a DLL with embedded Excel file. The goal is to retrieve this file and create some entry (Empty_File.txt in this example). When I'm using FileStream - the entry gets created, but when I'm using MemoryStream - entry isn't created.
var filePath = "C:\\Temp\\Test2.xlsx";
var asm = typeof(Program).Assembly;
var asmName = asm.GetName().Name;
using var resourceStream = asm.GetManifestResourceStream($"{asmName}.Resources.Template.xlsx");
if (File.Exists(filePath)) File.Delete(filePath);
await UseFileStream(resourceStream, filePath);
// or
await UseMemoryStream(resourceStream, filePath);
static async Task UseMemoryStream(Stream resourceStream, string filePath)
{
using (var ms = new MemoryStream())
{
await resourceStream.CopyToAsync(ms);
using (var zip = new ZipArchive(ms, ZipArchiveMode.Update))
{
zip.CreateEntry("Empty_File.txt");
using (var fs = CreateFileStream(filePath))
{
ms.Seek(0L, SeekOrigin.Begin);
await ms.CopyToAsync(fs);
}
}
}
}
static async Task UseFileStream(Stream resourceStream, string filePath)
{
using var fs = CreateFileStream(filePath);
await resourceStream.CopyToAsync(fs);
using var zip = new ZipArchive(fs, ZipArchiveMode.Update);
zip.CreateEntry("Empty_File.txt");
}
static FileStream CreateFileStream(string filePath) =>
new FileStream(filePath, new FileStreamOptions
{
Access = FileAccess.ReadWrite,
Mode = FileMode.Create,
Share = FileShare.None
});
Per the docs for ZipArchive.Dispose:
This method finishes writing the archive and releases all resources used by the ZipArchive object. Unless you construct the object by using the ZipArchive(Stream, ZipArchiveMode, Boolean) constructor overload and set its leaveOpen parameter to true, all underlying streams are closed and no longer available for subsequent write operations.
You are currently writing to the file stream before this happens, so the changes to the zip file haven't been written yet.
You'll also note from this that the underlying MemoryStream will be disposed unless you specify leaveOpen: true in the constructor, which would prevent you copying to the file afterwards.
So putting both of these together:
static async Task UseMemoryStream(Stream resourceStream, string filePath)
{
using (var ms = new MemoryStream())
{
await resourceStream.CopyToAsync(ms);
using (var zip = new ZipArchive(ms, ZipArchiveMode.Update, leaveOpen: true))
{
zip.CreateEntry("Empty_File.txt");
}
using (var fs = CreateFileStream(filePath))
{
ms.Seek(0L, SeekOrigin.Begin);
await ms.CopyToAsync(fs);
}
}
}

C# Bitmap Invalid Parameter

Here is what I am trying to do: Take a folder full of images, perform an optimization on them, and store them in the same file.
I get the error:
System.ArgumentException
Message = Parameter is not valid.
From line:
using (var bitmap = new Bitmap(image))
I think this has something to do with the file being open, and blocking access but I'm not sure. I know for a fact that the filepath I'm using is correct, and that it is a folder filled with images. Can anyone help me?
string[] folder = Directory.GetFiles(GetSourceDirectory());
Parallel.ForEach(folder, (file) =>
{
using (var fileStream = File.Open(file, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite))
{
var image = fileStream.Name;
Console.WriteLine(image);
Console.ReadLine();
using (var bitmap = new Bitmap(image))
{
using (var quantized = quantizer.QuantizeImage(bitmap, 1, 1))
{
var blob = container.GetBlockBlobReference(fileStream.Name);
try
{
quantized.Save(fileStream, ImageFormat.Png);
}
catch (ArgumentException e)
{
Console.WriteLine(e.Message);
Console.ReadLine();
Console.WriteLine();
throw;
}
blob.UploadFromStreamAsync(fileStream).Wait();
}
}
}
});
This is because you are using the filestream to read and write at the same time.
If you change the File.Open to
using (var fileStream = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
it will allow you to do
using (var bitmap = new Bitmap(image))
But then obviously you will come unstuck later when you try to write using the fileStream variable.
So I think you will have to change it so you don't have the nested using statements.
FileStream.Name gives you the file name only, like "image.jpg", have you tried using (var bitmap = new Bitmap(fileStream))?

Read file that's already used by another process

I have a C# app that tries to read a log file which is being written to by another app. When I try to read the file, I get IOException
"The process cannot access the file ... because it is being used by
another process."
What I tried using so far are the following, but none of them fix the problem
var log = File.ReadAllText(logPath);
var stream = new FileStream(logPath, FileMode.Open);
using (var stream = File.Open(logPath, FileMode.Open))
{
}
try this:
FileStream logFileStream = new FileStream("c:\test.txt", FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
StreamReader logFileReader = new StreamReader(logFileStream);
while (!logFileReader.EndOfStream)
{
string line = logFileReader.ReadLine();
// Your code here
}
// Clean up
logFileReader.Close();
logFileStream.Close();
edited with MethodMan's suggestions
using(FileStream logFileStream = new FileStream(#"c:\test.txt", FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
{
using(StreamReader logFileReader = new StreamReader(logFileStream))
{
string text = logFileReader.ReadToEnd();
// Your code..
}
}
You can do nothing, if the "another app" does not use Share.Read while creating/opening the file.

Renaming files and filesharing (filestream)

I'm using a filestream to access a file (making an md5 ComputeHash). If I attempt to rename the file during this time (which fails as the file is being accessed). So far so good, but when I then try to open the file anew after the original filestream is closed I get the info that the file is open in another process.
Code:
using (Stream fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) {
MD5 md5 = MD5.Create();
byte[] mymd5computed = md5.ComputeHash(fileStream);
......
}
Thread.Sleep(50);
Thread a = new Thread (()=>{(FileStream sourceStream = File.Open(filePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)){....} });
Like I said if while during the computation of the MD5 I try to rename the file I get the info that the file is still locked.
The lock on a file sometimes isn't released right away when you close your stream, so there are some other solutions you can use to wait until you can access the file again. One of them is explained here: http://www.codeproject.com/Tips/164428/C-FileStream-Lock-How-to-wait-for-a-file-to-get-re.
Recap:
public static void Lock(string path, Action<FileStream> action) {
var autoResetEvent = new AutoResetEvent(false);
while(true)
{
try
{
using (var file = File.Open(path,
FileMode.OpenOrCreate,
FileAccess.ReadWrite,
FileShare.Write))
{
action(file);
break;
}
}
catch (IOException)
{
var fileSystemWatcher =
new FileSystemWatcher(Path.GetDirectoryName(path))
{
EnableRaisingEvents = true
};
fileSystemWatcher.Changed +=
(o, e) =>
{
if(Path.GetFullPath(e.FullPath) == Path.GetFullPath(path))
{
autoResetEvent.Set();
}
};
autoResetEvent.WaitOne();
}
}
}
Sample use:
Lock(#"c:\file",
(f) =>
{
try
{
f.Write(buf, 0, buf.Length);
}
catch(IOException ioe)
{
// handle IOException
}
});
Hope it helps! :)

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);
}

Categories