Michael
Michael

Reputation: 1010

How to change a static file in a physical file provider before delivery?

I have different ASP.NET Core (non MVC) applications, which serve multiple static Angular applications.

I use the PhysicalFileProvider class do so, which works fine.

// This works fine!
var fileProvider = new PhysicalFileProvider(Path.Combine(Directory.GetCurrentDirectory(), "NG", "UI", "Login"));
var options = new FileServerOptions
{
    FileProvider = fileProvider,
    RequestPath = "/ng/ui/login",
    EnableDirectoryBrowsing = true
};

However, before delivering these static files to the client, I need to change some of these files in the RAM, without changing them physically on the disk.

I already wrote my own implementation of a physical file provider, but I don't understand at which point of time or where the files are actually read from the disk.

PhysicalFileProvider has basically just two or three functions, which seem to deliver only informations about files, but not the actual files as bytes.

// These two functions of PhysicalFileProvider only deliver informations, but not the files? Am I 
wrong?

IDirectoryContents GetDirectoryContents(string subpath)

IFileInfo GetFileInfo(string subpath)

My question is: What kind of middleware needs to be hooked in the ASP.NET service pipeline, to change a static file (let's say a simple text file) in the RAM before delivery?

Thanks for any hints!

Upvotes: 1

Views: 87

Answers (1)

Guru Stron
Guru Stron

Reputation: 141565

Personally I would go with approach of serving the files via "ordinary" action/handler (probably with catch-all route) or probably some middleware (for example a one "intercepting" the actual requests for files.) but if you want to try solving this with file provider then you need to work with two interfaces IFileProvider and IFileInfo:

class MyFileProvider : IFileProvider
{
    private readonly string _root;
    private readonly PhysicalFileProvider _provider;

    public MyFileProvider(string root)
    {
        _root = root;
        _provider = new PhysicalFileProvider(_root);
    }
    public IDirectoryContents GetDirectoryContents(string subpath)
        => _provider.GetDirectoryContents(subpath);

    public IFileInfo GetFileInfo(string subpath)
    {
        var fileInfo = _provider.GetFileInfo(subpath);
        return new MyFileInfo(fileInfo);
    }

    public IChangeToken Watch(string filter)
    {
        throw new NotImplementedException();
    }
}
class MyFileInfo : IFileInfo
{
    private static readonly ReadOnlyMemory<byte> ToAppend 
        = new(" Hello World!"u8.ToArray());
    private readonly IFileInfo _fileInfo;

    public MyFileInfo(IFileInfo fileInfo) => _fileInfo = fileInfo;

    public bool Exists => _fileInfo.Exists;
    public bool IsDirectory => _fileInfo.IsDirectory;
    public string Name => _fileInfo.Name;

    // magic is done in the following parts:

    // original file modification:
    public Stream CreateReadStream()
    {
        var readStream = _fileInfo.CreateReadStream();
        var memoryStream = new MemoryStream();
        readStream.CopyTo(memoryStream);
        memoryStream.Write(ToAppend.Span);
        memoryStream.Seek(0, SeekOrigin.Begin);
        return memoryStream;
    }

    // disables caching like behavior 
    public DateTimeOffset LastModified  => DateTimeOffset.UtcNow;
    // needed to serve correct length of the file
    public long Length  => _fileInfo.Length + ToAppend.Length;
    // otherwise will try serving file from disk as is as far as I can see
    public string? PhysicalPath  => null;
}

And register it:

var myFileProvider = new MyFileProvider(
    Path.Combine(Directory.GetCurrentDirectory(), @"MyStaticFiles"));
app.UseFileServer(new FileServerOptions()
{
    FileProvider = myFileProvider,
    RequestPath = new PathString("/StaticFiles"),
    EnableDirectoryBrowsing = true
});

This is a sample implementation just for demonstration purposes which is far from perfect - it appends " Hello World!" to the served files, for example for example file containing text Test result is:

enter image description here

Upvotes: 4

Related Questions