Net core中使用System.Drawing對上傳的圖片流進行壓縮

来源:https://www.cnblogs.com/kangsir7/archive/2022/08/19/16602088.html
-Advertisement-
Play Games

由於net core 中預設沒有System.Drawing,可以通過nuget下載一個來代替System.Drawing.Common 直接壓縮圖片 /// <summary> /// 圖片壓縮 /// </summary> /// <param name="sFile">原圖片位置</param ...


由於net core 中預設沒有System.Drawing,可以通過nuget下載一個來代替System.Drawing.Common

 

直接壓縮圖片

/// <summary>
/// 圖片壓縮
/// </summary>
/// <param name="sFile">原圖片位置</param>
/// <param name="dFile">壓縮後圖片位置</param>
/// <param name="dHeight">圖片壓縮後的高度</param>
/// <param name="dWidth">圖片壓縮後的寬度</param>
/// <param name="flag">圖片壓縮比0-100,數值越小壓縮比越高,失真越多</param>
/// <returns></returns>
public static bool GetPicThumbnailTest(string sFile, string dFile, int dHeight, int dWidth, int flag)
{
    System.Drawing.Image iSource = System.Drawing.Image.FromFile(sFile);
    //如果為參數為0就保持原圖片的高寬嘛(不然想保持原圖外面還要去讀取一次)
    if (dHeight == 0)
    {
        dHeight = iSource.Height;
    }
    if (dWidth == 0)
    {
        dWidth = iSource.Width;
    }


    ImageFormat tFormat = iSource.RawFormat;
    int sW = 0, sH = 0;

    //按比例縮放
    Size tem_size = new Size(iSource.Width, iSource.Height);

    if (tem_size.Width > dHeight || tem_size.Width > dWidth)
    {
        if ((tem_size.Width * dHeight) > (tem_size.Width * dWidth))
        {
            sW = dWidth;
            sH = (dWidth * tem_size.Height) / tem_size.Width;
        }
        else
        {
            sH = dHeight;
            sW = (tem_size.Width * dHeight) / tem_size.Height;
        }
    }
    else
    {
        sW = tem_size.Width;
        sH = tem_size.Height;
    }

    Bitmap ob = new Bitmap(dWidth, dHeight);
    Graphics g = Graphics.FromImage(ob);

    g.Clear(Color.WhiteSmoke);
    g.CompositingQuality = CompositingQuality.HighQuality;
    g.SmoothingMode = SmoothingMode.HighQuality;
    g.InterpolationMode = InterpolationMode.HighQualityBicubic;

    g.DrawImage(iSource, new Rectangle((dWidth - sW) / 2, (dHeight - sH) / 2, sW, sH), 0, 0, iSource.Width, iSource.Height, GraphicsUnit.Pixel);

    g.Dispose();
    //以下代碼為保存圖片時,設置壓縮質量  
    EncoderParameters ep = new EncoderParameters();
    long[] qy = new long[1];
    qy[0] = flag;//設置壓縮的比例1-100  
    EncoderParameter eParam = new EncoderParameter(System.Drawing.Imaging.Encoder.Quality, qy);
    ep.Param[0] = eParam;
    try
    {
        ImageCodecInfo[] arrayICI = ImageCodecInfo.GetImageEncoders();
        ImageCodecInfo jpegICIinfo = null;
        for (int x = 0; x < arrayICI.Length; x++)
        {
            if (arrayICI[x].FormatDescription.Equals("JPEG"))
            {
                jpegICIinfo = arrayICI[x];
                break;
            }
        }
        if (jpegICIinfo != null)
        {
            ob.Save(dFile, jpegICIinfo, ep);//dFile是壓縮後的新路徑  
        }
        else
        {
            ob.Save(dFile, tFormat);
        }
        return true;
    }
    catch
    {
        return false;
    }
    finally
    {
        iSource.Dispose();
        ob.Dispose();
    }
}

  

通過文件流壓縮圖片

有些時候我們不想先把圖片保存後,然後在去讀取壓縮,我們想通過文件流就直接對圖片進行壓縮了,比如我們要把圖片上傳到七牛雲

先把流進行壓縮在上傳到七牛雲就比較科學了

1:首先我們需要通過圖片上傳的流來獲取圖片

 foreach (IFormFile file in files)//獲取多個文件列表集合
            {
                if (file.Length > 0)
                {
                    //獲取圖片上傳的流
                    Stream stream = file.OpenReadStream();
                    //直接從流裡邊變成圖片
                    System.Drawing.Image iSource = System.Drawing.Image.FromStream(stream);
                }
            }

  

2:通過圖片壓縮演算法把圖片進行壓縮

