網站首頁 編程語言 正文
由于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、換用其它支持跨平臺的圖像處理庫
如:
- ImageSharp
- SkiaSharp
需要注意的是,這些庫并不與System.Drawing.Common的API兼容,所以更換相應的庫之后需要重新編寫相關代碼。
原文鏈接:https://www.cnblogs.com/kangsir7/archive/2022/08/19/16602088.html
相關推薦
- 2022-03-07 深入淺析C/C++語言結構體指針的使用注意事項_C 語言
- 2023-03-17 pandas歸一化與反歸一化操作實現_python
- 2022-08-06 python練習之循環控制語句?break?與?continue_python
- 2022-10-01 詳解React?Fiber架構原理_React
- 2022-04-25 .Net?Core?Aop之IResourceFilter的具體使用_實用技巧
- 2022-07-28 Jquery回調對象與延遲對象用法詳解_jquery
- 2022-07-12 Git cherry-pick實現只復制某個commit
- 2022-11-30 Golang迭代如何在Go中循環數據結構使用詳解_Golang
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支