I have virtual directories that point to some file servers in IIS. I learned that ASP.NET Core cannot "see" the virtual directories in IIS because it is running on Kestral.
I'm told the workaround is to use app.UseFileServer:
app.UseFileServer(new FileServerOptions
{
FileProvider = new PhysicalFileProvider(@"\\server\path"),
RequestPath = new PathString("/MyPath"),
EnableDirectoryBrowsing = true
});
I set that up, and it works when I type in /MyPath in the browser address bar. However, I want to be able to get the physical path while in a controller.
Something like:
//Server.Map path no longer exists in ASP.NET Core...
var filePath = Server.MapPath("/MyPath");
var fileName = "MyFile.txt";
System.IO.File.OpenRead(Path.Combine(filePath , fileName));
Is there a way I can get the physical path by providing the RequestPath I setup in app.UseFileServer?
The core of this problem is getting access to the correct IFileProvider (which points to the physical path of your directory) in your controller.
You could go about this by configuring these paths in a custom Service so you could access them anywhere:
I have some code to demonstrate how you could go about this:
First, create a custom service, which will hold your FileServerOptions that can be accessed by UseFileServer() or your controller. I opted for a List of them, so you can add multiple paths.
public interface IFileServerProvider
{
/// <summary>
/// Contains a list of FileServer options, a combination of virtual + physical paths we can access at any time
/// </summary>
IList<FileServerOptions> FileServerOptionsCollection { get; }
/// <summary>
/// Gets the IFileProvider to access a physical location by using its virtual path
/// </summary>
IFileProvider GetProvider(string virtualPath);
}
/// <summary>
/// Implements IFileServerProvider in a very simple way, for demonstration only
/// </summary>
public class FileServerProvider : IFileServerProvider
{
public FileServerProvider(IList<FileServerOptions> fileServerOptions)
{
FileServerOptionsCollection = fileServerOptions;
}
public IList<FileServerOptions> FileServerOptionsCollection { get; }
public IFileProvider GetProvider(string virtualPath)
{
var options = FileServerOptionsCollection.FirstOrDefault(e => e.RequestPath == virtualPath);
if (options != null)
return options.FileProvider;
throw new FileNotFoundException($"virtual path {virtualPath} is not registered in the fileserver provider");
}
}
Then, for convenience, you could also add an Extension method which would call the UseFileServer middleware with the options from the service above:
/// <summary>
/// Wrapper for UseFileServer to easily use the FileServerOptions registered in the IFileServerProvider
/// </summary>
public static class FileServerProviderExtensions
{
public static IApplicationBuilder UseFileServerProvider(this IApplicationBuilder application, IFileServerProvider fileServerprovider)
{
foreach (var option in fileServerprovider.FileServerOptionsCollection)
{
application.UseFileServer(option);
}
return application;
}
}
Now, you'd only have to register the IFileServerProvider with your chosen paths (1) and hand it over to the convenience method to actually use them (2).
(1) First, register the IFileServerProvider with your chosen paths as a Singleton service:
public void ConfigureServices(IServiceCollection services)
{
//Add our IFileServerProvider implementation as a singleton
services.AddSingleton<IFileServerProvider>(new FileServerProvider(
new List<FileServerOptions>
{
new FileServerOptions
{
FileProvider = new PhysicalFileProvider(@"D:\\somepath"),
RequestPath = new PathString("/OtherPath"),
EnableDirectoryBrowsing = true
},
new FileServerOptions
{
FileProvider = new PhysicalFileProvider(@"\\server\path"),
RequestPath = new PathString("/MyPath"),
EnableDirectoryBrowsing = true
}
}));
// Add framework services.
services.AddMvc();
}
(2) And then actually use them in UseFileServer by calling the extension method created earlier. Note the extra argument IFileServerProvider fileServerprovider
in the Configure method:
// Note: accessing the IFileServerProvider implemantion here by adding it
// in the arguments list!
public void Configure(IApplicationBuilder app, IFileServerProvider fileServerprovider)
{
//call convenience method which adds our FileServerOptions from
// the IFileServerProvider service
app.UseFileServerProvider(fileServerprovider);
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
}
Now, when you browse to the paths /MyPath or /OtherPath, you should get a directory listing as before.
But, because we have everything tidy in a Service it's now easier to get to these paths programmatically in a Controller:
public class HomeController : Controller
{
private IFileServerProvider _fileServerProvider;
public HomeController(IFileServerProvider fileServerProvider)
{
_fileServerProvider = fileServerProvider;
}
public IActionResult Index()
{
var fileProvider = _fileServerProvider.GetProvider("/MyPath");
var fileInfo = fileProvider.GetFileInfo("MyFile.txt");
using (var stream = System.IO.File.OpenRead(fileInfo.PhysicalPath))
{
string contents = new StreamReader(stream).ReadToEnd();
}
return View();
}
}
This code could probably be written in a more abstract and more adequately named version, but it demonstrates a working solution to your problem.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With