Files
2026-07-10 10:24:29 +08:00

72 lines
2.7 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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
{
// 独立内网 CDNKestrel 静态文件服务,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;
}
}
}