這裡有一個參數是輸入流,後面還有一個是壓縮後的輸出流

        /// <summary>
        /// 上傳圖片文件
        /// </summary>
        /// <returns></returns>
        [HttpPost]
        public async Task<IActionResult> UploadImageFile_WeChat()
        {
            var file = IHttpContextAccessor.HttpContext.Request.Form.Files;
            if (file == null || file.Count == 0)
            {
                return Fail("未上傳有效文件");
            }
            var result = new List<dynamic>();
            foreach (var item in file)
            {
                var ExtensionName = Path.GetExtension(item.FileName).ToLower();

                var RemotePath = getRemotePath(ExtensionName);
                if (string.IsNullOrEmpty(RemotePath) || !"image".Equals(RemotePath))
                {
                    return Fail("不支持此類型文件的上傳");
                }

                string remotePath = PathFormatter.Format(item.FileName + "." + ExtensionName, "/upload/" + RemotePath + "/image" + "/{yyyy}{mm}/{dd}{time}{rand:6}");
                string savePath = AppDomain.CurrentDomain.BaseDirectory + "/wwwroot/" + remotePath;

                MemoryStream memoryStream = new MemoryStream();
                //ob.Save(memoryStream, jpegICIinfo, ep);//這裡的ob就是壓縮後的Bitmap對象
                var k = GetPicThumbnail(item.OpenReadStream(), 0, 0, 70, memoryStream);

                System.Drawing.Image imgSource = System.Drawing.Image.FromStream(memoryStream);
                imgSource.Save(savePath);

                if (k)
                {
                    result.Add(new { url = Config.FileConfig.fileUrl + remotePath, remoteUrl = remotePath, name = item.FileName });
                }
            }
            return Success("上傳成功", result);

        }

        private bool GetPicThumbnail(Stream stream, int dHeight, int dWidth, int flag, Stream outstream)
        {
            //可以直接從流裡邊得到圖片,這樣就可以不先存儲一份了
            System.Drawing.Image iSource = System.Drawing.Image.FromStream(stream);

            //如果為參數為0就保持原圖片
            if (dHeight == 0)
            {
                dHeight = iSource.Height;
            }
            if (dWidth == 0)
            {
                dWidth = iSource.Width;
            }


            ImageFormat tFormat = iSource.RawFormat;
            int sW = 0, sH = 0;

            //按比例縮放
            Size tem_size = new Size(iSource.Width, iSource.Height);

            if (tem_size.Width > dHeight || tem_size.Width > dWidth)
            {
                if ((tem_size.Width * dHeight) > (tem_size.Width * dWidth))
                {
                    sW = dWidth;
                    sH = (dWidth * tem_size.Height) / tem_size.Width;
                }
                else
                {
                    sH = dHeight;
                    sW = (tem_size.Width * dHeight) / tem_size.Height;
                }
            }
            else
            {
                sW = tem_size.Width;
                sH = tem_size.Height;
            }

            Bitmap ob = new Bitmap(dWidth, dHeight);
            Graphics g = Graphics.FromImage(ob);

            g.Clear(Color.WhiteSmoke);
            g.CompositingQuality = CompositingQuality.HighQuality;
            g.SmoothingMode = SmoothingMode.HighQuality;
            g.InterpolationMode = InterpolationMode.HighQualityBicubic;

            g.DrawImage(iSource, new Rectangle((dWidth - sW) / 2, (dHeight - sH) / 2, sW, sH), 0, 0, iSource.Width, iSource.Height, GraphicsUnit.Pixel);

            g.Dispose();
            //以下代碼為保存圖片時,設置壓縮質量  
            EncoderParameters ep = new EncoderParameters();
            long[] qy = new long[1];
            qy[0] = flag;//設置壓縮的比例1-100  
            EncoderParameter eParam = new EncoderParameter(System.Drawing.Imaging.Encoder.Quality, qy);
            ep.Param[0] = eParam;
            try
            {
                ImageCodecInfo[] arrayICI = ImageCodecInfo.GetImageEncoders();
                ImageCodecInfo jpegICIinfo = null;
                for (int x = 0; x < arrayICI.Length; x++)
                {
                    if (arrayICI[x].FormatDescription.Equals("JPEG"))
                    {
                        jpegICIinfo = arrayICI[x];
                        break;
                    }
                }
                if (jpegICIinfo != null)
                {
                    //可以存儲在流裡邊;
                    ob.Save(outstream, jpegICIinfo, ep);

                }
                else
                {
                    ob.Save(outstream, tFormat);
                }


                return true;
            }
            catch
            {
                return false;
            }
            finally
            {
                iSource.Dispose();
                ob.Dispose();
            }
        }

  3:把壓縮後的圖片轉化成流,很簡單用一個記憶體流來中轉一下就好了

 MemoryStream memoryStream = new MemoryStream();
  ob.Save(memoryStream, jpegICIinfo, ep);//這裡的ob就是壓縮後的Bitmap對象

     為了驗證一下轉化是否正確,我們可以把流在轉化成圖片然後在圖片進行存儲

 System.Drawing.Image imgSource = System.Drawing.Image.FromStream(memoryStream);
 imgSource.Save("url");

  如果能夠成功壓縮併成功保存就說明這些步驟都成功了!

