Search code examples
asp.netazureazure-virtual-machinesmbazure-storage-files

Writing files from Asp.Net to Azure File Storage fails


I have an Azure VM with IIS setup, which hosts my ASP.Net website.

I want to be able to read and write documents/files from a network share (Azure file storage) by using the normal windows class libraries (System.IO). I do not want to use the Azure storage client libraries nor the Azure Storage Rest API.

I have mounted the share and persisted my credentials as detailed in this article.

After mounting the drive and persisting my credentials, I point my website to make use of my shared drive for all document management related functions. Downloading a file works perfectly. I receive an error, however, as soon as I try to create a folder or write a document to the network share.

The error I receive is:

Could not find a part of the path 'F:[RestOfThePathHere]'. Stack Trace:

[DirectoryNotFoundException: Could not find a part of the path 'F:\Test\MyThing\Clients\TheirStuff'.]
System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath) +338 System.IO.Directory.InternalCreateDirectory(String fullPath, String path, Object dirSecurityObj, Boolean checkHost) +1578
System.IO.Directory.InternalCreateDirectoryHelper(String path, Boolean checkHost) +213
MyStuff.FileHandling.FileExtenders.SaveToFile(Byte[] ByteArray, String Directory, String FileName) +116
MyStuff.TheirStuff.BLL.TheirStuffTestClient.RunEnquiry(ClientBase client, String resultType) +3776
MyStuff.MyThing.UI.Views.ClientController.CreditCheck(Int64 id) +42
lambda_method(Closure , ControllerBase , Object[] ) +114
System.Web.Mvc.ReflectedActionDescriptor.Execute(ControllerContext controllerContext, IDictionary2 parameters) +242
System.Web.Mvc.ControllerActionInvoker.InvokeActionMethod(ControllerContext controllerContext, ActionDescriptor actionDescriptor, IDictionary
2 parameters) +39
System.Web.Mvc.Async.AsyncControllerActionInvoker.b__39(IAsyncResult asyncResult, ActionInvocation innerInvokeState) +12
System.Web.Mvc.Async.WrappedAsyncResult2.CallEndDelegate(IAsyncResult asyncResult) +139
System.Web.Mvc.Async.AsyncInvocationWithFilters.<InvokeActionMethodFilterAsynchronouslyRecursive>b__3d() +112 System.Web.Mvc.Async.<>c__DisplayClass46.<InvokeActionMethodFilterAsynchronouslyRecursive>b__3f() +452 System.Web.Mvc.Async.<>c__DisplayClass33.<BeginInvokeActionMethodWithFilters>b__32(IAsyncResult asyncResult) +15
System.Web.Mvc.Async.<>c__DisplayClass2b.<BeginInvokeAction>b__1c() +37 System.Web.Mvc.Async.<>c__DisplayClass21.<BeginInvokeAction>b__1e(IAsyncResult asyncResult) +241
System.Web.Mvc.Controller.<BeginExecuteCore>b__1d(IAsyncResult asyncResult, ExecuteCoreState innerState) +29
System.Web.Mvc.Async.WrappedAsyncVoid
1.CallEndDelegate(IAsyncResult asyncResult) +111
System.Web.Mvc.Controller.EndExecuteCore(IAsyncResult asyncResult) +53 System.Web.Mvc.Async.WrappedAsyncVoid1.CallEndDelegate(IAsyncResult asyncResult) +19
System.Web.Mvc.MvcHandler.<BeginProcessRequest>b__5(IAsyncResult asyncResult, ProcessRequestState innerState) +51
System.Web.Mvc.Async.WrappedAsyncVoid
1.CallEndDelegate(IAsyncResult asyncResult) +111
System.Web.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() +606 System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) +288

OR, when I use UNC addressing

