Abp小試牛刀之 圖片上傳


圖片上傳是很常見的功能,里面有些固定的操作也可以沉淀下來。
本文記錄使用Abp vNext做圖片上傳的姿勢。

本文的技術核心不在Abp,Abp只是手段!

目標

  1. 上傳圖片----->預覽圖片----->確定保存
  2. 支持集群部署

實現思路:

① 上傳圖片要使用WebAPI特定媒體類型:multipart/form-data;
② 因為要做圖片預覽,故在上傳時利用AbpCache做一個臨時緩存,返回圖片Id
③ 前端利用FileReader渲染預覽圖;
④ [確定]: 發起持久化WebAPI(利用第②步返回的圖片Id)

為什么強調支持集群部署?

就這個功能而言,[上傳預覽]和[確定保存]是兩次Http WebAPI請求。

如果服務端使用的是Redis等進程外緩存: 那這正好是一個Stateless應用功能,集群環境次功能無懼!

如果服務端使用的是進程內緩存:在集群環境,前后兩次請求有可能打到不同的App服務,后置的[確定保存]WebAPI因此可能報錯, 此處需要做 [會話親和性] Session affinity

實踐

利用Abp做圖片上傳

IFormFile能力如下紅框:

下面將圖片二進制流轉化為 base64字符串,注入Abp緩存組件IDistributedCache<string>
緩存圖片字符串1小時。

[上傳預覽], [確定保存]的API完整代碼如下:

/// <summary>
       /// 上傳預覽, 返回待上傳的圖片id,Content-Type:multipart/form-data
       /// </summary>
       /// <returns></returns>
       [Consumes("multipart/form-data")]
       [Route("upload/preview")]
       [ProducesResponseType(typeof(Guid),200)]
       [HttpPost]
       public async Task<Guid> UploadPicPreviewAsync(IFormFile uploadedFile)
       {
           var formFileName = uploadedFile.FileName;
           if (!new[] { ".png", ".jpg", ".bmp" }.Any((item) => formFileName.EndsWith(item)))
           {
               throw new AbpValidationException("您上傳的文件格式必須為png、jpg、bmp中的一種");
           }
           byte[] bytes;
           using (var bodyStream = uploadedFile.OpenReadStream())
           {
               using (var m = new MemoryStream())
               {
                   await bodyStream.CopyToAsync(m);
                   bytes = m.ToArray();
               }
           }
           string base64 = Convert.ToBase64String(bytes);
           var bgId = Guid.NewGuid();
           _cache.Set($"{CurrentUser.TenantId}:bg:{bgId}", base64, new DistributedCacheEntryOptions { SlidingExpiration = new TimeSpan(1, 0, 0) });
           return bgId;
       }
       
       /// <summary>
       /// 保存圖片,要使用到前置API的預覽圖片id
       /// </summary>
       /// <param name="createPictureInput"></param>
       /// <returns></returns>
       [Route("upload/")]
       [HttpPost]
       public async Task<bool> UploadPicAsync([FromBody] CreatePictureInput createPictureInput)
       {
           var based64 = await _cache.GetAsync($"{CurrentUser.TenantId}:bg:{createPictureInput.PreviewPicId}");
           if (string.IsNullOrEmpty(based64))
               throw  new AbpException("Cache Hotmap Picture do not find");

           var model = ObjectMapper.Map<CreatePictureInput, Picture>(createPictureInput);
           model.ProfileId = CurrentUser.TenantId;
           model.BlobStorage = Convert.FromBase64String(based64);
           return await _pictures.InsertAsync(model)!= null;
       }

Default implementation of the IDistributedCache interface is the MemoryDistributedCache which works in-memory.
The Distributed Memory Cache (AddDistributedMemoryCache) is a framework-provided implementation of IDistributedCache that stores items in memory. The Distributed Memory Cache isn't an actual distributed cache. Cached items are stored by the app instance on the server where the app is running.

以上兩段文字來自 Abp和Asp.NETCore官方文檔:

  1. Abp默認的IDistributedCache實現是分布式內存緩存;
  2. ASP.NETCore 分布式內存緩存是框架內置的,是一個假的分布式緩存,實際是單純的內存緩存。

在沒有使用真實分布式緩存的情況下, 需要對前后兩個API配置會話親和性。

會話親和性

下面從nginx、Azure、k8s ingress 三角度配置[會話親和性],(全站生效)
會話親和性的實現原理,是在接受客戶端首次請求時響應某個cookie,服務器會認定使用同一個cookie的請求為一個會話。

1. nginx

屬於nginx負載均衡的范疇:https://docs.nginx.com/nginx/admin-guide/load-balancer/http-load-balancer/

示例如下:

upstream backend {
    server backend1.example.com;
    server backend2.example.com;
    sticky cookie srv_id expires=1h domain=.example.com path=/;
}
2. Azure App Service

Azure pp Service是Azure雲平台提供的App托管服務,具備多實例自動縮放的能力,
其有關會話親和性的配置如圖:

3. K8S nginx-ingress

注解nginx.ingress.kubernetes.io/affinity在入口的所有上游中啟用和設置親和性類型。
這樣,請求將總是被定向到相同的上游服務器。

https://kubernetes.github.io/ingress-nginx/examples/affinity/cookie/

That's All

本文以常見的圖片上傳功能為例,實戰演練了Abp的緩存和持久化能力;
引申出對有狀態應用配置會話親和性,部署方式要結合業務功能。

希望對大家有所幫助!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM