对于硬盘容量有限且对处理过的图不在意的朋友,可以考虑一下压缩图片,大部分写真图一张就是20-50MB,压缩一下放大后肉眼基本看不出来,可以节省很大一部分空间
(网盘里储存原档,本地压缩节省容量,网盘容量比本地硬盘容量要廉价的多)。
测试压缩质量设置为60,压缩一个476G的目录,压缩后变成了232G。
png图片暂时不能压缩,因为压缩后会丢失很多色彩,我就去掉了。
压缩是基于ImageMagick的,所以需要先下载,安装到C盘。然后下载压缩程序。
下载ImageMagick:https://imagemagick.org/script/download.php
下载压缩程序:
运行界面:
开放我用Golang实现的源码,有能力可自行拓展,不过基本够用了:
package main
/*
压缩图片
*/
import (
"bufio"
"fmt"
mapset "github.com/deckarep/golang-set"
"log"
"os"
"os/exec"
"os/signal"
"path"
"path/filepath"
"strconv"
"strings"
"sync"
)
// 保存全部路径
var imageMagickPath = "C:\\ImageMagick"
var scanRootPath = "" // 搜索的目录
var saveRootName = "compress_output" // 结果输出的目录名
var imageQuality = 60 // 质量 0-100
var queueNumber = 5 // 多线程处理数量
var queueWaitGroup sync.WaitGroup
var imageExt = []interface{}{
".jpg",
".jpe",
".jpg2",
".jpeg",
".bmp",
".dif",
".wmf",
".ico",
".tif",
".tiff",
".webp",
".heic",
".jfif",
}
// 判断文件夹或文件是否存在
func PathExists(path string) (bool, error) {
_, err := os.Stat(path)
if err == nil {
return true, nil
}
if os.IsNotExist(err) {
return false, nil
}
return false, err
}
// 替换文件名称
func mediaCompress(oldName string, queue chan string) {
defer func() {
<-queue
queueWaitGroup.Done()
}()
oldName = strings.ReplaceAll(oldName, "\\", "/")
scanRootPath = strings.TrimRight(strings.ReplaceAll(scanRootPath, "\\", "/"), "/")
lastname := path.Base(oldName)
ext := path.Ext(lastname)
filename := strings.TrimSuffix(lastname, ext)
if ext == ".png" {
log.Println("不处理PNG文件:", oldName)
return
}
lastname = fmt.Sprintf("%s%s", filename, ext)
oldDirName := path.Dir(strings.Trim(strings.Replace(oldName, scanRootPath, "", 1), "/")) // 文件除了扫描路径外的路径
replacePath := fmt.Sprintf("%s/%s/%s/%s", scanRootPath, saveRootName, oldDirName, lastname)
// 判断目录是否存在,不存在创建
if PathExist, _ := PathExists(path.Dir(replacePath)); PathExist == false {
//log.Printf("目录不存在,创建:%s", path.Dir(replacePath))
_ = os.MkdirAll(path.Dir(replacePath), os.ModePerm)
}
if PathExist, _ := PathExists(replacePath); PathExist == true {
log.Println("文件已存在,跳过压缩:", replacePath)
return
}
//log.Println(oldName, ">>>", replacePath)
cmd := exec.Command(fmt.Sprintf("%s/%s", imageMagickPath, "magick.exe"), oldName, "-quality", strconv.Itoa(imageQuality), replacePath)
out, err := cmd.CombinedOutput()
if err != nil {
log.Printf("执行错误:%s %s %s", oldName, string(out), err.Error())
return
}
//log.Println("成功压缩", replacePath)
}
func searchPath(sourcePathname string) {
queue := make(chan string, queueNumber)
sourcePathname = strings.ReplaceAll(sourcePathname, "\\", "/")
err := filepath.Walk(sourcePathname, func(pathstr string, info os.FileInfo, err error) error {
if info.IsDir() == false && strings.Contains(pathstr, "\\"+saveRootName) == false {
ext := strings.ToLower(path.Ext(info.Name()))
switch {
case mapset.NewSetFromSlice(imageExt).Contains(ext):
queue <- pathstr
queueWaitGroup.Add(1)
go mediaCompress(pathstr, queue)
default:
// 其他未知文件不处理
}
}
return err
})
if err != nil {
log.Println("扫描文件列表错误", err.Error())
return
}
queueWaitGroup.Wait()
}
func haveError() {
fmt.Println("结束:可 CTRL+C 或者 关闭窗口")
sigChan := make(chan os.Signal)
signal.Notify(sigChan)
<-sigChan
}
func main() {
scanRootPath = strings.ReplaceAll(scanRootPath, "\\", "/")
fmt.Println("———— 压缩指定目录下的图片,会在输入的执行目录生成一个压缩后的版本。压缩成功后检查没问题就可以直接CTRL+X剪切出来覆盖原来的。")
fmt.Println("———— 需要下载:ImageMagick 安装到C盘,并命名文件夹名为:ImageMagick,最终需要能找到这个程序 C:\\ImageMagick\\magick.exe")
fmt.Println("ImageMagick下载地址:https://imagemagick.org/script/download.php")
fmt.Println("———— 注意:请勿直接输入盘符根目录,否则扫描路径下的全部目录都会压缩。 (CTRL+C可随时终止)")
fmt.Println("———— 以下后缀会进行压缩")
fmt.Printf("支持压缩格式:%v \n\n", imageExt)
fmt.Println("请粘贴要执行的目录地址(回车后执行):")
reader := bufio.NewReader(os.Stdin) // 标准输入输出
scanRootPath, _ = reader.ReadString('\n') // 回车结束
scanRootPath = strings.TrimSpace(scanRootPath)
scanRootPath = strings.Trim(scanRootPath, " ")
if scanRootPath == "" {
log.Println("错误:没有获取到你输入的目录")
haveError()
}
// 判断扫描的目录是否存在
if PathExist, _ := PathExists(scanRootPath); PathExist == false {
log.Printf("扫描的目录不存在 '%s'", scanRootPath)
haveError()
}
fmt.Println("———— 压缩质量(越低图片大小越小) 请输入:1-100 默认为60")
var inputImageQuality int
scanIsDebugN, _ := fmt.Scanln(&inputImageQuality)
if scanIsDebugN == 0 {
inputImageQuality = imageQuality
}
if inputImageQuality > 0 && inputImageQuality <= 100 {
imageQuality = inputImageQuality
} else {
log.Printf("质量输入错误 '%s'", inputImageQuality)
haveError()
}
fmt.Printf("压缩质量:%v\n\n", imageQuality)
fmt.Println("———— 并行数量(越大处理越快) 默认为5,不建议过高")
var inputQueueNumber int
scaninputQueueNumberN, _ := fmt.Scanln(&inputQueueNumber)
if scaninputQueueNumberN == 0 {
inputQueueNumber = queueNumber
}
if inputQueueNumber > 0 && inputQueueNumber <= 100 {
queueNumber = inputQueueNumber
} else {
log.Printf("并行数量 '%s'", inputQueueNumber)
haveError()
}
fmt.Printf("并行数量:%v\n\n", queueNumber)
if PathExist, _ := PathExists(fmt.Sprintf("%s/%s", scanRootPath, saveRootName)); PathExist == false {
err := os.MkdirAll(fmt.Sprintf("%s/%s", scanRootPath, saveRootName), os.ModePerm)
if err != nil {
log.Println("压缩:创建目录错误", err.Error(), fmt.Sprintf("%s/%s", scanRootPath, saveRootName))
haveError()
}
}
fmt.Println("开始执行,请等待执行结果")
searchPath(scanRootPath)
fmt.Println("运行结束,请检查结果", fmt.Sprintf("%s/%s", scanRootPath, saveRootName))
haveError()
}
温馨提示:本文最后更新于
注意:为了节省大家的时间,不会使用百度云下载也不愿意看解压说明教程、或者遇到不会解压的问题也不右下角私聊站长解决就直接投诉订单的朋友,还是别开通会员了,既浪费了您的时间也浪费了您的精力,感谢理解。→解压说明 2023-12-14 18:01:40
,某些文章具有时效性,若有错误或已失效,请在下方留言或者右下角私信。© 版权声明
THE END
请登录后查看评论内容