The network path was not found. StackTrace: [IOException: The network path was not found. ]
System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath) +14382132 System.IO.Directory.InternalCreateDirectory(String fullPath, String path, Object dirSecurityObj, Boolean checkHost) +1578 System.IO.Directory.InternalCreateDirectoryHelper(String path, Boolean checkHost) +213
MyStuff.FileHandling.FileExtenders.SaveToFile(Byte[] ByteArray, String Directory, String FileName) +116
MyStuff.TheirStuff.BLL.TheirStuffTestClient.RunEnquiry(ClientBase client, String resultType) +3776
MyStuff.MyThing.UI.Views.ClientController.CreditCheck(Int64 id) +42
lambda_method(Closure , ControllerBase , Object[] ) +114
System.Web.Mvc.ReflectedActionDescriptor.Execute(ControllerContext controllerContext, IDictionary2 parameters) +242
System.Web.Mvc.ControllerActionInvoker.InvokeActionMethod(ControllerContext controllerContext, ActionDescriptor actionDescriptor, IDictionary
2 parameters) +39
System.Web.Mvc.Async.AsyncControllerActionInvoker.b__39(IAsyncResult asyncResult, ActionInvocation innerInvokeState) +12
System.Web.Mvc.Async.WrappedAsyncResult2.CallEndDelegate(IAsyncResult asyncResult) +139
System.Web.Mvc.Async.AsyncInvocationWithFilters.<InvokeActionMethodFilterAsynchronouslyRecursive>b__3d() +112 System.Web.Mvc.Async.<>c__DisplayClass46.<InvokeActionMethodFilterAsynchronouslyRecursive>b__3f() +452 System.Web.Mvc.Async.<>c__DisplayClass33.<BeginInvokeActionMethodWithFilters>b__32(IAsyncResult asyncResult) +15
System.Web.Mvc.Async.<>c__DisplayClass2b.<BeginInvokeAction>b__1c() +37 System.Web.Mvc.Async.<>c__DisplayClass21.<BeginInvokeAction>b__1e(IAsyncResult asyncResult) +241
System.Web.Mvc.Controller.<BeginExecuteCore>b__1d(IAsyncResult asyncResult, ExecuteCoreState innerState) +29
System.Web.Mvc.Async.WrappedAsyncVoid
1.CallEndDelegate(IAsyncResult asyncResult) +111
System.Web.Mvc.Controller.EndExecuteCore(IAsyncResult asyncResult) +53 System.Web.Mvc.Async.WrappedAsyncVoid1.CallEndDelegate(IAsyncResult asyncResult) +19
System.Web.Mvc.MvcHandler.<BeginProcessRequest>b__5(IAsyncResult asyncResult, ProcessRequestState innerState) +51
System.Web.Mvc.Async.WrappedAsyncVoid
1.CallEndDelegate(IAsyncResult asyncResult) +111
System.Web.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() +606 System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) +288

Update - My current code

MVC Controller Method

public ActionResult Upload(DocumentUploadVM VM, long ClientId, long ContextInstanceId, long DocumentClassId, long TaskId)
    {
            var documentClass = docMan.Get_DocumentClass(DocumentClassId);

            var errors = new List<string>();

            if (VM.File == null || VM.File.ContentLength == 0)
                errors.Add("You haven't selected a file to upload");
            else if (!documentClass.MimeTypeAllowed(VM.File.ContentType, docMan))
                errors.Add("The file type is forbidden");

            if (errors.Count == 0 && ModelState.IsValid)
            {
                var path = settings.GetSettingValueAsString("Document Management: Client Documents Root Path").TrimEnd('\\') + "\\Uploads\\";
                var pvtFileName = new Random().Next().ToString() + "." + VM.File.FileName.Split('.').Last();
                var targetURI = Path.Combine(path, pvtFileName);

                MemoryStream ms = new MemoryStream();
                VM.File.InputStream.CopyTo(ms);

                IFileHandler fh = FileHandlingFactory.Create();
                fh.SaveFile(ms, targetURI);

                docMan.Add_Document(
                    new FileItem
                    {
                        FilePathOnServer = path,
                        PublicFileName = VM.File.FileName,
                        PrivateFileName = pvtFileName,
                        MimeType = VM.File.ContentType
                    },
                    docMan.Get_ContextInstance(ContextInstanceId),
                    new List<DocumentClass> { documentClass });
            }
            else
            {
                TempData["errors"] = errors;
            }

            //TODO: In case of model errors, this call will suppress the errors.  Look to TempData / PRG pattern to maintain model state over redirect.
            return
              RedirectToAction("ListClientDocuments", new { ClientId = ClientId, TaskId = TaskId });
        }

IFileHandler.SaveFile implementation

public override void SaveFile(MemoryStream MemoryStream, string Fullpath)
    {
        var directory = Fullpath.Substring(0, Fullpath.LastIndexOf('\\')).TrimEnd('\\');
        var fileName = Fullpath.Replace(directory, "").Trim('\\');
        MemoryStream.ToArray()
        .SaveToFile(directory, fileName);
    }

PS - Please excuse the terrible code and blatant disregard for C# coding standards. I was a noob when I wrote this. However, this code has not yet given me any problems when used to address local file-storage.


Solution

  • I think you may want try create a local user that maps to the storage account user and then add this user to IIS_IUSRS group.

    Please refer to this blog for details http://blogs.iis.net/davidso/azurefile

    -Jason