72 lines
2.7 KiB
C#
72 lines
2.7 KiB
C#
using System;
|
||
using System.IO;
|
||
using System.Linq;
|
||
using System.Threading.Tasks;
|
||
using Microsoft.AspNetCore.Builder;
|
||
using Microsoft.AspNetCore.Hosting;
|
||
using Microsoft.AspNetCore.Hosting.Server;
|
||
using Microsoft.AspNetCore.Hosting.Server.Features;
|
||
using Microsoft.Extensions.DependencyInjection;
|
||
using Microsoft.Extensions.FileProviders;
|
||
using Microsoft.Extensions.Logging;
|
||
|
||
namespace XWorld.Server.Gateway
|
||
{
|
||
// 独立内网 CDN:Kestrel 静态文件服务,HTTP 根 = root(发布产物 client/ 目录)。
|
||
// 仿 GameServerHost 的 Start/Stop 形态;缺失文件返回 404,无目录浏览。
|
||
public sealed class StaticFileServer
|
||
{
|
||
private readonly string _root;
|
||
private readonly int _port;
|
||
private readonly bool _bindAll;
|
||
private WebApplication _app;
|
||
|
||
public string HttpBaseUrl { get; private set; } // 例如 http://127.0.0.1:15081
|
||
|
||
public StaticFileServer(string root, int port = 15081, bool bindAllInterfaces = false)
|
||
{
|
||
if (string.IsNullOrWhiteSpace(root)) throw new ArgumentException("root 不能为空", nameof(root));
|
||
_root = Path.GetFullPath(root);
|
||
_port = port;
|
||
_bindAll = bindAllInterfaces;
|
||
}
|
||
|
||
public async Task StartAsync()
|
||
{
|
||
Directory.CreateDirectory(_root); // 根目录不存在则建空目录(GET 全 404,而非启动崩溃)
|
||
|
||
var builder = WebApplication.CreateBuilder();
|
||
builder.Logging.ClearProviders();
|
||
builder.WebHost.ConfigureKestrel(o =>
|
||
o.Listen(_bindAll ? System.Net.IPAddress.Any : System.Net.IPAddress.Loopback, _port)); // 0 = 临时端口
|
||
|
||
var app = builder.Build();
|
||
|
||
var provider = new PhysicalFileProvider(_root);
|
||
var opts = new StaticFileOptions
|
||
{
|
||
FileProvider = provider,
|
||
ServeUnknownFileTypes = true, // .bytes/.unity3d/.sig 等无标准 MIME 也要发
|
||
DefaultContentType = "application/octet-stream",
|
||
};
|
||
app.UseStaticFiles(opts); // 仅静态文件,不开目录浏览;未命中落到下面的 404
|
||
|
||
await app.StartAsync();
|
||
_app = app;
|
||
|
||
string http = app.Services.GetRequiredService<IServer>()
|
||
.Features.Get<IServerAddressesFeature>().Addresses.FirstOrDefault()
|
||
?? throw new InvalidOperationException("Kestrel 已启动但未注册任何监听地址");
|
||
HttpBaseUrl = http;
|
||
}
|
||
|
||
public async Task StopAsync()
|
||
{
|
||
if (_app == null) return;
|
||
await _app.StopAsync();
|
||
await _app.DisposeAsync();
|
||
_app = null;
|
||
}
|
||
}
|
||
}
|