4

I am using the code below to save a posted file to a server, but that file is being read continually and need to use FileShare.ReadWrite so I don't get a locked error.

 httpRequest.Files[0].SaveAs(filePath);

Below is my reading method, how can I accomplish this with the HttpPosted file is the right way with the best performance.

using (var fileStream = new FileStream(
                   fileLocation,
                   FileMode.Open,
                   FileAccess.Read,
                   FileShare.ReadWrite))
                {
                    using (var streamReader = new StreamReader(fileStream))
                    {
                        xDocument = XDocument.Parse(streamReader.ReadToEnd());
                    }
                }

Is this my best option?

 using (var memoryStream = new MemoryStream())
                {
                    httpRequest.Files[0].InputStream.CopyTo(memoryStream);
                    var bytes = memoryStream.ToArray();

                    using (var fs = File.Open(filePath, FileMode.OpenOrCreate, FileAccess.Write, FileShare.ReadWrite))
                    {
                        fs.Write(bytes, 0, bytes.Length);
                    }
                }
Mike Flynn
  • 22,342
  • 54
  • 182
  • 341
  • 1
    I know @efaruk now deleted answer wasn't the question, but is it the answer? Here is a better way to check if the file **is actually locked**: https://stackoverflow.com/a/11060322/495455 - I've got roughly the same code as you, and I tested it. If you read many of the comments, a file locking issue and performance could exists at the slightest of race conditions. I'd opt for the Polly .Net Framework with built-in fault handling and tolerance... – Jeremy Thompson Jun 03 '19 at 00:01
  • My question isn’t about that but doing it from the HTTPPosted file. – Mike Flynn Jun 03 '19 at 00:49
  • 1
    How so? Fundamentally you're writing to disk and you're reading from disk at the same file. You get a file locked error. You mention the `FileAccess` and `FileShare` flags to get around that and then its about performance, as I mentioned I've got the same code as you. It performs well. So what's the question? **Use FileShare.ReadWrite with HttpPostedFile in ASP.NET MVC** - you would have to use memory, MQ as @efaruk mentioned or a dB. Let me double check the doco `HttpPostedFile` doesn't give you that control. – Jeremy Thompson Jun 03 '19 at 01:26
  • I knew it didn’t, that is why I asked for an alternative FROM the httpposted file and even gave an example. – Mike Flynn Jun 03 '19 at 01:30
  • 1
    No, it doesn't support it: https://learn.microsoft.com/en-us/dotnet/api/system.web.httppostedfile.saveas?view=netframework-4.8 - when the file is being uploaded signal not to read from the file. Or make the file upload a 2 step process where they upload it to a DataGrid and then click a button to process the data which would merge/upsert the new/changed values. – Jeremy Thompson Jun 03 '19 at 01:31

1 Answers1

2

Proplem:

  • You want a "Write:Once, Read:Many" Lock

Assumptions :

  • File is small (average write opration is 5000 ms)
  • No other write or read oprations (Only one programe with 2 function)
  • You read the file a lot more than you write to it

Solution


    using System;
    using System.IO;
    using System.Threading;
    using System.Web.Mvc;

    namespace stackoverflow_56307594.Controllers
    {

        public class HomeController : Controller
        {

            public ActionResult A()
            {
                readFile();
                return View();
            }

            public ActionResult B()
            {
                writeFile();
                return View();
            }


            private static object writeLock = new Object();

            private void readFile()
            {
                while (!Monitor.TryEnter(writeLock, 5000)) ; //wait 5000 ms for the writeLock (serializing access)
                using (var stream = new FileStream("filePath", FileMode.Open, FileAccess.Read, FileShare.Read))
                using (var reader = new StreamReader(stream))
                {
                    // active read
                    // xDocument = XDocument.Parse(streamReader.ReadToEnd());

                }
            }
            private void writeFile()
            {
                lock (writeLock)
                {
                    FileStream stream = null;
                    while (stream == null) //wait for the active read
                    {
                        try
                        {
                            stream = new FileStream("filePath", FileMode.Open, FileAccess.ReadWrite, FileShare.None);
                        }
                        catch (IOException)
                        {
                        // will fail if active read becase FileShare.None   while (stream == null) will  wait
                    }
                }
                    Request.Files[0].InputStream.CopyTo(stream);
                }// unlock
            }

        }
    }

Note :

  1. I did not test load or simply test the solution on a webserver
  2. I only tested it on paper

enter image description here

Refs:

locking - How long will a C# lock wait, and what if the code crashes during the lock? - Stack Overflow

c# - Deleting files in use - Stack Overflow

multithreading - Is there a way to detect if an object is locked? - Stack Overflow

Implementing Singleton in C# | Microsoft Docs

c# - Using the same lock for multiple methods - Stack Overflow

c# - Write-Once, Read-Many Lock - Stack Overflow

c# lock write once read many - Google Search

FileShare Enum (System.IO) | Microsoft Docs

Mohamed Elrashid
  • 8,125
  • 6
  • 31
  • 46