图片exif清理程序 & 清除图片缩略图水印

事情是这样的,整理资源时发现部分图片小图显示时是加了缩略图的,只有放大后才会显示,如下。

图片[1]-图片exif清理程序 & 清除图片缩略图水印-死宅屋
图片[2]-图片exif清理程序 & 清除图片缩略图水印-死宅屋

这对于资源用于收藏的人来说,破坏了资源的观赏性是很难忍受的,然后经过我不断找寻,从二进制对比到图片信息exif信息,最后发现是exif里有个隐藏的不可读写的Thumbnail Image字段,只能用exiftool工具读,写入也不能单独写入空缩略图,只能清空exif信息,清空虽然会把摄像机信息拍摄日期之类的清空了,但是也比需要找某张图片研究时,必须要放大才显示原图要好。

图片[3]-图片exif清理程序 & 清除图片缩略图水印-死宅屋
图片[4]-图片exif清理程序 & 清除图片缩略图水印-死宅屋

清除exif的依赖程序:https://exiftool.org/,需要下载解压到C盘,具体看运行截图。

运行截图:

图片[5]-图片exif清理程序 & 清除图片缩略图水印-死宅屋

打包好的程序:

481f5c3b1a155257.7z
7z文件
1.2M
图片[6]-图片exif清理程序 & 清除图片缩略图水印-死宅屋

goalng源码如下:

package main

/*
	清空图片exif
*/

import (
	"bufio"
	"fmt"
	mapset "github.com/deckarep/golang-set"
	"log"
	"os"
	"os/exec"
	"os/signal"
	"path"
	"path/filepath"
	"strings"
	"sync"
)

// 保存全部路径
var exiftoolPath = "C:\\exiftool.exe"

var scanRootPath = ""                  // 搜索的目录
var saveRootName = "exif_clear_output" // 不覆盖原图时,结果输出的目录名
var is_overwrite = false               // 是否覆盖原图               // 质量 0-100
var queueNumber = 5                    // 多线程处理数量
var queueWaitGroup sync.WaitGroup
var imageExt = []interface{}{
	".png",
	".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)

	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)
	var cmd *exec.Cmd
	if is_overwrite {
		cmd = exec.Command(exiftoolPath, "-all=", "-overwrite_original", oldName)
	} else {
		// 判断目录是否存在,不存在创建
		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
		}
		cmd = exec.Command(exiftoolPath, "-all=", "-o", replacePath, oldName)
	}
	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("———— 清空指定目录下图片的exif信息,可选覆盖原图或在输入的执行目录生成一个清空后的版本。压缩成功后检查没问题就可以直接CTRL+X剪切出来覆盖原来的。")
	fmt.Println("———— 需要下载:exiftool 解压到C盘,并命名文件夹名为:exiftool.exe,最终需要能找到这个程序 C:\\exiftool.exe")
	fmt.Println("exiftool:https://exiftool.org/ 下载:Windows Executable: exiftool-12.78.zip")
	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:是 0:不是] 默认0")
	var input_is_overwrite int
	scanIsDebugN, _ := fmt.Scanln(&input_is_overwrite)
	if scanIsDebugN == 0 {
		input_is_overwrite = 0
	}
	if input_is_overwrite == 0 {
		is_overwrite = false
	} else {
		is_overwrite = true
	}
	fmt.Printf("覆盖原图:%v\n\n", is_overwrite)

	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 is_overwrite == false {
		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("运行结束,请检查结果")
	haveError()
}
温馨提示:本文最后更新于2024-03-15 15:53:14,某些文章具有时效性,若有错误或已失效,请在下方留言或者右下角私信。
注意:为了节省大家的时间,不会使用百度云下载也不愿意看解压说明教程、或者遇到不会解压的问题也不右下角私聊站长解决就直接投诉订单的朋友,还是别开通会员了,既浪费了您的时间也浪费了您的精力,感谢理解。解压说明
© 版权声明
THE END
喜欢就支持一下吧
点赞5
留言 共6条

请登录后发表评论

    请登录后查看评论内容