图片自动压缩

1,410 阅读3分钟

在进行包大小优化工作时,压缩图片的大小是其中一个重要的环节。而要压缩的图片包括本地项目中的图片和之后要新增到项目中的图片。所以压缩图片分为两个部分:

  1. 遍历项目中的所有图片,压缩后替换原图片
  2. 每次git提交代码前,如果有新增图片,进行压缩后再提交

压缩本地项目中的图片

require "fileutils"
require "find"
require "tinify"

t = Time.now
$image_count = 0
$total_size = 0
$total_after_size = 0
$fail_count = 0
$success_count = 0
$success_file_name = "successLog.txt"
$fail_file_name = "failLog.txt"
compress_dir = "/Users/zhouweijie1/Documents/test/Expression.xcassets" #将要压缩的文件夹路径放这
# 获取白名单列表路径
$white_list_path = "#{Dir.pwd}/gitHooks/imageCompressWhiteList.txt"

$keys = ['tbfVHxRmxxR3Vb3XQwrxMbfHPNnxszpH', 'B83mGyQcbpmFzz1Qym5ZdhT3Ss503b5b', 'L1DfbF8kpRzstlMfbvmkvCSg6knkQD71', '2L6km1p5yJRZsNYs0GJ6m4klL1rMJ4RJ', '5wmc8dDxY1WKg4DTPSLXQ20dWWjRbzyG', '1DkYWCXDvPJfMrNbV6NPB0QpQTGzZLfD', 'bRG9yXbc07w77sP43gqjgP8tlgDPjdVJ', 'xwvXrTp2pSJYWDjkHQ7wTBTxDMbLdx4r', '4pFYmxVBK6vnpKR5hh8r0hD4BGmS75K4', '6rSpQHxHpygLyZMQnTH6WNjxGVV9mt0x']
$keys_index = -1

def setup_key
    $keys_index += 1
    Tinify.key = $keys[$keys_index]
    Tinify.validate! # validate后会更新compression_count
    if $keys_index == $keys.length
        puts "本月所有免费使用次数都用完,请增加key"
    elsif Tinify.compression_count >= 500
        setup_key
    end
end

def write_log(fail, success)
    if success != 0
        file = File.new($success_file_name, "a")
        file.syswrite("#{success}\n")
    end
    if fail != 0
        file = File.new($fail_file_name, "a")
        file.syswrite("#{fail}\n")
    end
end

def compress(image_name)
    begin
        # Use the Tinify API client.
        origin_size = File.size(image_name)
        Tinify.from_file(image_name).to_file(image_name)
        log = image_name + "\n#{origin_size} bit" + " -> " + "#{File.size(image_name)} bit"
        puts log + ":#{Time.now}"
        write_log(0, log)
        $success_count += 1
    rescue Tinify::AccountError
        # Verify your API key and account limit.
        setup_key
        print("失效的key:" + Tinify.key + "\n")
        compress(image_name)
    rescue Tinify::ClientError => e
        # Check your source image and request options.
        log = image_name + "\nClientError:#{e.message}"
        puts log + ":#{Date.now}"
        write_log(log, 0)
        $fail_count += 1
    rescue Tinify::ServerError => e
        # Temporary issue with the Tinify API.
        log = image_name + "\nServerError:#{e.message}"
        puts log + ":#{Date.now}"
        write_log(log, 0)
        $fail_count += 1
    rescue Tinify::ConnectionError => e
        # A network connection error occurred.
        log = image_name + "\nConnectionError:#{e.message}"
        puts log + ":#{Date.now}"
        write_log(log, 0)
        $fail_count += 1
    rescue => e
        # Something else went wrong, unrelated to the Tinify API.
        log = image_name + "\nOtherError:#{e.message}"
        puts log + ":#{Time.now}"
        write_log(log, 0)
        $fail_count += 1
    end
