- 博主一直在維護一個導出PDF的服務,但是這個服務導出的PDF文件是真的巨大,動輒就上百MB。這里面主要是圖片占據了大多數體積,所以考慮在導出前壓縮一下圖片。
- Jpeg的圖片壓縮是很好做的,因為jpeg這個協議本身就支持調整圖片質量的。在golang中,我們只需要使用標准庫的image/jpeg,將圖片從二進制數據解碼后,降低質量再編碼為二進制數據即可實現壓縮。而且質量和壓縮比例相對而言還不錯。
func compressImageResource(data []byte) []byte {
img, _, err := image.Decode(bytes.NewReader(data))
if err != nil {
return data
}
buf := bytes.Buffer{}
err = jpeg.Encode(&buf, img, &jpeg.Options{Quality: 40})
if err != nil {
return data
}
if buf.Len() > len(data) {
return data
}
return buf.Bytes()
}
- 比較麻煩的是壓縮PNG圖片,在網上找了很多相關的庫,感覺都沒什么即可以保持質量,又可以盡可能壓縮的辦法。
//下面這兩個庫都比較偏重於轉換圖片大小,在保持寬高不變的情況下,壓縮比例很一般
https://github.com/discord/lilliput //這個庫是一家海外公司基於C語言的一個開源圖片處理庫,但是封裝的很好,不需要安裝額外依賴
https://github.com/disintegration/imaging
//下面這個庫可以對PNG圖片進行較大的壓縮,可惜壓縮比例過大時會嚴重失真
https://github.com/foobaz/lossypng/
- 后來,借鑒一篇博客的做法,還是先把PNG圖片轉換為Jpeg圖片,然后再將jpeg圖片的質量降低。相對上邊這些庫,壓縮比例和質量都比較令人滿意
func compressImageResource(data []byte) []byte {
imgSrc, _, err := image.Decode(bytes.NewReader(data))
if err != nil {
return data
}
newImg := image.NewRGBA(imgSrc.Bounds())
draw.Draw(newImg, newImg.Bounds(), &image.Uniform{C: color.White}, image.Point{}, draw.Src)
draw.Draw(newImg, newImg.Bounds(), imgSrc, imgSrc.Bounds().Min, draw.Over)
buf := bytes.Buffer{}
err = jpeg.Encode(&buf, newImg, &jpeg.Options{Quality: 40})
if err != nil {
return data
}
if buf.Len() > len(data) {
return data
}
return buf.Bytes()
}
- 最后給大家分享一個超級好用PDF處理的golang 庫:
https://github.com/unidoc/unipdf
。一開始使用這個庫將生成后的PDF壓縮的,可以將一個200M的PDF(里面都是圖片)直接壓縮到7M左右。可惜的是這個庫商用需要購買商業版權,所以最后只能采取了導出前壓縮圖片的做法。
- 這個庫沒有授權的情況下會在處理后的PDF中加上水印,這個想去掉也簡單,fork下來改一下代碼就好了。雖然我這里因為是商業的場景不能這么用,但是我還是嘗試了下,倉庫在這:
https://github.com/lianggx6/unipdf
。然后再在go.mod文件中將依賴替換即可。大家如果有個人開發實踐需要的可以直接這樣拿來用,商用務必購買版權。
replace (
github.com/unidoc/unipdf/v3 => github.com/lianggx6/unipdf v0.0.0-20200409043947-1c871b2c4951
)