Skip to content

Commit

Permalink
Added BlazorFileProvider
Browse files Browse the repository at this point in the history
Updated README
Bumped version to 0.1.0-beta-4
  • Loading branch information
SQL-MisterMagoo committed Mar 19, 2019
1 parent ca4fa22 commit a6de699
Show file tree
Hide file tree
Showing 8 changed files with 227 additions and 4 deletions.
2 changes: 1 addition & 1 deletion Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@
<PropertyGroup>
<BlazorVersion>0.9.0-preview3-19154-02</BlazorVersion>
<AspNetCoreVersion>3.0.0-preview3-19153-02</AspNetCoreVersion>
<ReleaseVersion>0.1.0-beta-3</ReleaseVersion>
<ReleaseVersion>0.1.0-beta-4</ReleaseVersion>
</PropertyGroup>
</Project>
29 changes: 28 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ This is a component library that provides Blazor-style static file embedding for

Chanegelog:

#### Version 0.1.0-beta-4
- Add BlazorFileProvider : Static File Provider that serves embedded files from Blazor Libraries
- Add usage example to RazorComponentSample to serve files from BlazorComponentSample

#### Version 0.1.0-beta-3
- Restructure source folders
- Add ability to handle multiple component assemblies in one go
Expand All @@ -24,14 +28,16 @@ Projects in this repo:

This is the component library that provides all the functionality.

It is a netstandard component library (i.e. a netstandard2.0 library) with one c# code file.
It is a netstandard component library (i.e. a netstandard2.0 library).

## How to use this library

Add the nuget package BlazorEmbedLibrary

https://www.nuget.org/packages/BlazorEmbedLibrary/

#### Using the EmbeddedComponent to extract CSS/JS files

Add a *Using* and an *addTagHelper* to the __ViewImports file

```
Expand Down Expand Up @@ -92,6 +98,27 @@ List<System.Reflection.Assembly> Assemblies = new List<System.Reflection.Assembl
```

### Serve Static Files (images/data etc) using BlazorFileProvider

Available in 0.1.0-beta-4 is a new class that provides aspnetcore middleware to server embedded resources as static files in the pipeline.

To use this facility in a Razor Components project, add the nuget to your project and simply add the new BlazorFileProvider to your Configure method and pass it a list of Blazor Library assemblies.

```
app.UseStaticFiles();
app.UseStaticFiles(new StaticFileOptions()
{
FileProvider = new BlazorFileProvider(
new List<Assembly>()
{
typeof(BlazorComponentSample.Component1).Assembly
}
)
});
```

_Note: Currently, there is a restriction that this provider will not allow duplicate file names. This is because CSS files added via the EmbeddedComponent appear in the root of the application, so any images requested by a relative URL will need to come from the root path as well, which means there is no way to distinguish between files with the same name. If two libraries have a file with the same name, the first one found will be used._
## Sample Projects

I have included Blazored.Toast and a simple custom component in the Razorcomponents and Blazor Apps
Expand Down
2 changes: 1 addition & 1 deletion samples/BlazorEmbedContent/Properties/launchSettings.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
},
"applicationUrl": "http://localhost:55428/"
"applicationUrl": "http://localhost:5428/"
}
}
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
using Blazored.LocalStorage;
using Blazored.Toast.Services;
using BlazorEmbedLibrary;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using RazorComponentsSample.Server.Components;
using RazorComponentsSample.Server.Services;
using System.Collections.Generic;
using System.Reflection;

namespace RazorComponentsSample.Server
{
Expand Down Expand Up @@ -40,6 +43,10 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env)

//app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseStaticFiles(new StaticFileOptions()
{
FileProvider = new BlazorFileProvider(new List<Assembly>() { typeof(BlazorComponentSample.Component1).Assembly })
});

