Asp.NET Core實(shí)現(xiàn)動(dòng)態(tài)文件服務(wù)器

需求

我這邊有一些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文件結(jié)構(gòu)

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)用停止砌创。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末虏缸,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子嫩实,更是在濱河造成了極大的恐慌刽辙,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,284評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件甲献,死亡現(xiàn)場(chǎng)離奇詭異宰缤,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)晃洒,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,115評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門慨灭,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人球及,你說(shuō)我怎么就攤上這事氧骤。” “怎么了吃引?”我有些...
    開(kāi)封第一講書人閱讀 164,614評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵筹陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我镊尺,道長(zhǎng)朦佩,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書人閱讀 58,671評(píng)論 1 293
  • 正文 為了忘掉前任庐氮,我火速辦了婚禮语稠,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘旭愧。我一直安慰自己颅筋,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,699評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布输枯。 她就那樣靜靜地躺著议泵,像睡著了一般。 火紅的嫁衣襯著肌膚如雪桃熄。 梳的紋絲不亂的頭發(fā)上先口,一...
    開(kāi)封第一講書人閱讀 51,562評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音瞳收,去河邊找鬼碉京。 笑死,一個(gè)胖子當(dāng)著我的面吹牛螟深,可吹牛的內(nèi)容都是我干的谐宙。 我是一名探鬼主播,決...
    沈念sama閱讀 40,309評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼界弧,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼凡蜻!你這毒婦竟也來(lái)了搭综?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書人閱讀 39,223評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤划栓,失蹤者是張志新(化名)和其女友劉穎兑巾,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體忠荞,經(jīng)...
    沈念sama閱讀 45,668評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡蒋歌,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,859評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了委煤。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片堂油。...
    茶點(diǎn)故事閱讀 39,981評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖碧绞,靈堂內(nèi)的尸體忽然破棺而出称诗,到底是詐尸還是另有隱情,我是刑警寧澤头遭,帶...
    沈念sama閱讀 35,705評(píng)論 5 347
  • 正文 年R本政府宣布,位于F島的核電站癣诱,受9級(jí)特大地震影響计维,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜撕予,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,310評(píng)論 3 330
  • 文/蒙蒙 一鲫惶、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧实抡,春花似錦欠母、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 31,904評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至啄清,卻和暖如春六水,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背辣卒。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 33,023評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工掷贾, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人荣茫。 一個(gè)月前我還...
    沈念sama閱讀 48,146評(píng)論 3 370
  • 正文 我出身青樓想帅,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親啡莉。 傳聞我的和親對(duì)象是個(gè)殘疾皇子港准,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,933評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容