這裡說一下圖片傳輸的思路

圖片文件這種本身是無法進行傳輸的,就像跨語言的對象也是無法進行傳輸。但是我們可以事先約定一種標準,

讓雙方都可以認識都可以解析的一種標準,比如base64,比如對象的json序列化,比如光纖信號的光波表示,其實原理都是一樣。

 

上傳到七牛雲前壓縮圖片

 

通過上面的方法可以得到一個輸出流,我們可以通過它進行圖片的保存,但是如果直接把這個輸出流傳遞到七牛雲的方法中去,圖片是不能被上傳成功的,存儲大小會是0kb,說明我們這個流七牛雲的介面識別不到,也就是約定的內容不一樣,我們要改造成七牛雲能夠被識別的狀態

 

換一個方法嘗試,直接用流不行,就從流裡邊讀出來位元組數組試試

 //實例化一個記憶體流,存放壓縮後的圖片
    MemoryStream ysstream = new MemoryStream();
    bool issuc = ImageTool.GetPicThumbnail(stream, 300, 300, 80, ysstream);

    if (issuc)
    {
        //通過流上傳圖片到七牛雲
        //HttpResult result = um.UploadStream(stream, saveKey, uploadToken);
        //從記憶體流裡邊讀出來位元組數組上傳到七牛雲
        HttpResult result = um.UploadData(ysstream.ToArray(), saveKey, uploadToken);
        if (result.Code == 200)
        {
            return Json(result.Text);
        }
        else
        {
            throw new Exception(result.RefText);//上傳失敗錯誤信息
        }
    }
    else
    {
        throw new Exception("圖片壓縮失敗");//上傳失敗錯誤信息
    }

  成功了

換迴流試試呢,不應該啊。傳遞流進去他裡邊也應該是讀取的直接哇,本質上都一樣哇

還是不行,看來得看一下他這個源碼了,看一下他拿到這個流過後是怎麼去用的,就能針對性解決問題了

 

部署問題

在Windows環境下直接運行是沒問題的,但是發佈到Linux上就會報錯

 

在Linux中安裝

開始安裝libgdiplus,執行【docker ps -a 】查看所有容器

 

 

【docker start 容器ID】 將容器運行起來

 

【docker exec -it e90f2b9d448d /bin/bash】進入該容器bash界面

 

 

執行【apt-get update】

 

【apt-get install -y libgdiplus】安裝libgdiplus類庫

 

 

【 ln -s /usr/lib/libgdiplus.so /usr/lib/gdiplus.dll】創建鏈接文件

 

【eixt】退出docker bash到宿主機的bash,執行 【docker restart 容器ID】,此時介面已經能正確訪問了

 

 

上面的方法有個弊端,假如容器被誤刪,又要重新給容器安裝libgdiplus庫。

我們可以把修改好的容器製作成鏡像,執行【docker commit e90f2b9d448d skyapi_libgdiplus】,然後執行【docker images】,

可以看到名字叫skyapi_libgdiplus的Docker鏡像已經製作好了。今後只需要在 docker run -t 參數後面指定 skyapi_libgdiplus鏡像即可。

當前還可以將鏡像保存到docker hub,本地硬碟都可以。

 

喜聞樂見的是,.NET 6發佈了,但是避免不了新框架帶來各種問題。在以往的跨平臺應用中,往往採用System.Drawing.Common這個庫作為圖形編輯組件。

在.NET 6之前,在Linux操作系統中需要用到這個庫時,只需要安裝libgdiplus和libc6-dev這兩個依賴即可。但是在.NET 6中,System.Drawing.Common被歸為Windows特定的庫,編譯時產生“'Image.xxx()' is only supported on: 'windows'.”這樣的警告。這不是最重要的,嚴重的是,在Linux中調用時,會產生“The type initializer for 'Gdip' threw an exception.”這樣的異常。

產生原因

在設計上System.Drawing.Common 是 Windows 技術的精簡包裝器,因此其跨平臺實現欠佳。

