<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      Loading

      基于.NetCore開發博客項目 StarBlog - (17) 自動下載文章里的外部圖片

      系列文章

      前言

      好久沒更新博客了,上個月底更新了一篇關于StarBlog博客開發的文章之后,就因為線下培訓、詩詞大會之類的雜七雜八的事浪費了很多時間,有段時間一直在忙這些事情都沒空寫代碼……

      PS:我在詩詞大會上分享了這首詩:讀白居易的《禽蟲十二章》

      然后最近買了楊中科大佬新出的《AspNetCore技術內幕》,看得津津有味,花了一個多星期的時間,把書里的內容大致看了一遍,DDD(領域驅動設計)我早就想學了,不過一直沒找到好的入門資料,大佬的這本書就很不錯,很好懂,盡管如此,DDD還是一個相對復雜的方法,需要通過不斷的實踐來掌握。

      雖然最近做了這么多事,但同時工作也很忙,有個項目需要在九月前上線,本來我打算來實踐一下DDD的,不過寫著寫著發現還是把握不住,只好先用我之前的DjangoStarter框架,后面再慢慢把我的StarBlog博客用DDD思想進行改造~

      對了,這么久沒更新博客的原因,還有一點是我在使用過程中對目前的管理后臺非常不滿(使用Vue2+ElementUI開發),用戶體驗極差,所以我同時在構思用何種技術對管理后臺前端項目進行重構,目前有幾個備選項:

      • blazor(使用C#開發前端,很酷)
      • react(相對其他的來說,我最喜歡的前端技術棧)
      • 仍然vue,但重寫現有架構(工作量較小)

      還沒拿定主意,在重構完成之前,只能先捏著鼻子用現有的管理后臺,同時大概率也不會在現有的前端項目中增加新功能了。

      回到正題

      OK,說回本文的內容。在博客的使用過程中,有時候我會從其他網站復制一些markdown片段,或者是從我在其他平臺的博客上復制markdown內容(博客園、掘金之類的),這時候復制過來的markdown內容里面可能會有一些圖片,如果不做處理,可能會產生某些問題,如因圖片防盜鏈功能導致網絡圖片在StarBlog博客中無法顯示、網站運營商關閉導致圖片丟失等,對于數據,還是牢牢掌握在自己的手中比較放心。

      于是,我就做了這個功能:將markdown文章中的網絡圖片下載下來,并且替換markdown中的鏈接

      原理很簡單,掃描markdown,把圖片鏈接拿出來下載,同時把圖片鏈接替換成StarBlog上的地址。下面一步步介紹如何在代碼中實現。

      下載圖片

      首先是下載圖片的功能,C#中訪問網絡,可以使用HttpClient這個標準庫

      最簡單的用法是這樣:

      var client = new HttpClient();
      await client.GetAsync("圖片地址");
      

      不過官方文檔中并不推薦這種用法,最佳實踐是一個程序中只維護一個HttpClient的對象

      在AspNetCore中,我們可以利用依賴注入IHttpClientFactory來管理HttpClient對象。

      Program.cs中注冊服務

      builder.Services.AddHttpClient();
      

      在需要的地方注入IHttpClientFactory,比如在本項目中,我們新建一個CommonService.cs來放下載文件的代碼,考慮到這個功能以后別的地方也可能用到,所以做成通用的,不和PostService耦合在一起。

      代碼如下:

      public class CommonService {
        private readonly ILogger<CommonService> _logger;
        private readonly IHttpClientFactory _httpClientFactory;
      
        public CommonService(ILogger<CommonService> logger, IHttpClientFactory httpClientFactory) {
          _logger = logger;
          _httpClientFactory = httpClientFactory;
        }
        
        public async Task<string?> DownloadFileAsync(string url, string savePath) {
          var httpClient = _httpClientFactory.CreateClient();
          try {
            var resp = await httpClient.GetAsync(url, HttpCompletionOption.ResponseHeadersRead);
      
            // 生成隨機文件名
            var fileName = GuidUtils.GuidTo16String() + Path.GetExtension(url);
            var filePath = Path.Combine(savePath, WebUtility.UrlEncode(fileName));
            await using var fs = new FileStream(filePath, FileMode.OpenOrCreate, FileAccess.Write);
            await resp.Content.CopyToAsync(fs);
      
            return fileName;
          }
          catch (Exception ex) {
            _logger.LogError("下載文件出錯,信息:{Error}", ex);
            return null;
          }
        }
      }
      

      分析一下部分代碼:

      • 第13行代碼使用HttpClient的GetAsync方法下載數據,添加了個HttpCompletionOption.ResponseHeadersRead參數,這樣我們不必等全部信息加載到內存中后再進行流讀取之類的操作,而是在請求頭返回的時候就可以進入下一步處理。避免因為要下載的文件太大而導致OutOfMemoryException,這對下載文件的程序來說很重要!
      • 第16行,使用封裝好的Guid工具生成16位的GUID,直接用Guid.NewGuid().ToString()也行,這是32位的。
      • 第18-19行,將Http響應內容寫入文件流

      搞定,下載文件代碼比較簡單,涉及到IO操作這種容易出錯的地方,細節要處理好,才能保證程序的穩定性。

      PS:別忘了注冊服務!

      builder.Services.AddSingleton<CommonService>();
      

      處理Markdown

      下載圖片的功能搞定了之后,我們繼續來做markdown處理的部分

      關于C#處理Markdown,之前已經有過多次探索了,可以說是輕車熟路了hhh~

      附上之前關于Markdown處理的文章:

      依然是用Markdig這個庫(貌似.NetCore處理markdown上也沒其他選擇)

      PostService.cs中增加代碼

      /// <summary>
      /// Markdown中外部圖片下載
      /// <para>如果Markdown中包含外部圖片URL,則下載到本地且進行URL替換</para>
      /// </summary>
      private async Task<string> MdExternalUrlDownloadAsync(Post post) {
        if (post.Content == null) return string.Empty;
      
        // 得先初始化目錄
        InitPostMediaDir(post);
      
        var document = Markdown.Parse(post.Content);
        foreach (var node in document.AsEnumerable()) {
          if (node is not ParagraphBlock {Inline: { }} paragraphBlock) continue;
          foreach (var inline in paragraphBlock.Inline) {
            if (inline is not LinkInline {IsImage: true} linkInline) continue;
      
            var imgUrl = linkInline.Url;
            
            // 跳過空鏈接
            if (imgUrl == null) continue;
            // 跳過本站地址的圖片
            if (imgUrl.StartsWith(Host)) continue;
      
            // 下載圖片
            _logger.LogDebug("文章:{Title},下載圖片:{Url}", post.Title, imgUrl);
            var savePath = Path.Combine(_environment.WebRootPath, "media", "blog", post.Id!);
            var fileName = await _commonService.DownloadFileAsync(imgUrl, savePath);
            linkInline.Url = fileName;
          }
        }
      
        await using var writer = new StringWriter();
        var render = new NormalizeRenderer(writer);
        render.Render(document);
        return writer.ToString();
      }
      

      代碼說明:

      • 第9行的初始化目錄就是檢查這篇文章有沒有對應的目錄,沒有就先創建,很簡單就不貼代碼了。可以在github項目里看到完整代碼
      • 第12行開始的兩層循環通過遍歷markdown文檔樹,把圖片鏈接找出來
      • 第22行檢查圖片是站外還是站內的,站內圖片不用下載

      這樣就完成了markdown里站外圖片的下載和鏈接替換~

      修改文章保存邏輯

      接下來修改一下文章的保存邏輯

      還是在這個PostService.cs里,保存和新增文章共享一個方法:InsertOrUpdateAsync

      直接上代碼

      public async Task<Post> InsertOrUpdateAsync(Post post) {
        // 是新文章的話,先保存到數據庫
        if (await _postRepo.Where(a => a.Id == post.Id).CountAsync() == 0) {
          post = await _postRepo.InsertAsync(post);
        }
      
        // 檢查文章中的外部圖片,下載并進行替換
        post.Content = await MdExternalUrlDownloadAsync(post);
        // 修改文章時,將markdown中的圖片地址替換成相對路徑再保存
        post.Content = MdImageLinkConvert(post, false);
      
        // 處理完內容再更新一次
        await _postRepo.UpdateAsync(post);
        return post;
      }
      

      代碼說明:

      • 新文章的話,會先保存一次,作為草稿。
      • 先下載外部圖片,再替換本地圖片鏈接(關于圖片鏈接替換的,可以參考本系列第4篇文章,上面有鏈接)
      • 完成這些之后再保存,注意這時文章還是草稿狀態,需要通過另一個方法將文章的IsPublish屬性設置為true,不過與本文關系不大,這里先不貼代碼,后續在RESTFul接口開發部分的文章里會詳細介紹這個流程。

      到這里就搞定啦~

      參考資料

      posted @ 2022-08-14 21:44  程序設計實驗室  閱讀(840)  評論(0)    收藏  舉報
      主站蜘蛛池模板: 中文字幕亚洲资源网久久| 国产成人久久综合第一区| 日韩av一区二区高清不卡| 亚洲精品区午夜亚洲精品区| 甘孜县| 欧美黑人巨大xxxxx| 久久国产精品第一区二区| 日韩精品亚洲精品第一页| 丰满少妇在线观看网站| 亚洲成亚洲成网| 波多野无码中文字幕av专区| 国产精品中文字幕在线| 亚洲无?码A片在线观看| 国产成人一区二区不卡| 国产亚洲精品久久久久久青梅| 国产精品成人网址在线观看| 国产激情第一区二区三区| 狠狠躁天天躁中文字幕无码| 少妇激情一区二区三区视频小说 | 亚洲精品专区永久免费区| 国产亚洲欧美精品久久久| 亚洲成a人片在线观看中| 亚洲激情在线一区二区三区| 这里只有精品在线播放| 成人欧美日韩一区二区三区| 亚洲国产精品日韩专区av| 日韩av一区二区三区不卡| 欧美日本一区二区视频在线观看| 久久精品国产一区二区三| 精品无码成人片一区二区98| 久久国产精品精品国产色婷婷| 日本道高清一区二区三区| 欧美v国产v亚洲v日韩九九| 亚洲av伊人久久综合性色| 扒开双腿猛进入喷水高潮叫声| 国产漂亮白嫩美女在线观看| 男女做aj视频免费的网站| 好男人官网资源在线观看| 国产亚洲精品久久777777| 老熟妇欲乱一区二区三区| 精品国产精品中文字幕|