end
# 检测到文件夹中所有PNG和JPEG图片并压缩
def traverse_dir(file_path)
    setup_key
    Dir.glob(%W[#{file_path}/**/*.png #{file_path}/**/*.jpeg]).each do |image_name|
        $total_size += File.size(image_name)
        # compress(image_name)
        $total_after_size += File.size(image_name)
        $image_count += 1
    end
end

traverse_dir(compress_dir)
time = "时间:#{Time.now - t}s from #{t} to #{Time.now}"
count = "图片总数:#{$image_count},本次压缩图片数:#{$image_count}, 成功图片数:#{$success_count},失败图片数:#{$fail_count}"
size = "之前总大小:#{$total_size/1024.0} k,之后总大小:#{$total_after_size/1024.0} k,优化大小:#{($total_size - $total_after_size)/1024.0}"
puts time
puts count
puts size
write_log(0, time)
write_log(0, count)
write_log(0, size)
complete = "压缩完毕!!!"
if $fail_count != 0
    complete += "有#{$fail_count}张图片失败,请查看:#{File.absolute_path($fail_file_name)}"
end
puts complete

# 检查key的免费使用次数
def check_keys_status
    $keys.each do |key|
        begin
            Tinify.key = key
            Tinify.validate!
            puts "#{key}:#{Tinify.compression_count}"
        rescue
        end
    end
end
# 白名单
def ignore?
    file = File.new($white_list_path, "a+")
    file.readlines.each { |line|
        line_without_white_space = line.strip
        if line_without_white_space.length > 0
            result = $image_path.match?(line_without_white_space)
            if result
                return true
            end
        end
    }
    return false
end

压缩即将提交的图片

要压缩即将提交的图片,就要使用git hook拦截代码提交动作,将pre-commit文件放到.git/hooks文件中就行了。pre-commit文件中的代码逻辑为获取当前提交的内容,遍历是否是图片,是的话就执行压缩脚本:

#!/bin/sh

#检测是否为最初提交
if git rev-parse --verify HEAD >/dev/null 2>&1
then
    against=HEAD
else
    # Initial commit: diff against an empty tree object
    against=4b825dc642cb6eb9a060e54bf8d69288fbee4904
fi

# If you want to allow non-ASCII filenames set this variable to true.
git config hooks.allownonascii true

# Redirect output to stderr.
exec 1>&2

#获取.git所在目录
git_path=$(cd "$(dirname "$0")";cd ..;cd ..; pwd)
#获取当前分支名
branch=$(git symbolic-ref --short HEAD)

# 得到修改过的代码的文件列表
git diff --cached --name-only --diff-filter=ACMR -z $against | while read -d $'\0' f; do
if [[ $f == *".png" || $f == *".jpg" || $f == *".jpeg" ]];then
    #拼接文件绝对路径
    path="$(cd "$(dirname "$0")";cd ..;cd ..; pwd)/$f"
    pattern='/Pods/'
    pathStr="$path"
  if [[ ! ($pathStr =~ $pattern) ]]; then
    #执行压缩脚本
    ruby "$git_path/gitHooks/imageCompressor.rb" $path $branch
    git add $f
  fi
fi

done

压缩脚本单独放在一个文件中,内容如下:

require "tinify"

$keys = %w[tbfVHxRmxxR3Vb3XQwrxMbfHPNnxszpH B83mGyQcbpmFzz1Qym5ZdhT3Ss503b5b L1DfbF8kpRzstlMfbvmkvCSg6knkQD71 2L6km1p5yJRZsNYs0GJ6m4klL1rMJ4RJ 5wmc8dDxY1WKg4DTPSLXQ20dWWjRbzyG 1DkYWCXDvPJfMrNbV6NPB0QpQTGzZLfD bRG9yXbc07w77sP43gqjgP8tlgDPjdVJ xwvXrTp2pSJYWDjkHQ7wTBTxDMbLdx4r 4pFYmxVBK6vnpKR5hh8r0hD4BGmS75K4 6rSpQHxHpygLyZMQnTH6WNjxGVV9mt0x]
$keys_index = -1
$image_path = ARGV[0]
$branch_name = ARGV[1]
# 获取.git所在目录
git_path = `git rev-parse --git-dir`; git_path = git_path.strip;
# 获取当前文件所在目录
cur_path = `printf $(cd '#{git_path}'; cd ..; pwd)/gitHooks`; cur_path = cur_path.strip;
$white_list_path = "#{cur_path}/imageCompressWhiteList.txt"
def setup_key
    $keys_index += 1
    Tinify.key = $keys[$keys_index]
    Tinify.validate! # validate后会更新compression_count
    if $keys_index == $keys.length
        puts "本月所有免费使用次数都用完,请增加key"
    elsif Tinify.compression_count >= 500
        setup_key
    end
end

def ignore?
    file = File.new($white_list_path, "a+")
    file.readlines.each { |line|
        line_without_white_space = line.strip
        if line_without_white_space.length > 0
            result = $image_path.match?(line_without_white_space)
            if result
                return true
            end
        end
    }
    return false
end

begin
    # Use the Tinify API client.
    result = ignore?
    if result
        puts "图片在白名单中,不压缩:" + $image_path
    else
        setup_key
        Tinify.from_file($image_path).to_file($image_path)
        puts "图片压缩成功:" + $image_path
    end
rescue Tinify::AccountError
    # Verify your API key and account limit.
    setup_key
rescue Tinify::ClientError => e
    # Check your source image and request options.
    puts "图片压缩失败:" + $image_path + ", ClientError:#{e.message}"
rescue Tinify::ServerError => e
    # Temporary issue with the Tinify API.
    puts "图片压缩失败:" + $image_path + ", ServerError:#{e.message}"
rescue Tinify::ConnectionError => e
    # A network connection error occurred.
    puts "图片压缩失败:" + $image_path + ", ConnectionError:#{e.message}"
rescue => e
    # Something else went wrong, unrelated to the Tinify API.
    puts "图片压缩失败:" + $image_path + ", OtherError:#{e.message}"
end

如果某张图片不需要或者不能压缩,需要将图片名放到白名单中,白名单格式如下:

test_expression_100fen@3x.png
test_expression_666@3x.png
expression_100fen@3x.png

上面提到将pre-commit文件放到.git/hooks文件中就可以实现提交拦截,也可以用脚本完成这个操作: 文件名:setupGitHook.rb

#!/usr/bin/ruby
require "Fileutils"

# 获取.git所在目录
git_path = `git rev-parse --git-dir`; git_path = git_path.strip;
# 获取当前文件所在目录
cur_path = `printf $(cd '#{git_path}'; cd ..; pwd)/gitHooks`; cur_path = cur_path.strip;
puts "gitPath:#{git_path}"
puts "cur_path:#{cur_path}"
# .git目录下没有hooks文件夹时新建一个
if Dir.exist?("#{git_path}/hooks") == false
  FileUtils.mkpath("#{git_path}/hooks")
end
# 将当前文件夹中pre-commit文件拷贝到.git/hooks目录下
FileUtils.cp("#{cur_path}/pre-commit", "#{git_path}/hooks/pre-commit")

当同事很多时,比如有四十多个,让每个人都在项目目录下执行一遍setupGitHook.rb,每个同事都来问一遍就比较麻烦了。所以可以添加一个运行脚本,运行项目时自动执行就可以了:

# Type a script or drag a script file from your workspace to insert its path.

# 获取gitHooks文件夹位置

gitHooks_path=$(**cd** "$(git rev-parse --git-dir)"; **cd** ..; **pwd**;)/gitHooks

ruby $gitHooks_path/setupGitHook.rb

如下图:

image.png

Demo地址:github.com/Wejua/Demos…