具微軟文檔中描述,在舊的行為上,libgdiplus 是本機端 System.Drawing.Common 跨平臺實現的主要提供程式。 libgdiplus 實際上是對 System.Drawing.Common 所依賴的 Windows 部分的重新實現。 該實現使 libgdiplus 成為一個重要的組件。 它大約有 30,000 行 C 代碼,大部分未經測試,而且缺少很多功能。 libgdiplus 還具有許多用於圖像處理和文本呈現的外部依賴項,例如 cairo、pango 和其他本機庫。 這些依賴項使得維護和交付組件更具挑戰性。 自從包含 Mono 跨平臺實現以來,我們已將許多從未得到修複的問題重定向到 libgdiplus。 相比之下,我們採用的其他外部依賴項,例如 icu 或 openssl,都是高質量的庫。 使 libgdiplus 的功能集和質量與 .NET 堆棧的其餘部分相媲美是不可行的。

在這之後,System.Drawing.Common 將僅在 Windows 窗體和 GDI+ 項目中使用。

解決方案

1、項目不會在Linux平臺運行,僅在Windows中運行

可以忽略這個警告。

2、通過將 runtimeconfig.json 文件中的 System.Drawing.EnableUnixSupport 運行時配置開關設置為 true 來啟用對非 Windows 平臺的支持。

{
   "runtimeOptions": {
      "configProperties": {
         "System.Drawing.EnableUnixSupport": true
      }
   }
}

3、換用其它支持跨平臺的圖像處理庫

如:

需要註意的是,這些庫並不與System.Drawing.Common的API相容,所以更換相應的庫之後需要重新編寫相關代碼。

 

本文來自博客園,作者:康Sir7,轉載請註明原文鏈接:https://www.cnblogs.com/kangsir7/p/16602088.html


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 前言 嗨嘍~大家好呀,這裡是魔王吶 ! 這個小恐龍熟悉叭,相信你在摸魚得時候玩過它~ 這是谷歌流量器中很有名的彩蛋:當你網路出現問題時,就會出現一個“小恐龍游戲”。 當然你如果想要直接進行游戲,可以在地址欄輸入:chrome://dino 那麼今天我們得目的就是:用python來製作一個仿製的“小恐 ...
  • 哈嘍大家好,今天說一下reduce函數的使用方法,以及與for迴圈的對比。 reduce函數原本在python2中也是個內置函數,不過在python3中被移到functools模塊中。 reduce函數先從列表(或序列)中取出2個元素執行指定函數,並將輸出結果與第3個元素傳入函數,輸出結果再與第4個 ...
  • 該工程採用spring+mybatis的關聯映射,動態sql,sql片段實現 1、搭建項目結構如下 2、配置項目的pom.xml文件中的依賴 1 <?xml version="1.0" encoding="UTF-8"?> 2 3 <project xmlns="http://maven.apach ...
  • 哈嘍兄弟們,今天實現一下人臉識別。 先問大家一個問題什麼是百度Aip模塊? 百度AI平臺提供了很多的API介面供開發者快速的調用運用在項目中本文寫的是使用百度AI的線上介面SDK模塊(baidu-aip)進行實現人臉識別 除了人臉識別,其他api功能的調用也同理。 準備工作 本機環境 系統:win1 ...
  • 發展史 1.早期的時候,網站都沒有保存用戶功能的需求,所有用戶訪問網站返回的結果都是一樣的,比如新聞、文章等網站! 2.但是,隨著網站的發展,出現了一些需要保存用戶信息的網站,比如:淘寶、京東、個人博客等! 3.以登錄功能為例,如果不保存用戶登錄的信息,就意味著用戶每次都需要重新登錄網站,為此非常的 ...
  • 1、標記實現參數化 通過@pytest.mark.parametrize(argnames, argvalues, indirect=False, ids=None, scope=None)裝飾器來實現參數化。 參數說明: argnames:參數名,使用逗號分隔的列表,或元祖,或字元串,表示一個或多 ...
  • 輕量容器引擎Docker Docker是什麼 ​ Docker 是一個開源項目,誕生於 2013 年初,最初是 dotCloud 公司內部的一個業餘項目。它基於 Google 公司推出的 Go 語言實現。項目後來加入了 Linux 基金會,遵從了 Apache 2.0 協議,項目代碼在GitHub ...
  • 一、時間輪演算法簡介 為了大家能夠理解下文中的代碼,我們先來簡單瞭解一下netty時間輪演算法的核心原理 時間輪演算法名副其實,時間輪就是一個環形的數據結構,類似於表盤,將時間輪分成多個bucket(比如:0-8)。假設每個時間輪輪片的分隔時間段tickDuration=1s(即:指針經過每個格子花費時間 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...