概述
在日常应用中,图片的处理是一个经常需要解决的问题,不论是在web前端还是后台(如博客),对于一张高清大图可能面临到加载缓慢、占用带宽过高等问题。本文将通过Golang来实现一种图片的渐进式加载和压缩处理,以解决这些问题。图片渐进式加载是指通过在缩略图中使用渐进式处理算法,使得在网络环境差的情况下,也可以快速的展示图片的轮廓和大概的内容,对于用户体验有很大的提升。
环境及依赖
Golang版本: 1.16.2
使用的相关依赖包: github.com/disintegration/imaging
使用Golang对图片进行操作,需要安装相关依赖包。本文中使用的是github上的disintegration/imaging包,该包是一个用来处理图片和图像的基本包,支持图片压缩、裁剪、旋转等功能。
渐进式加载实现
1. 读取原始图片并重新编码
在渐进式加载的实现过程中,我们需要将需要进行处理的原始图片重新编码为webp格式。webp是一种基于WebM格式的谷歌专利的免费的、开放源代码的图像格式,旨在提供更高的压缩率和更好的图像质量。
func getProgressiveImage(rawImgDir, progressiveImgDir string) error {
file, err := os.Open(rawImgDir)
if err != nil {
return err
}
defer file.Close()
img, _, err := image.Decode(file)
if err != nil {
return err
}
err = ioutil.WriteFile(progressiveImgDir, []byte(""), 0666)
if err != nil {
return err
}
writer, err := webp.NewEncoderFile(progressiveImgDir, webp.EncoderPreset(4))
if err != nil {
return err
}
err = writer.Encode(img)
if err != nil {
return err
}
writer.Close()
return nil
}
通过os.Open()读取需要进行处理的图片,使用image.Decode解码,然后使用webp.NewEncoderFile()创建一个webp编码器,将图片及编码器的实例传输给Encode()进行编码处理。最后返回的就是处理好的webp图片对象。需要注意的是,此时的图片已经由原来的类型,例如jpg,转换成了webp类型。
2. 生成渐进式图片
有了处理好的webp图片以后,我们需要对其进行渐进式处理,使得在加载图片时,使用者可以较快地看到图片轮廓。后续的图片细节渲染则是由更高质量的图片按需加载来完成。
func getProgressiveJpeg(rawImgDir, progressiveJpegDir string) error {
img, err := imaging.Open(rawImgDir)
if err != nil {
return err
}
progressiveImg := imaging.Encode(img, imaging.JPEGEncoder(writers.Options{Quality: 90}))
os.Remove(progressiveJpegDir)
outputFile, err := os.OpenFile(progressiveJpegDir, os.O_WRONLY|os.O_CREATE, 0666)
if err != nil {
return err
}
defer outputFile.Close()
writer, err := progressivejpeg.NewWriter(outputFile)
if err != nil {
return err
}
writer.SetBaseline(qualityFilter(90))
defer writer.Close()
_, err = outputFile.Seek(0, io.SeekStart)
if err != nil {
return err
}
_, err = io.Copy(writer, bytes.NewReader(progressiveImg))
if err != nil {
return err
}
return nil
}
这里的实现采用disintegration/imaging包的Encode()方法对图片重新编码,同时采用progressivejpeg包将其生成为渐进式Jpeg格式,以实现更快速的展示。其中, progressivejpeg.NewWriter()方法会将传入的io.Writer接口封装成一个用于逐渐的写入JPEG文件的Writer。而接下来的writer.SetBaseline()方法就是设置基线过滤器,并且返回一个基于导入器质量的过滤器。通过qualityFilter()函数进行过滤,采用90的输出质量。
3. Webp图片压缩
有些情况下,图片过大可能会造成带宽满载和页面加载过慢的情况,因此采用图片压缩方式可以有效解决这个问题。下面针对webp图片格式实现压缩处理。
func resizeImage(size int, inputPath, outputPath string, q float32, progressive bool) error {
img, err := imaging.Open(inputPath)
if err != nil {
return err
}
dstSize := size
if img.Bounds().Max.X < size && img.Bounds().Max.Y < size {
dstSize = img.Bounds().Max.X
if img.Bounds().Max.Y < dstSize {
dstSize = img.Bounds().Max.Y
}
dstSize = int(float32(dstSize) / q)
}
var finalImg *image.NRGBA
if progressive {
finalImg = imaging.New(dstSize, dstSize, color.NRGBA{0x0, 0x0, 0x0, 0x0})
} else {
finalImg = imaging.Resize(img, dstSize, dstSize, imaging.Lanczos)
}
opts := webp.Options{
Quality: qualityFilter(int(q * 100)),
TargetSize: 500,
TargetPSNR: 42,
Method: webp.PreprocessingMixed,
ImageHint: webp.Photo,
AlphaQuality: 7,
FilterStrength: 6,
FilterSharpness: 0,
FilterType: webp.AutoFilter,
ShowCompressed: true,
Segments: 4,
}
err = webp.EncodeFile(outputPath, finalImg, &opts)
if err != nil {
return err
}
return nil
}
在压缩的实现中,采用了imaging.Resize()方法对图片进行一定的缩小处理,来达到压缩的效果。同时,设置了压缩的配置选项,包括压缩质量、压缩大小、压缩方法、颜色历史记录等等。其中qualityFilter()是一个辅助函数,用于将压缩质量从0-100映射到0-9。
总结
在本文中,我们介绍了使用Golang对图片进行渐进式加载和压缩处理。其中,通过disintegration/imaging包和progressivejpeg包实现了图片的编码和渐进式处理,而通过webp包实现了对webp格式图片的压缩处理。通过这些操作,我们可以在保证图片质量的同时,提升了图片加载的速度,并且减小了图片占用的带宽。