Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

VirtualFileResult file not found when file exists

Maybe I'm not understanding what a VirtualFileResult is, but I'm not sure why it's throwing a FileNotFoundException when the file exists. Here's the code:

if (System.IO.File.Exists(fileName))
{
    FileInfo info = new FileInfo(fileName);

    return File(fileName, FileUtility.GetContentType(fileName), info.Name);
}

(Edit: FileUtility.GetContentType (correctly) returns the Mime type of the file, which in my case is "application/pdf".)

Here's the exception I get: (I removed the file path)

System.IO.FileNotFoundException: Could not find file: [file path removed]
File name: '[file path removed]'
   at Microsoft.AspNet.Mvc.VirtualFileResult.<WriteFileAsync>d__11.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.GetResult()
   at Microsoft.AspNet.Mvc.Controllers.FilterActionInvoker.<InvokeResultAsync>d__56.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.GetResult()
   at Microsoft.AspNet.Mvc.Controllers.FilterActionInvoker.<InvokeResultFilterAsync>d__55.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at Microsoft.AspNet.Mvc.Controllers.FilterActionInvoker.<InvokeAllResultFiltersAsync>d__54.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.GetResult()
   at Microsoft.AspNet.Mvc.Controllers.FilterActionInvoker.<InvokeResourceFilterAsync>d__49.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at Microsoft.AspNet.Mvc.Controllers.FilterActionInvoker.<InvokeAsync>d__44.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.GetResult()
   at Microsoft.AspNet.Mvc.Infrastructure.MvcRouteHandler.<RouteAsync>d__6.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.GetResult()
   at Microsoft.AspNet.Routing.Template.TemplateRoute.<RouteAsync>d__27.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.GetResult()
   at Microsoft.AspNet.Routing.RouteCollection.<RouteAsync>d__9.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.GetResult()
   at Microsoft.AspNet.Builder.RouterMiddleware.<Invoke>d__4.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.GetResult()
   at Microsoft.AspNet.IISPlatformHandler.IISPlatformHandlerMiddleware.<Invoke>d__8.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.GetResult()
   at Microsoft.AspNet.Diagnostics.StatusCodePagesMiddleware.<Invoke>d__3.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.GetResult()
   at Microsoft.AspNet.Diagnostics.DeveloperExceptionPageMiddleware.<Invoke>d__7.MoveNext()

It's seemingly bugging out even though an explicit check was done for whether the file exists or not. I can also manually confirm the file exists.

Maybe I'm not understanding what a VirtualFileResult is (File method returns this). On the documentation here it doesn't really describe what it is.

I'm using ASP.NET 5 RC1.

like image 356
painiyff Avatar asked Feb 26 '16 17:02

painiyff


3 Answers

The method you're looking for is PhysicalFile():

return PhysicalFile(fileName, FileUtility.GetContentType(fileName), info.Name);

Beware, don't use a caller-supplied path with this.

like image 109
Nicholas Blumhardt Avatar answered Oct 03 '22 20:10

Nicholas Blumhardt


The VirtualFileResult class needs the virtual path to the file in its constructor.
This means you need to provide the path relative to the wwwroot folder because it use HostingEnvironment's WebRootFileProvider.

    private IFileProvider GetFileProvider(IServiceProvider requestServices)
    {
        if (FileProvider != null)
        {
            return FileProvider;
        }

        var hostingEnvironment = requestServices.GetService<IHostingEnvironment>();
        FileProvider = hostingEnvironment.WebRootFileProvider;

        return FileProvider;
    }
}

Check the code on github

like image 41
agua from mars Avatar answered Oct 03 '22 21:10

agua from mars


No luck with File("index.html") or File("~/index.html"), Virtualfileresult throws NotFound.

My solution was to use Microsoft.AspNetCore.Mvc.ControllerBase method File(Stream fileStream...) :

public virtual FileStreamResult File(Stream fileStream, 
       string contentType, string fileDownloadName);

Index controller sample:

 using System.IO;
 using Microsoft.AspNetCore.Mvc;
 using Microsoft.Extensions.Logging;

 public class IndexController : Controller
 {
    ILoggerFactory Logger;

    public IndexController(ILoggerFactory loggerFactory)
    {
        Logger = loggerFactory;
    }

    static string sep = Path.DirectorySeparatorChar.ToString();

    [HttpGet("/index")]
    [HttpGet("/")]
    public IActionResult Index()
    {
        var log = Logger.CreateLogger("trace");

        var path = $"{Startup.RootPath}{sep}index.html";
        log.LogWarning(path);
        Stream fileStream  = new System.IO.FileStream(path, FileMode.Open);

        return File(fileStream, "text/html");
    }
}

// AspnetCore version 1.1.1 :
// <TargetFramework>netcoreapp1.1</TargetFramework>
// <PackageReference Include="Microsoft.AspNetCore.StaticFiles" Version="1.1.1" />
like image 20
Andrew Avatar answered Oct 03 '22 21:10

Andrew