Merge pull request #502 from sbwalker/master
Optimized downloading of assemblies when using WebAssembly
This commit is contained in:
commit
9b65cd0e07
@ -12,6 +12,8 @@ using Oqtane.Modules;
|
||||
using Oqtane.Shared;
|
||||
using Oqtane.Providers;
|
||||
using Microsoft.AspNetCore.Components.Authorization;
|
||||
using System.IO.Compression;
|
||||
using System.IO;
|
||||
|
||||
namespace Oqtane.Client
|
||||
{
|
||||
@ -90,15 +92,50 @@ namespace Oqtane.Client
|
||||
|
||||
private static async Task LoadClientAssemblies(HttpClient http)
|
||||
{
|
||||
var list = await http.GetFromJsonAsync<List<string>>($"/~/api/ModuleDefinition/load");
|
||||
// get list of loaded assemblies on the client ( in the client-side hosting module the browser client has its own app domain )
|
||||
var assemblyList = AppDomain.CurrentDomain.GetAssemblies().Select(a => a.GetName().Name).ToList();
|
||||
foreach (var name in list)
|
||||
// get list of loaded assemblies on the client
|
||||
var assemblies = AppDomain.CurrentDomain.GetAssemblies().Select(a => a.GetName().Name).ToList();
|
||||
|
||||
// get assemblies from server and load into client app domain
|
||||
var zip = await http.GetByteArrayAsync($"/~/api/Installation/load");
|
||||
|
||||
// asemblies and debug symbols are packaged in a zip file
|
||||
using (ZipArchive archive = new ZipArchive(new MemoryStream(zip)))
|
||||
{
|
||||
if (assemblyList.Contains(name)) continue;
|
||||
// download assembly from server and load
|
||||
var bytes = await http.GetByteArrayAsync($"/~/api/ModuleDefinition/load/{name}.dll");
|
||||
Assembly.Load(bytes);
|
||||
Dictionary<string, byte[]> dlls = new Dictionary<string, byte[]>();
|
||||
Dictionary<string, byte[]> pdbs = new Dictionary<string, byte[]>();
|
||||
|
||||
foreach (ZipArchiveEntry entry in archive.Entries)
|
||||
{
|
||||
if (!assemblies.Contains(Path.GetFileNameWithoutExtension(entry.Name)))
|
||||
{
|
||||
using (var memoryStream = new MemoryStream())
|
||||
{
|
||||
entry.Open().CopyTo(memoryStream);
|
||||
byte[] file = memoryStream.ToArray();
|
||||
switch (Path.GetExtension(entry.Name))
|
||||
{
|
||||
case ".dll":
|
||||
dlls.Add(entry.Name, file);
|
||||
break;
|
||||
case ".pdb":
|
||||
pdbs.Add(entry.Name, file);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var item in dlls)
|
||||
{
|
||||
if (pdbs.ContainsKey(item.Key))
|
||||
{
|
||||
Assembly.Load(item.Value, pdbs[item.Key]);
|
||||
}
|
||||
else
|
||||
{
|
||||
Assembly.Load(item.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -51,7 +51,7 @@ namespace Oqtane.Services
|
||||
|
||||
public async Task CreateModuleDefinitionAsync(ModuleDefinition moduleDefinition, int moduleId)
|
||||
{
|
||||
await PostJsonAsync($"{Apiurl}?moduleid={moduleId.ToString()}", moduleDefinition);
|
||||
await PostJsonAsync($"{Apiurl}?moduleid={moduleId}", moduleDefinition);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4,6 +4,11 @@ using Microsoft.Extensions.Configuration;
|
||||
using Oqtane.Models;
|
||||
using Oqtane.Shared;
|
||||
using Oqtane.Infrastructure;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using System.Linq;
|
||||
using System.IO.Compression;
|
||||
|
||||
namespace Oqtane.Controllers
|
||||
{
|
||||
@ -55,5 +60,56 @@ namespace Oqtane.Controllers
|
||||
_installationManager.UpgradeFramework();
|
||||
return installation;
|
||||
}
|
||||
|
||||
// GET api/<controller>/load
|
||||
[HttpGet("load")]
|
||||
public IActionResult Load()
|
||||
{
|
||||
if (_config.GetSection("Runtime").Value == "WebAssembly")
|
||||
{
|
||||
// get list of assemblies which should be downloaded to browser
|
||||
var assemblies = AppDomain.CurrentDomain.GetOqtaneClientAssemblies();
|
||||
var list = assemblies.Select(a => a.GetName().Name).ToList();
|
||||
var deps = assemblies.SelectMany(a => a.GetReferencedAssemblies()).Distinct();
|
||||
list.AddRange(deps.Where(a => a.Name.EndsWith(".oqtane", StringComparison.OrdinalIgnoreCase)).Select(a => a.Name));
|
||||
|
||||
// create zip file containing assemblies and debug symbols
|
||||
string binfolder = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location);
|
||||
byte[] zipfile;
|
||||
using (var memoryStream = new MemoryStream())
|
||||
{
|
||||
using (var archive = new ZipArchive(memoryStream, ZipArchiveMode.Create, true))
|
||||
{
|
||||
ZipArchiveEntry entry;
|
||||
foreach (string file in list)
|
||||
{
|
||||
entry = archive.CreateEntry(file + ".dll");
|
||||
using (var filestream = new FileStream(Path.Combine(binfolder, file + ".dll"), FileMode.Open, FileAccess.Read))
|
||||
using (var entrystream = entry.Open())
|
||||
{
|
||||
filestream.CopyTo(entrystream);
|
||||
}
|
||||
|
||||
if (System.IO.File.Exists(Path.Combine(binfolder, file + ".pdb")))
|
||||
{
|
||||
entry = archive.CreateEntry(file + ".pdb");
|
||||
using (var filestream = new FileStream(Path.Combine(binfolder, file + ".pdb"), FileMode.Open, FileAccess.Read))
|
||||
using (var entrystream = entry.Open())
|
||||
{
|
||||
filestream.CopyTo(entrystream);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
zipfile = memoryStream.ToArray();
|
||||
}
|
||||
return File(zipfile, "application/octet-stream", "oqtane.zip");
|
||||
}
|
||||
else
|
||||
{
|
||||
HttpContext.Response.StatusCode = 401;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -15,8 +15,6 @@ using System;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using System.Xml.Linq;
|
||||
using Microsoft.AspNetCore.Mvc.Formatters;
|
||||
// ReSharper disable StringIndexOfIsCultureSpecific.1
|
||||
|
||||
namespace Oqtane.Controllers
|
||||
{
|
||||
@ -163,39 +161,6 @@ namespace Oqtane.Controllers
|
||||
}
|
||||
}
|
||||
|
||||
// GET api/<controller>/load
|
||||
[HttpGet("load")]
|
||||
public List<string> Load()
|
||||
{
|
||||
List<string> list = new List<string>();
|
||||
if (_config.GetSection("Runtime").Value == "WebAssembly")
|
||||
{
|
||||
var assemblies = AppDomain.CurrentDomain.GetOqtaneClientAssemblies();
|
||||
list = AppDomain.CurrentDomain.GetOqtaneClientAssemblies().Select(a => a.GetName().Name).ToList();
|
||||
var deps = assemblies.SelectMany(a => a.GetReferencedAssemblies()).Distinct();
|
||||
list.AddRange(deps.Where(a => a.Name.EndsWith(".oqtane", StringComparison.OrdinalIgnoreCase)).Select(a => a.Name));
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
// GET api/<controller>/load/assembyname
|
||||
[HttpGet("load/{assemblyname}")]
|
||||
public IActionResult Load(string assemblyname)
|
||||
{
|
||||
if (_config.GetSection("Runtime").Value == "WebAssembly" && Path.GetExtension(assemblyname).ToLower() == ".dll")
|
||||
{
|
||||
string binfolder = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location);
|
||||
byte[] file = System.IO.File.ReadAllBytes(Path.Combine(binfolder, assemblyname));
|
||||
return File(file, "application/octet-stream", assemblyname);
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.Log(LogLevel.Error, this, LogFunction.Read, "User Not Authorized To Download Assembly {Assembly}", assemblyname);
|
||||
HttpContext.Response.StatusCode = 401;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// POST api/<controller>?moduleid=x
|
||||
[HttpPost]
|
||||
[Authorize(Roles = Constants.HostRole)]
|
||||
|
@ -92,6 +92,13 @@ namespace Oqtane.Infrastructure
|
||||
|
||||
switch (foldername)
|
||||
{
|
||||
case "":
|
||||
if (filename.EndsWith(".nuspec"))
|
||||
{
|
||||
filename = Path.Combine(sourceFolder, name);
|
||||
entry.ExtractToFile(filename, true);
|
||||
}
|
||||
break;
|
||||
case "lib":
|
||||
if (binFolder != null) entry.ExtractToFile(Path.Combine(binFolder, filename), true);
|
||||
break;
|
||||
|
@ -76,7 +76,8 @@ namespace System.Reflection
|
||||
public static IEnumerable<Assembly> GetOqtaneClientAssemblies(this AppDomain appDomain)
|
||||
{
|
||||
return appDomain.GetOqtaneAssemblies()
|
||||
.Where(a => a.GetTypes<IModuleControl>().Any() || a.GetTypes<IThemeControl>().Any() || a.GetTypes<IClientStartup>().Any());
|
||||
.Where(a => a.GetTypes<IModuleControl>().Any() || a.GetTypes<IThemeControl>().Any() || a.GetTypes<IClientStartup>().Any())
|
||||
.Where(a => Utilities.GetFullTypeName(a.GetName().Name) != "Oqtane.Client");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user