app.UseRouting(routes =>
{
Expand Down
34 changes: 34 additions & 0 deletions src/BlazorEmbedLibrary/BlazorDirectoryContents.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using Microsoft.Extensions.FileProviders;

namespace BlazorEmbedLibrary
{
internal class BlazorDirectoryContents : IDirectoryContents
{
private readonly Assembly assembly;

public BlazorDirectoryContents(Assembly assembly)
{
this.assembly = assembly;
}
public bool Exists => true;

public IEnumerator<IFileInfo> GetEnumerator()
{
var resources = assembly.GetManifestResourceNames();
foreach (var item in resources)
{
var name = Path.GetFileName(item.Replace(":", "/"));
yield return new BlazorFileInfo(item, name, assembly);
}
}

IEnumerator IEnumerable.GetEnumerator()
{
throw new System.NotImplementedException();
}
}
}
3 changes: 2 additions & 1 deletion src/BlazorEmbedLibrary/BlazorEmbedLibrary.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
https://dotnet.myget.org/f/blazor-dev/api/v3/index.json;
</RestoreAdditionalProjectSources>
<LangVersion>latest</LangVersion>
<BlazorLinkOnBuild>true</BlazorLinkOnBuild>
<BlazorLinkOnBuild>false</BlazorLinkOnBuild>
<Authors>Mister Magoo</Authors>
<Company>MM</Company>
<Description>A Blazor Component that brings Blazor-style static content to Razor Components</Description>
Expand All @@ -26,6 +26,7 @@
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Blazor" Version="$(BlazorVersion)" />
<PackageReference Include="Microsoft.AspNetCore.Blazor.Build" Version="$(BlazorVersion)" PrivateAssets="all" />
<PackageReference Include="Microsoft.Extensions.FileProviders.Abstractions" Version="2.2.0" />

<DotNetCliToolReference Include="Microsoft.AspNetCore.Blazor.Cli" Version="$(BlazorVersion)" />
</ItemGroup>
Expand Down
39 changes: 39 additions & 0 deletions src/BlazorEmbedLibrary/BlazorFileInfo.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using Microsoft.Extensions.FileProviders;

namespace BlazorEmbedLibrary
{
internal class BlazorFileInfo : IFileInfo
{
private readonly string subpath;
private readonly string filename;
private readonly Assembly assembly;
public BlazorFileInfo(string path, string name, Assembly assembly)
{
this.subpath = path;
this.filename = name;
this.assembly = assembly;
}

public bool Exists => true;

public long Length => assembly.GetManifestResourceStream(subpath).Length;

public string PhysicalPath => $"/{filename}";

public string Name => filename;

public DateTimeOffset LastModified => DateTimeOffset.FromFileTime( int.Parse(assembly.GetName().Version.ToString().Replace(".","")));

public bool IsDirectory => false;

public Stream CreateReadStream()
{
return assembly.GetManifestResourceStream(subpath);
}
}
}
115 changes: 115 additions & 0 deletions src/BlazorEmbedLibrary/BlazorFileProvider.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.RenderTree;
using Microsoft.Extensions.FileProviders;
using Microsoft.Extensions.Primitives;
using Microsoft.JSInterop;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;

namespace BlazorEmbedLibrary
{
public class BlazorFileProvider : IFileProvider
{
/// <summary>
/// Displays a list of the embedded files for each assembly and extra console logging.
/// </summary>
public bool Debug { get; set; } = false;
/// <summary>
/// Allows multiple Assemblies to be passed as a list e.g. Assemblies=@ListOfAssemblies (where ListOfAssemblies is List<Assembly>
/// </summary>
private List<Assembly> Assemblies { get; set; }

private Dictionary<string, Assembly> fileMap;
private Dictionary<string, Assembly> assMap;
private Dictionary<string, string> nameMap;
public BlazorFileProvider(List<Assembly> assemblies)
{
Assemblies = assemblies ?? new List<Assembly>();
fileMap = new Dictionary<string, Assembly>();
assMap = new Dictionary<string, Assembly>();
nameMap = new Dictionary<string, string>();
LoadEmbeddedResources();
}
private void LoadEmbeddedResources()
{
foreach (var assembly in Assemblies)
{
string name = assembly.GetName().Name;
assMap.Add(name, assembly);
foreach (var item in ListEmbeddedResources(assembly))
{
string key = Path.GetFileName(item.Replace(":","/"));
if (nameMap.ContainsKey(key))
{
DebugLog($"BFP: Duplicate resource - unable to add {key} from {name}");
}
else
{
fileMap.Add(key, assembly);
nameMap.Add(key, item);
DebugLog($"BFP: Mapped {name}.{item} as {key}");
}
}
}
}

private void DebugLog(string message)
{
if (Debug) Console.WriteLine(message);
}

private IEnumerable<string> ListEmbeddedResources(Assembly assembly)
{
var resources = assembly.GetManifestResourceNames();
DebugLog($"Got resources: {string.Join(", ", resources)}");
DebugLog($"Using assembly: {assembly.GetName().Name}");
foreach (var item in resources)
{
yield return item;
}
}

public IFileInfo GetFileInfo(string subpath)
{
DebugLog($"BFP: GetFileInfo({subpath})");
var key = Path.GetFileName(subpath);
if (nameMap.ContainsKey(key))
{
return new BlazorFileInfo(nameMap[key], key, fileMap[key]);
}
return new NotFoundFileInfo(subpath);
}

public IDirectoryContents GetDirectoryContents(string subpath)
{
DebugLog($"BFP: GetDirectoryContents({subpath})");
var parts = subpath.Split('/');
string root;
if (string.IsNullOrEmpty(parts[0]) && parts.Length>2)
{
root = parts[1];
} else
{
root = parts[0];
}

if (root.Equals("_content"))
{
var name = parts[parts.Length-1];
return new BlazorDirectoryContents(assMap[name]);

}
return new NotFoundDirectoryContents();
}

public IChangeToken Watch(string filter)
{
throw new NotImplementedException();
}

}
}

0 comments on commit a6de699

Please sign in to comment.