需求
我這邊有一些3DTiles數(shù)據(jù)需要?jiǎng)討B(tài)發(fā)布辕万,3DTiles數(shù)據(jù)簡(jiǎn)單來(lái)說(shuō)是把大規(guī)模的三維地理模型切成很多小片俐筋,在展示的時(shí)候按精度按范圍調(diào)取需要的數(shù)據(jù)绸贡,以減輕網(wǎng)絡(luò)和渲染壓力咏窿,加快渲染速度的一個(gè)方案息拜。因此他是有記錄切片配置的json文件和b3dm格式的數(shù)據(jù)文件構(gòu)成的廉丽,在數(shù)據(jù)的根目錄下有一個(gè)根的配置文件,每個(gè)子目錄下通常也會(huì)有子配置文件弄兜。
3DTiles數(shù)據(jù)大小得看數(shù)據(jù)規(guī)模和切片精度药蜻,通常城市級(jí)別的傾斜攝影模型切成3DTiles大小得按T計(jì)算瓷式,文件個(gè)數(shù)得按萬(wàn)計(jì)算,不太適合像普通文件一樣上傳然后通過(guò)接口訪問(wèn)语泽。所以考慮將需要發(fā)布的數(shù)據(jù)先通過(guò)其他方式上傳到服務(wù)器贸典,然后通過(guò)文件服務(wù)器的方式展示出來(lái)。
當(dāng)然其實(shí)也可以直接將某個(gè)文件夾通過(guò)IIS/Nginx發(fā)布出去踱卵,然后要求用戶每次上傳的數(shù)據(jù)都放在那個(gè)文件夾下也是可以的瓤漏,但是這樣靈活性和通用性就大打折扣。
因此設(shè)定的業(yè)務(wù)邏輯應(yīng)該是客戶通過(guò)FTP或者其他什么工具將數(shù)據(jù)上傳到服務(wù)器颊埃,然后通過(guò)應(yīng)用選擇數(shù)據(jù)文件夾,設(shè)定虛擬目錄(url子路徑)蝶俱,發(fā)布班利,就可以通過(guò)url訪問(wèn)了。
技術(shù)棧
我這邊習(xí)慣上后臺(tái)使用Asp.Net Core Web API開(kāi)發(fā)榨呆,現(xiàn)在到了.Net 6罗标,是一個(gè)長(zhǎng)期支持版本。當(dāng)使用Visual Studio創(chuàng)建Asp.Net Core Web API后积蜻,入口文件Program.cs下會(huì)自動(dòng)生成類似以下的代碼闯割,直接運(yùn)行就會(huì)有一個(gè)天氣預(yù)報(bào)的示例接口和Swagger接口文檔頁(yè)面(所以說(shuō).net core好用呀):
var apiAppBuilder = WebApplication.CreateBuilder(args);
apiAppBuilder.Services.AddControllers();
apiAppBuilder.Services.AddEndpointsApiExplorer();
apiAppBuilder.Services.AddSwaggerGen();
var apiApp = apiAppBuilder.Build();
if (apiApp.Environment.IsDevelopment())
{
apiApp.UseSwagger();
apiApp.UseSwaggerUI();
}
apiApp.UseAuthorization();
apiApp.MapControllers();
await apiApp.RunAsync();
文件服務(wù)
如果想直接讓上面代碼中的apiAPP支持文件服務(wù),只需要給他綁定靜態(tài)文件服務(wù)相關(guān)的內(nèi)容就行:
var staticfile = new StaticFileOptions();
staticfile.ServeUnknownFileTypes = true;
staticfile.FileProvider = new PhysicalFileProvider(physicalPath);
staticfile.RequestPath = urlPath;
apiApp.UseStaticFiles(staticfile);
如果希望除了提供文件服務(wù)之外還可以在瀏覽器中瀏覽竿拆,就需要綁定文件夾瀏覽相關(guān)的內(nèi)容:
var dirOp = new DirectoryBrowserOptions();
dirOp.FileProvider = new PhysicalFileProvider(physicalPath);
dirOp.RequestPath = urlPath;
apiApp.UseDirectoryBrowser(dirOp);
上面urlPath是在網(wǎng)址路徑中的path宙拉,以'/'開(kāi)頭,比如我的服務(wù)是http://localhost:5000
丙笋,這里的urlPath設(shè)置為/data
谢澈,那通過(guò)http://localhost:5000/data
訪問(wèn)到的就是physicalPath中的內(nèi)容,UseStaticFiles和UseDirectoryBrowser都是可以反復(fù)添加的御板,通過(guò)這樣的方式就可以添加多個(gè)文件夾锥忿。
寄生應(yīng)用
很可惜,這些個(gè)是不能夠動(dòng)態(tài)設(shè)定的怠肋,也就是這些設(shè)置必須在apiApp.RunAsync()
之前設(shè)定敬鬓,啟動(dòng)之后再設(shè)置就沒(méi)用了。但是如果按默認(rèn)的設(shè)定笙各,把a(bǔ)piApp停了整個(gè)服務(wù)就會(huì)掛掉钉答,沒(méi)法走設(shè)定后重啟的路線,因此得在主應(yīng)用之下加一個(gè)寄生應(yīng)用作為文件服務(wù)的專有應(yīng)用酪惭。
public class FileServerApp
{
private static WebApplication AppInstance = null;
private Dictionary<string,string> Directories = new Dictionary<string, string>();
/// <summary>
/// 添加文件夾
/// </summary>
/// <param name="key"></param>
/// <param name="dir"></param>
/// <returns></returns>
public async Task AddDirectoryAsync(string urlPath, string physicalPath)
{
if (Directories.ContainsKey(urlPath))
{
Directories[urlPath] = physicalPath;
}
else
{
Directories.Add(urlPath, physicalPath);
}
await this.StopAsync();
await this.StartAsync();
}
/// <summary>
/// 停止
/// </summary>
/// <returns></returns>
private async Task StopAsync()
{
if (AppInstance != null)
{
await AppInstance.StopAsync();
await AppInstance.WaitForShutdownAsync();
await AppInstance.DisposeAsync();
}
}
/// <summary>
/// 啟動(dòng)
/// </summary>
/// <returns></returns>
private async Task StartAsync()
{
var fileAppBuilder = WebApplication.CreateBuilder();
fileAppBuilder.Services.AddCors(options =>
{
options.AddPolicy("Any", builder =>
{
builder.AllowAnyOrigin()
.AllowAnyHeader()
.AllowAnyMethod();
});
});
AppInstance = fileAppBuilder.Build();
AppInstance.UseCors("Any");
AppInstance.Urls.Add("http://*:6789");
foreach (var urlPath in Directories.Keys)
{
var dirOp = new DirectoryBrowserOptions();
dirOp.FileProvider = new PhysicalFileProvider(Directories[urlPath]);
dirOp.RequestPath = urlPath;
AppInstance.UseDirectoryBrowser(dirOp);
var staticfile = new StaticFileOptions();
staticfile.ServeUnknownFileTypes = true;
staticfile.FileProvider = new PhysicalFileProvider(Directories[urlPath]);
staticfile.RequestPath = urlPath;
AppInstance.UseStaticFiles(staticfile);
}
await AppInstance.RunAsync();
}
}
這里用Dictionary模擬持久化數(shù)據(jù)希痴,只需要在相應(yīng)的接口中執(zhí)行以下代碼就可以啟動(dòng)寄生應(yīng)用了:
var fileServerApp = new FileServerApp();
fileServerApp.AddDirectoryAsync(urlPath, physicalPath);
注意這里的AddDirectoryAsync不能await,否則會(huì)阻塞住春感,直到該應(yīng)用停止砌创。