
簡介基于VB開發(fā)的圖片縮略圖生成與壓縮程序源碼目標明確主要面向?qū)B圖像處理感興趣的新手以及希望快速實現(xiàn)圖片壓縮與縮略圖功能的開發(fā)人員。程序完整演示了從本地讀取圖片、按比例生成縮略圖、設定壓縮參數(shù)、保存輸出結果的全過程可應用于批量生成縮略圖、網(wǎng)站上傳前圖片壓縮等實際場景。資源包共32個文件壓縮后約286KB以vb源文件、resx資源文件、exe可執(zhí)行程序、config配置和vbproj工程文件為主要構成相關注釋清晰工程結構緊湊便于按模塊研讀和二次開發(fā)。已有351人學習瀏覽說明該案例在VB圖像處理入門與借鑒方面具有一定熱度。用戶可獲得一套可直接運行的VB工程覆蓋界面交互設計、圖片編碼保存、異常處理與調(diào)試運行等關鍵環(huán)節(jié)結合源代碼注釋能較快掌握縮略圖生成和圖片壓縮的核心寫法也為后續(xù)自主開發(fā)圖片處理工具提供現(xiàn)成的實現(xiàn)參考適合VB課程設計或日常工具開發(fā)時直接參考。1. 為什么 VB 生成縮略圖還在被需要維護老一套 WinForms 圖庫系統(tǒng)時最煩的不是數(shù)據(jù)庫慢而是用戶直接上傳 5MB 原圖導致列表頁卡死。給圖片生成縮略圖是對讀圖體驗成本最低的優(yōu)化。這份基于 VB 寫的“圖片生成縮略圖”程序源碼核心是用 GDI 把原圖按比例繪制到新位圖并保存附帶完整注釋適合剛從 VB6 轉(zhuǎn) VB.NET 的新手也適合需要快速做一個圖片壓縮工具的維護型開發(fā)人員。縮略圖不等于簡單把 PictureBox 拉小這里涉及插值算法、JPEG 質(zhì)量參數(shù)和文件釋放值得逐行看。程序老媛出品的一貫特點是親測校正、質(zhì)量保證拿它能省去不少排查時間。2. 縮略圖生成的基礎GDI 的 Graphics.DrawImage 與插值算法2.1 PictureBox 縮放不等于生成縮略圖很多新手會把PictureBox.SizeMode StretchImage當成縮略圖方案但這樣只是改變了控件顯示尺寸并沒有生成新的圖片文件。真要產(chǎn)生縮略圖必須創(chuàng)建一個獨立的目標位圖用Graphics.DrawImage將原圖畫進去再Save到磁盤。PictureBox的 StretchImage 走的是控件繪制路徑縮放質(zhì)量受控件默認參數(shù)限制縮到 160×120 之后文字邊緣會明顯發(fā)虛而且你無法直接拿到壓縮后的字節(jié)流。正確的做法是繞開控件直接操作System.Drawing。整個過程包括讀取原圖、計算目標尺寸、創(chuàng)建畫布、設置插值模式、繪制、編碼保存。下面先講清楚畫質(zhì)相關的三個關鍵參數(shù)否則代碼寫得再多輸出也是“能看但沒法用”的圖。2.2 決定縮放質(zhì)量的三個 GDI 參數(shù)在System.Drawing.Drawing2D命名空間里與縮放畫質(zhì)直接相關的是InterpolationMode、SmoothingMode和PixelOffsetMode。這三者解決的問題完全不同插值模式?jīng)Q定縮放時像素怎么補齊平滑模式影響線條和邊緣抗鋸齒像素偏移模式?jīng)Q定繪制位置是否落在像素中心。參數(shù)常用枚舉值作用建議InterpolationModeHighQualityBicubic / HighQualityBilinear / NearestNeighbor決定像素補齊方式影響縮放清晰度照片用 HighQualityBicubic像素圖標用 NearestNeighborSmoothingModeHighQuality / AntiAlias / None控制線條抗鋸齒對位圖縮放影響較小生成透明 PNG 時設 HighQualityPixelOffsetModeHighQuality / Half / Default修正像素中心偏移避免出現(xiàn)半透明邊緣透明圖片建議 HighQualityHighQualityBicubic會參考周圍 4×4 像素做加權在縮小場景下能保留更多細節(jié)但計算量也最大HighQualityBilinear只參考 2×2 像素速度更快但更容易出現(xiàn)鋸齒和摩爾紋。對這個圖片壓縮工具來說選擇HighQualityBicubic是通用性最好的方案。如果縮略圖來源是二維碼、驗證碼這種二值圖反而應該用NearestNeighbor否則邊緣會出現(xiàn)一圈灰邊。注意這三個枚舉都在System.Drawing.Drawing2D命名空間下使用前需要Imports System.Drawing.Drawing2D否則 VB.NET 編輯器會報未定義類型。2.3 先算比例再畫圖防止變形最常見的錯誤是拿到maxW和maxH后直接g.DrawImage(src, 0, 0, maxW, maxH)這樣會把豎圖壓扁橫圖拉高。正確做法是先計算原寬高與目標上限的縮放比例取較小值并限制不允許放大Private Function CalcThumbSize(srcW As Integer, srcH As Integer, maxW As Integer, maxH As Integer) As Size 取兩個比例中較小的保證縮略圖完整落在最大矩形內(nèi) Dim ratio As Double Math.Min(maxW / srcW, maxH / srcH) If ratio 1.0 Then ratio 1.0 原圖比目標小時不放大 Return New Size( Math.Max(1, CInt(Math.Round(srcW * ratio))), Math.Max(1, CInt(Math.Round(srcH * ratio))) ) End Function這段代碼的關鍵在Math.Min(maxW / srcW, maxH / srcH)。橫向縮放比例和縱向縮放比例取較小者能保證兩個方向都不超出目標矩形。ratio 1.0時說明原圖本身比目標還小縮略圖場景下放大沒有意義只會增加文件體積所以直接限制為1.0。Math.Max(1, ...)防止極端情況下計算結果是 0導致New Bitmap(0, 0)拋異常。需要注意 VB 的/是浮點除法\才是整數(shù)除法。這里必須用/否則maxW / srcW在整數(shù)除法下會得到截斷值寬高比稍小于 1 時直接變成 0。調(diào)試時看到尺寸翻倍或全黑先檢查是不是這里寫成了反斜杠。2.4 最小可運行繪制流程拿到目標尺寸后最小繪制流程可以寫成下面這個樣子。這個結構會貫穿整個源碼創(chuàng)建畫布、設置質(zhì)量參數(shù)、繪制、保存每一步都不能省。Using src As New Bitmap(sourcePath) Dim thumbSize As Size CalcThumbSize(src.Width, src.Height, 160, 120) Using thumb As New Bitmap(thumbSize.Width, thumbSize.Height) Using g As Graphics Graphics.FromImage(thumb) g.InterpolationMode Drawing2D.InterpolationMode.HighQualityBicubic g.Clear(Color.White) g.DrawImage(src, 0, 0, thumbSize.Width, thumbSize.Height) End Using thumb.Save(targetPath, Imaging.ImageFormat.Jpeg) End Using End UsingUsing是 .NET 的資源釋放保證。Bitmap從文件加載時會鎖定源文件只有Dispose后其他進程才能改寫或刪除Graphics是 GDI 句柄的封裝不釋放會造成句柄泄漏。DrawImage的重載參數(shù)是(Image, x, y, width, height)由于寬高比已經(jīng)算過不會變形。g.Clear(Color.White)必須放在DrawImage之前它會把畫布刷成白色同時解決透明 PNG 轉(zhuǎn) JPEG 時黑底的問題。2.5 透明通道和背景色的處理策略2.4 使用白色背景只是其中一種策略。生成 PNG 縮略圖時可以不調(diào)用Clear讓透明區(qū)域保留但保存成 JPEG 時必須先刷一層底色因為 JPEG 不支持 Alpha 通道。如果原圖是帶透明通道的 PNGDrawImage繪制時會與畫布底色做 alpha 混合所以最終呈現(xiàn)的背景就是你設置的顏色而不是原圖里的透明區(qū)域。這里有個容易忽略的細節(jié)PixelOffsetMode設為HighQuality后縮小透明圖標偶爾會在邊緣出現(xiàn)半透明像素導出成 JPG 后會變成一圈灰邊。解決辦法是先把畫布底色設成與頁面背景接近的顏色或者用PixelOffsetMode.Half讓像素中心對齊。對圖標類資源壓縮這個參數(shù)比SmoothingMode影響更明顯調(diào)試時可以先只改它對比輸出文件。3. 源碼拆解Form1.vb 里的圖片加載、縮放與保存實現(xiàn)3.1 從 .sln 和 .vbproj 看這套源碼的形態(tài)拿到源碼包第一眼看到WindowsApp4.sln和WindowsApp4.vbproj可以確定這是 Visual Studio 的 VB.NET Windows Forms 應用不是 VB6 的.vbp工程。WindowsApp4是新建項目時的默認命名空間作者沒有重命名但不影響編譯運行。.vs文件夾是 VS 緩存obj和bin是編譯輸出這三個目錄刪除后重新生成即可不影響源碼可用性。文件/目錄作用能否刪除WindowsApp4.sln / WindowsApp4.vbproj解決方案和項目入口否Form1.vb / Form1.Designer.vb主窗體邏輯與設計器生成代碼否My Project程序集信息、設置、資源管理按需ResourceHome.png內(nèi)置資源圖片可能是示例圖或按鈕背景可替換bin / obj編譯中間文件與輸出程序集可刪Form1.vb和Form1.Designer.vb的分離是有意設計Designer.vb由 IDE 維護保存按鈕、PictureBox 等控件的屬性設置手寫邏輯應放在Form1.vb。新手經(jīng)常直接改Designer.vb重新生成解決方案時被 IDE 覆蓋找半天原因。真正的生成邏輯應該放在Form1.vb的按鈕事件或獨立函數(shù)里。3.2 核心邏輯加載原圖并生成縮略圖這類源碼最核心的部分是一個可復用的生成函數(shù)。簽名大致是GenerateThumbnail(srcPath, dstPath, maxW, maxH)傳入源圖片路徑、目標路徑和尺寸上限返回布爾值表示是否成功。完整實現(xiàn)如下Public Function GenerateThumbnail(srcPath As String, dstPath As String, maxW As Integer, maxH As Integer) As Boolean Try Using src As New Bitmap(srcPath) 讀取原圖 Dim ratio As Double Math.Min( maxW / src.Width, maxH / src.Height) If ratio 1.0 Then ratio 1.0 Dim tw As Integer Math.Max(1, CInt(Math.Round(src.Width * ratio))) Dim th As Integer Math.Max(1, CInt(Math.Round(src.Height * ratio))) Using thumb As New Bitmap(tw, th) 創(chuàng)建目標畫布 Using g As Graphics Graphics.FromImage(thumb) g.InterpolationMode Drawing2D.InterpolationMode.HighQualityBicubic g.SmoothingMode Drawing2D.SmoothingMode.HighQuality g.PixelOffsetMode Drawing2D.PixelOffsetMode.HighQuality g.Clear(Color.White) g.DrawImage(src, 0, 0, tw, th) End Using If Not SaveJpeg(thumb, dstPath, 85L) Then Return False End Using End Using Return True Catch ex As Exception Debug.WriteLine(GenerateThumbnail: ex.Message) Return False End Try End FunctionNew Bitmap(srcPath)只接受 GDI 能解碼的圖片格式遇到損壞文件或 CMYK 的 TIFF 可能拋異常所以整段放在Try/Catch中并返回False。g.Clear(Color.White)必須在DrawImage之前否則刷色會覆蓋繪制結果。三個質(zhì)量參數(shù)與第二章參數(shù)表對應SaveJpeg是下一步要講的質(zhì)量控制函數(shù)。3.3 用 EncoderParameters 控制 JPEG 壓縮比直接調(diào)thumb.Save(path, ImageFormat.Jpeg)雖然能保存但壓縮質(zhì)量走的是 GDI 默認值不可調(diào)生成的縮略圖往往體積偏大。要控制圖片壓縮程度必須先找到 JPEG 編碼器再傳入EncoderParametersPrivate Function SaveJpeg(img As Bitmap, path As String, quality As Long) As Boolean Dim codec As ImageCodecInfo Nothing For Each c As ImageCodecInfo In ImageCodecInfo.GetImageEncoders() If c.FormatID.Equals(Imaging.ImageFormat.Jpeg.Guid) Then codec c Exit For End If Next If codec Is Nothing Then Return False Using eps As New Imaging.EncoderParameters(1) eps.Param(0) New Imaging.EncoderParameter( Imaging.Encoder.Quality, quality) img.Save(path, codec, eps) End Using Return True End FunctionGetImageEncoders()返回系統(tǒng)已注冊的所有圖片編碼器包括 BMP、JPEG、PNG、TIFF 等。通過FormatID匹配比按MimeType字符串匹配穩(wěn)定不會因為系統(tǒng)語言環(huán)境不同而失效。EncoderParameter(Encoder.Quality, quality)的第二個參數(shù)是Long類型范圍 0 到 100。85L 是通用平衡點網(wǎng)頁預覽場景體積和畫質(zhì)都比較合適如果想更小可以降到 70L但會出現(xiàn)明顯的壓縮色塊。3.4 從源碼注釋里最容易踩的坑我在檢查這類源碼時通常先看三個位置文件釋放、透明背景、控件引用。首先Bitmap和Graphics必須用Using管理。漏掉文件釋放時批量處理同一個目錄會報“文件正由另一進程使用”必須重啟程序才能解鎖源圖。其次透明 PNG 轉(zhuǎn) JPEG 前必須Clear畫布。如果不調(diào)用默認底色是黑色透明區(qū)域會變成黑塊。Clear(Color.White)是大多數(shù)預覽場景的正確選擇。第三不要直接把PictureBox.Image指向一個即將釋放的Bitmap。控件只是持有引用底層圖像被Dispose后下一次重繪會出現(xiàn)空白或異常。常見做法是在下一次加載前讓控件繼續(xù)保持舊引用或者對圖像調(diào)用Clone后交給控件管理。3.5 把最大寬高與壓縮質(zhì)量暴露到窗體上固定 160×120 的源碼只能算 demo實際使用時需要把參數(shù)暴露到界面上。常見做法是在窗體上加NumericUpDown控件控制最大寬高加TrackBar控制壓縮質(zhì)量。按鈕事件里這樣取值Dim ok As Boolean GenerateThumbnail( txtSource.Text, txtTarget.Text, CInt(nudMaxW.Value), CInt(nudMaxH.Value) )NumericUpDown.Value類型是Decimal傳給 Integer 參數(shù)前必須CInt。TrackBar.Value本身就是 32 位整數(shù)可以直接傳給SaveJpeg的質(zhì)量參數(shù)替換掉寫死的85L。把參數(shù)開放出來之后同一個工具既能生成 160×120 的列表縮略圖也能生成 800×600 的詳情頁預覽圖不再需要為每種尺寸重新編譯一次。4. 批量生成與縮略圖驗證的進階技巧4.1 批量處理整個目錄的循環(huán)寫法單張?zhí)幚砗笙乱徊阶匀皇桥俊1闅v文件夾并調(diào)用GenerateThumbnail時建議先快速檢查分辨率跳過原本就很小的小圖Dim folder As String C:\Pictures For Each file As String In Directory.GetFiles(folder, *.jpg) Try Using probe As New Bitmap(file) If probe.Width 800 AndAlso probe.Height 800 Then Continue For End Using GenerateThumbnail(file, Path.Combine(folder, thumbs, Path.GetFileNameWithoutExtension(file) _thumb.jpg), 160, 120) Catch ex As Exception Debug.WriteLine(file : ex.Message) End Try NextGetFiles(folder, *.jpg)只匹配小寫.jpg擴展名不匹配.jpeg。要支持多種格式可以把*.jpg、*.jpeg、*.png三個結果拼接再去重。文件數(shù)量上十萬張時建議改用Directory.EnumerateFiles它邊遍歷邊處理不會一次性把所有路徑載入內(nèi)存。Continue For會先跳出當前迭代同時Using塊正確釋放探針位圖。4.2 修正手機相片的 EXIF 方向手機上傳的圖片經(jīng)常出現(xiàn)“縮略圖旋轉(zhuǎn) 90 度”的問題。原因不在縮放而在于原圖的 EXIF0x0112屬性記錄了拍攝方向Bitmap繪制時默認忽略它。處理方式是在繪制前把方向修正到像素上Private Sub FixOrientation(ByVal img As Bitmap) If Array.IndexOf(img.PropertyIdList, H112) 0 Then Exit Sub Dim v As Integer BitConverter.ToUInt16( img.GetPropertyItem(H112).Value, 0) Select Case v Case 3 : img.RotateFlip(RotateFlipType.Rotate180FlipNone) Case 6 : img.RotateFlip(RotateFlipType.Rotate90FlipNone) Case 8 : img.RotateFlip(RotateFlipType.Rotate270FlipNone) End Select End SubPropertyIdList是一個整型數(shù)組H112是 274 的十六進制寫法表示方向?qū)傩浴etPropertyItem返回的Value前兩個字節(jié)是小端序短整數(shù)1 表示無旋轉(zhuǎn)3 表示 180 度6 表示需要順時針旋轉(zhuǎn) 90 度8 表示逆時針旋轉(zhuǎn) 90 度。RotateFlip會修改原圖像素必須在生成縮略圖之前調(diào)用。這個函數(shù)對沒有 EXIF 信息的截圖不會產(chǎn)生副作用因為PropertyIdList不包含H112時直接退出。4.3 用代碼驗證生成結果而不是靠肉眼批量生成后用代碼自檢比一張張看快得多Using chk As New Bitmap(targetPath) Debug.Assert(chk.Width 160 AndAlso chk.Height 120) End Using If (New FileInfo(targetPath)).Length 1024 Then Debug.WriteLine(縮略圖過小請檢查原圖是否有效) End If重新打開文件讀取寬高能立刻發(fā)現(xiàn)尺寸計算是否被改壞文件小于 1KB 通常說明原圖本身是純色或讀取失敗。至于 Windows 資源管理器里不顯示縮略圖優(yōu)先確認targetPath這張圖能否用圖片查看器打開能打開就是系統(tǒng)縮略圖緩存問題和生成程序無關。outlook 不顯示縮略圖、pdf 縮略圖補丁、solidworks 重新注冊縮略圖組件都屬于另一個排查分支不要混在編碼邏輯里。把縮略圖目錄加入資源管理器“大圖標”視圖后再對比原圖與_thumb.jpg的字節(jié)數(shù)如果尺寸落在 160×120 以內(nèi)且體積明顯下降就說明生成環(huán)節(jié)沒有問題。本文還有配套的精品資源點擊獲取