Net?core中使用System.Drawing對(duì)上傳的圖片流進(jìn)行壓縮(示例代碼)
由于net core 中默認(rèn)沒有System.Drawing,可以通過nuget下載一個(gè)來代替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í),設(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(); } }
通過文件流壓縮圖片
有些時(shí)候我們不想先把圖片保存后,然后在去讀取壓縮,我們想通過文件流就直接對(duì)圖片進(jìn)行壓縮了,比如我們要把圖片上傳到七牛云
先把流進(jìn)行壓縮在上傳到七牛云就比較科學(xué)了
1:首先我們需要通過圖片上傳的流來獲取圖片
foreach (IFormFile file in files)//獲取多個(gè)文件列表集合 { if (file.Length > 0) { //獲取圖片上傳的流 Stream stream = file.OpenReadStream(); //直接從流里邊變成圖片 System.Drawing.Image iSource = System.Drawing.Image.FromStream(stream); } }
2:通過圖片壓縮算法把圖片進(jìn)行壓縮
這里有一個(gè)參數(shù)是輸入流,后面還有一個(gè)是壓縮后的輸出流
/// <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對(duì)象 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) { //可以直接從流里邊得到圖片,這樣就可以不先存儲(chǔ)一份了 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í),設(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) { //可以存儲(chǔ)在流里邊; ob.Save(outstream, jpegICIinfo, ep); } else { ob.Save(outstream, tFormat); } return true; } catch { return false; } finally { iSource.Dispose(); ob.Dispose(); } }
3:把壓縮后的圖片轉(zhuǎn)化成流,很簡(jiǎn)單用一個(gè)內(nèi)存流來中轉(zhuǎn)一下就好了
MemoryStream memoryStream = new MemoryStream(); ob.Save(memoryStream, jpegICIinfo, ep);//這里的ob就是壓縮后的Bitmap對(duì)象
為了驗(yàn)證一下轉(zhuǎn)化是否正確,我們可以把流在轉(zhuǎn)化成圖片然后在圖片進(jìn)行存儲(chǔ)
System.Drawing.Image imgSource = System.Drawing.Image.FromStream(memoryStream); imgSource.Save("url");
如果能夠成功壓縮并成功保存就說明這些步驟都成功了!
這里說一下圖片傳輸?shù)乃悸罚?/p>
圖片文件這種本身是無法進(jìn)行傳輸?shù)模拖窨缯Z(yǔ)言的對(duì)象也是無法進(jìn)行傳輸。但是我們可以事先約定一種標(biāo)準(zhǔn),
讓雙方都可以認(rèn)識(shí)都可以解析的一種標(biāo)準(zhǔn),比如base64,比如對(duì)象的json序列化,比如光纖信號(hào)的光波表示,其實(shí)原理都是一樣。
上傳到七牛云前壓縮圖片
通過上面的方法可以得到一個(gè)輸出流,我們可以通過它進(jìn)行圖片的保存,但是如果直接把這個(gè)輸出流傳遞到七牛云的方法中去,圖片是不能被上傳成功的,存儲(chǔ)大小會(huì)是0kb,說明我們這個(gè)流七牛云的接口識(shí)別不到,也就是約定的內(nèi)容不一樣,我們要改造成七牛云能夠被識(shí)別的狀態(tài)
換一個(gè)方法嘗試,直接用流不行,就從流里邊讀出來字節(jié)數(shù)組試試
//實(shí)例化一個(gè)內(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);//上傳失敗錯(cuò)誤信息 } } else { throw new Exception("圖片壓縮失敗");//上傳失敗錯(cuò)誤信息 }
成功了
換回流試試呢,不應(yīng)該啊。傳遞流進(jìn)去他里邊也應(yīng)該是讀取的直接哇,本質(zhì)上都一樣哇
還是不行,看來得看一下他這個(gè)源碼了,看一下他拿到這個(gè)流過后是怎么去用的,就能針對(duì)性解決問題了
部署問題
在Windows環(huán)境下直接運(yùn)行是沒問題的,但是發(fā)布到Linux上就會(huì)報(bào)錯(cuò)
在Linux中安裝
開始安裝libgdiplus,執(zhí)行【docker ps -a 】查看所有容器
【docker start 容器ID】 將容器運(yùn)行起來
【docker exec -it e90f2b9d448d /bin/bash】進(jìn)入該容器bash界面
執(zhí)行【apt-get update】
【apt-get install -y libgdiplus】安裝libgdiplus類庫(kù)
【ln -s /usr/lib/libgdiplus.so /usr/lib/gdiplus.dll】創(chuàng)建鏈接文件
【eixt】退出docker bash到宿主機(jī)的bash,執(zhí)行 【docker restart 容器ID】,此時(shí)接口已經(jīng)能正確訪問了
上面的方法有個(gè)弊端,假如容器被誤刪,又要重新給容器安裝libgdiplus庫(kù)。
我們可以把修改好的容器制作成鏡像,執(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 6發(fā)布了,但是避免不了新框架帶來各種問題。在以往的跨平臺(tái)應(yīng)用中,往往采用System.Drawing.Common這個(gè)庫(kù)作為圖形編輯組件。
在.NET 6之前,在Linux操作系統(tǒng)中需要用到這個(gè)庫(kù)時(shí),只需要安裝libgdiplus和libc6-dev這兩個(gè)依賴即可。但是在.NET 6中,System.Drawing.Common被歸為Windows特定的庫(kù),編譯時(shí)產(chǎn)生“'Image.xxx()' is only supported on: 'windows'.”這樣的警告。這不是最重要的,嚴(yán)重的是,在Linux中調(diào)用時(shí),會(huì)產(chǎn)生“The type initializer for 'Gdip' threw an exception.”這樣的異常。
產(chǎn)生原因
在設(shè)計(jì)上System.Drawing.Common 是 Windows 技術(shù)的精簡(jiǎn)包裝器,因此其跨平臺(tái)實(shí)現(xiàn)欠佳。
具微軟文檔中描述,在舊的行為上,libgdiplus 是本機(jī)端 System.Drawing.Common 跨平臺(tái)實(shí)現(xiàn)的主要提供程序。 libgdiplus 實(shí)際上是對(duì) System.Drawing.Common 所依賴的 Windows 部分的重新實(shí)現(xiàn)。 該實(shí)現(xiàn)使 libgdiplus 成為一個(gè)重要的組件。 它大約有 30,000 行 C 代碼,大部分未經(jīng)測(cè)試,而且缺少很多功能。 libgdiplus 還具有許多用于圖像處理和文本呈現(xiàn)的外部依賴項(xiàng),例如 cairo、pango 和其他本機(jī)庫(kù)。 這些依賴項(xiàng)使得維護(hù)和交付組件更具挑戰(zhàn)性。 自從包含 Mono 跨平臺(tái)實(shí)現(xiàn)以來,我們已將許多從未得到修復(fù)的問題重定向到 libgdiplus。 相比之下,我們采用的其他外部依賴項(xiàng),例如 icu 或 openssl,都是高質(zhì)量的庫(kù)。 使 libgdiplus 的功能集和質(zhì)量與 .NET 堆棧的其余部分相媲美是不可行的。
在這之后,System.Drawing.Common 將僅在 Windows 窗體和 GDI+ 項(xiàng)目中使用。
解決方案
1、項(xiàng)目不會(huì)在Linux平臺(tái)運(yùn)行,僅在Windows中運(yùn)行
可以忽略這個(gè)警告。
2、通過將 runtimeconfig.json 文件中的 System.Drawing.EnableUnixSupport 運(yùn)行時(shí)配置開關(guān)設(shè)置為 true 來啟用對(duì)非 Windows 平臺(tái)的支持。
{ "runtimeOptions": { "configProperties": { "System.Drawing.EnableUnixSupport": true } } }
3、換用其它支持跨平臺(tái)的圖像處理庫(kù)
如:
需要注意的是,這些庫(kù)并不與System.Drawing.Common的API兼容,所以更換相應(yīng)的庫(kù)之后需要重新編寫相關(guān)代碼。
到此這篇關(guān)于Net core中使用System.Drawing對(duì)上傳的圖片流進(jìn)行壓縮的文章就介紹到這了,更多相關(guān)Net core圖片壓縮內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
深入Lumisoft.NET組件與.NET API實(shí)現(xiàn)郵件發(fā)送功能的對(duì)比分析
本篇文章對(duì)Lumisoft.NET組件與.NET API實(shí)現(xiàn)郵件發(fā)送的功能兩者進(jìn)行了深入的對(duì)比分析。需要的朋友參考下2013-05-05一文輕松了解ASP.NET與ASP.NET?Core多環(huán)境配置對(duì)比
ASP.NET?Core支持在多個(gè)環(huán)境中管理應(yīng)用程序,如開發(fā)(Development),預(yù)演(Staging)和生產(chǎn)(Production),下面這篇文章主要給大家介紹了關(guān)于ASP.NET與ASP.NET?Core多環(huán)境配置對(duì)比?的相關(guān)資料,需要的朋友可以參考下2022-04-04WCF如何綁定netTcpBinding寄宿到控制臺(tái)應(yīng)用程序詳解
這篇文章主要給大家介紹了關(guān)于WCF如何綁定netTcpBinding寄宿到控制臺(tái)應(yīng)用程序的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用WCF具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧2019-07-07關(guān)于.net環(huán)境下跨進(jìn)程、高頻率讀寫數(shù)據(jù)的問題
最近老大教給我一個(gè)項(xiàng)目,項(xiàng)目要求高頻次地讀寫數(shù)據(jù),數(shù)據(jù)量也不是很大,難點(diǎn)在于這個(gè)規(guī)模的熱點(diǎn)數(shù)據(jù),變化非常頻繁,下面把我的處理方法分享到腳本之家平臺(tái),對(duì).net跨進(jìn)程高頻率讀寫數(shù)據(jù)相關(guān)知識(shí)感興趣的朋友跟隨小編一起學(xué)習(xí)下吧2021-05-05解析Asp.net,C# 純數(shù)字加密解密字符串的應(yīng)用
本篇文章是對(duì)Asp.net,C# 純數(shù)字加密解密字符串的應(yīng)用進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下2013-05-05C#中Dictionary幾種遍歷的實(shí)現(xiàn)代碼
C#中Dictionary幾種遍歷的實(shí)現(xiàn)代碼,需要的朋友可以參考一下2013-02-02Asp.Net 網(wǎng)站性能優(yōu)化之緩字決 (上) 緩沖寫數(shù)據(jù)
通常情況下Asp.Net 網(wǎng)站的底層數(shù)據(jù)存儲(chǔ)都是關(guān)系數(shù)據(jù)庫(kù),關(guān)系數(shù)據(jù)庫(kù)資源比較昂貴,而且也很容易造成瓶頸。緩字決文章就是為大家介紹如何有效使用緩存,異步寫緩沖數(shù)據(jù)庫(kù)的壓力,從而保證網(wǎng)站的性能。2010-06-06Entity?Framework使用配置伙伴創(chuàng)建數(shù)據(jù)庫(kù)
這篇文章介紹了Entity?Framework使用配置伙伴創(chuàng)建數(shù)據(jù)庫(kù)的方法,對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2022-03-03