VirtualFileResult file not found when file exists
Asked Answered
B

4

9

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.

Balzer answered 26/2, 2016 at 17:2 Comment(0)
D
15

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.

Deadandalive answered 31/10, 2018 at 3:3 Comment(1)
could you explain on your caller-supplied path?Dedifferentiation
S
6

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

Selden answered 26/2, 2016 at 22:52 Comment(0)
D
3

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" />
Darlinedarling answered 15/9, 2017 at 14:10 Comment(0)
C
1

Your VirtualFileResult must be a relative path from the application's wwwroot.

return File("~/foo.js","text/javascript")

is going to point to wwwroot/foo.js

Carlottacarlovingian answered 23/3, 2016 at 21:52 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.