溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

Net?core中怎么使用System.Drawing對上傳的圖片流進(jìn)行壓縮

發(fā)布時間:2022-08-25 11:34:34 來源:億速云 閱讀:132 作者:iii 欄目:開發(fā)技術(shù)

這篇文章主要介紹“Net core中怎么使用System.Drawing對上傳的圖片流進(jìn)行壓縮”的相關(guān)知識,小編通過實際案例向大家展示操作過程,操作方法簡單快捷,實用性強(qiáng),希望這篇“Net core中怎么使用System.Drawing對上傳的圖片流進(jìn)行壓縮”文章能幫助大家解決問題。

    由于net core 中默認(rèn)沒有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,數(shù)值越小壓縮比越高,失真越多</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);
        //如果為參數(shù)為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();
        //以下代碼為保存圖片時,設(shè)置壓縮質(zhì)量 
        EncoderParameters ep = new EncoderParameters();
        long[] qy = new long[1];
        qy[0] = flag;//設(shè)置壓縮的比例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();
        }
    }

    通過文件流壓縮圖片

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

    先把流進(jìn)行壓縮在上傳到七牛云就比較科學(xué)了

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

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

    2:通過圖片壓縮算法把圖片進(jìn)行壓縮

    這里有一個參數(shù)是輸入流,后面還有一個是壓縮后的輸出流

    /// <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);
        //如果為參數(shù)為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();
        //以下代碼為保存圖片時,設(shè)置壓縮質(zhì)量 
        EncoderParameters ep = new EncoderParameters();
        long[] qy = new long[1];
        qy[0] = flag;//設(shè)置壓縮的比例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:把壓縮后的圖片轉(zhuǎn)化成流,很簡單用一個內(nèi)存流來中轉(zhuǎn)一下就好了

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

       為了驗證一下轉(zhuǎn)化是否正確,我們可以把流在轉(zhuǎn)化成圖片然后在圖片進(jìn)行存儲

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

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

    這里說一下圖片傳輸?shù)乃悸罚?/p>

    圖片文件這種本身是無法進(jìn)行傳輸?shù)?,就像跨語言的對象也是無法進(jìn)行傳輸。但是我們可以事先約定一種標(biāo)準(zhǔn),

    讓雙方都可以認(rèn)識都可以解析的一種標(biāo)準(zhǔn),比如base64,比如對象的json序列化,比如光纖信號的光波表示,其實原理都是一樣。

    上傳到七牛云前壓縮圖片

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

    換一個方法嘗試,直接用流不行,就從流里邊讀出來字節(jié)數(shù)組試試

    //實例化一個內(nèi)存流,存放壓縮后的圖片
       MemoryStream ysstream = new MemoryStream();
       bool issuc = ImageTool.GetPicThumbnail(stream, 300, 300, 80, ysstream);
     
       if (issuc)
       {
           //通過流上傳圖片到七牛云
           //HttpResult result = um.UploadStream(stream, saveKey, uploadToken);
           //從內(nèi)存流里邊讀出來字節(jié)數(shù)組上傳到七牛云
           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("圖片壓縮失敗");//上傳失敗錯誤信息
       }

      成功了

    換回流試試呢,不應(yīng)該啊。傳遞流進(jìn)去他里邊也應(yīng)該是讀取的直接哇,本質(zhì)上都一樣哇

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

    部署問題

    在Windows環(huán)境下直接運行是沒問題的,但是發(fā)布到Linux上就會報錯

    Net?core中怎么使用System.Drawing對上傳的圖片流進(jìn)行壓縮

    在Linux中安裝

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

    Net?core中怎么使用System.Drawing對上傳的圖片流進(jìn)行壓縮

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

    Net?core中怎么使用System.Drawing對上傳的圖片流進(jìn)行壓縮

    【docker exec -it e90f2b9d448d /bin/bash】進(jìn)入該容器bash界面

    Net?core中怎么使用System.Drawing對上傳的圖片流進(jìn)行壓縮

    執(zhí)行【apt-get update】

    Net?core中怎么使用System.Drawing對上傳的圖片流進(jìn)行壓縮

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

    Net?core中怎么使用System.Drawing對上傳的圖片流進(jìn)行壓縮

    【ln -s /usr/lib/libgdiplus.so /usr/lib/gdiplus.dll】創(chuàng)建鏈接文件

    【eixt】退出docker bash到宿主機(jī)的bash,執(zhí)行 【docker restart 容器ID】,此時接口已經(jīng)能正確訪問了

    Net?core中怎么使用System.Drawing對上傳的圖片流進(jìn)行壓縮

    Net?core中怎么使用System.Drawing對上傳的圖片流進(jìn)行壓縮

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

    我們可以把修改好的容器制作成鏡像,執(zhí)行【docker commit e90f2b9d448d skyapi_libgdiplus】,然后執(zhí)行【docker images】,

    可以看到名字叫skyapi_libgdiplus的Docker鏡像已經(jīng)制作好了。今后只需要在 docker run -t 參數(shù)后面指定skyapi_libgdiplus鏡像即可。

    當(dāng)前還可以將鏡像保存到docker hub,本地硬盤都可以。

    Net?core中怎么使用System.Drawing對上傳的圖片流進(jìn)行壓縮

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

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

    產(chǎn)生原因

    在設(shè)計上System.Drawing.Common 是 Windows 技術(shù)的精簡包裝器,因此其跨平臺實現(xiàn)欠佳。

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

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

    解決方案

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

    可以忽略這個警告。

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

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

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

    如:

    • ImageSharp

    • SkiaSharp

    需要注意的是,這些庫并不與System.Drawing.Common的API兼容,所以更換相應(yīng)的庫之后需要重新編寫相關(guān)代碼。

    關(guān)于“Net core中怎么使用System.Drawing對上傳的圖片流進(jìn)行壓縮”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識,可以關(guān)注億速云行業(yè)資訊頻道,小編每天都會為大家更新不同的知識點。

    向AI問一下細(xì)節(jié)

    免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

    AI