自动裁剪本地文件夹中所有的图片并上传到云服务器(Google Storage)

692 阅读2分钟

这个项目是本地执行的一个脚本,业务人员会把做好的大图素材放到google drive上,然后项目所在的机器会装一个Google drive的app,目的是把google drive上的文件素材同步到本地文件夹中(怎么同步请自行搜索)

我用的是laravel框架,写在了conslole命令行里,定时执行这个脚本,大家也可以放到线上,对接google drive的webhook(我没试),然后根据通知自动触发任务

项目需要安装第三方图片处理包,我用的是Intervention\Image这个包,composer安装下

上代码

<?php

namespace App\Console\Commands;

use Illuminate\Console\Command;
use Intervention\Image\ImageManagerStatic as Image;

class CutImage extends Command
{
    /**
     * The name and signature of the console command.
     *
     * @var string
     */
    protected $signature = 'cut-image';

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = '自动裁剪google drive里的最新修改的图片并上传到Google storage';

    const FILE_PATH = "/home/vagrant/cao";
    const FILE_PATH_TEMP = "/home/vagrant/cao-temp";

    const MID_H = 1024;
    const MID_NAME = 'mid-';
    const SM_H = 512;
    const SM_NAME = 'small-';
    const ICON_H = 128;
    const ICON_NAME = 'icon-';

    const STORAGE_FOLDER = 'wox-materials';
    const STORAGE_BUCKET = 'gs://storage-custom.dollarhoo-test.me/';

    public function handle()
    {
        $fileTemp = escapeshellarg(self::FILE_PATH_TEMP); // 对命令行参数进行安全转义
        $originFile = self::FILE_PATH;
        try {
            //按修改时间过滤文件并且复制到临时文件夹
            $this->filterMoveImageToTemp($originFile);
            //获取临时文件夹里的图片进行裁剪上传才做
            $file = self::FILE_PATH_TEMP;
            $imagePathLists = $this->formatImagePathMap($this->getImageList($file));
        } catch (\Exception $e) {
            echo "++++++++++++++++++++++【发生错误】+++++++++++++++++++++\n";
            echo "错误信息:" . $e->getMessage() . "\n";
            echo "错误文件:" . $e->getFile() . "\n";
            echo "错误行:" . $e->getLine() . "\n";
            echo "+++++++++++++++++++++++++++++++++++++++++++++++++++++\n";
            $imagePathLists = [];
            //删除临时文件夹
            $shell = `rm -rf $fileTemp`;
            echo $shell;
            //todo:钉钉报警
        }
        $picNum = 1;
        $all = count($imagePathLists);
        echo "总共【{$all}】张图片\n";
        sleep(1);
        echo "开始处理第一张图\n";
        foreach ($imagePathLists as $imagePath) {
            try {
                $imageName = array_reverse(explode("/", $imagePath))[0];
                //先上传原图
                $this->moveImageToStorage($imagePath);
                echo "*******************【正在处理$imagePath 】*******************\n";
                //开始裁剪
                $img = Image::make($imagePath);
                $originW = $img->getWidth();
                $originH = $img->getHeight();
                //等比例计算
                $midW = self::MID_H / $originH * $originW;
                $smW = self::SM_H / $originH * $originW;
                $iconW = self::ICON_H / $originH * $originW;
                //mid image
                $img->resize($midW, self::MID_H);
                $mageResizeName = self::MID_NAME . $imageName;
                $midImagePath = str_replace($imageName, $mageResizeName, $imagePath);
                $img->save($midImagePath);
                //google storage sdk ,同步处理好的图片到google storage
                $this->moveImageToStorage($midImagePath);
                echo "*******************【正在处理$midImagePath 】*******************\n";
                //small image
                $img->resize($smW, self::SM_H);
                $mageResizeName = self::SM_NAME . $imageName;
                $smallImagePath = str_replace($imageName, $mageResizeName, $imagePath);
                $img->save($smallImagePath);
                //google storage sdk ,同步处理好的图片到google storage
                $this->moveImageToStorage($smallImagePath);
                echo "*******************【正在处理 $smallImagePath 】*******************\n";
                //icon image
                $img->resize($iconW, self::ICON_H);
                $mageResizeName = self::ICON_NAME . $imageName;
                $iconImagePath = str_replace($imageName, $mageResizeName, $imagePath);
                $img->save($iconImagePath);
                //google storage sdk ,同步处理好的图片到google storage
                $this->moveImageToStorage($iconImagePath);
                echo "*******************【正在处理 $iconImagePath 】*******************\n";
                echo "===================================================================\n";
                $picNum++;
                if ($picNum <= $all) {
                    echo "开始处理第{$picNum}张图\n";
                }
            } catch (\Exception $e) {
                echo "++++++++++++++++++++++【发生错误】+++++++++++++++++++++\n";
                echo "错误信息:" . $e->getMessage() . "\n";
                echo "错误文件:" . $e->getFile() . "\n";
                echo "错误行:" . $e->getLine() . "\n";
                echo "+++++++++++++++++++++++++++++++++++++++++++++++++++++\n";
                continue;
                //todo:钉钉报警
            }
        }
        //删除临时文件夹
        $shell = `rm -rf $fileTemp`;
        echo $shell;
        echo "+++++++++++++++【所有图处理完毕!】++++++++++++++++++++\n";
    }

    /**
     * 上传临时文件夹里的文件到Google storage
     * @param string $tempImagePath 临时文件夹里的图片位置
     */
    public function moveImageToStorage(string $tempImagePath)
    {
        $storagePath = escapeshellarg(self::STORAGE_BUCKET . self::STORAGE_FOLDER . str_replace(self::FILE_PATH_TEMP, '', $tempImagePath));
        $tempImagePath = escapeshellarg($tempImagePath);
        $shell = `/home/vagrant/google-cloud-sdk/bin/gsutil cp -r $tempImagePath $storagePath`;
        echo $shell;
    }


    /**遍历临时文件夹获取所有图片树状图
     * @param string $pathName
     * @return array
     */
    public function getImageList(string $pathName): array
    {
        //将结果保存在result变量中
        $result = array();
        $temp = array();
        //判断传入的变量是否是目录
        if (!is_dir($pathName) || !is_readable($pathName)) {
            return [];
        }
        //取出目录中的文件和子目录名,使用scandir函数
        $allFiles = scandir($pathName);
        //遍历他们
        foreach ($allFiles as $fileName) {
            //判断是否是.和..因为这两个东西神马也不是。。。
            if (in_array($fileName, array('.', '..'))) {
                continue;
            }
            //路径加文件名
            $fullName = $pathName . '/' . $fileName;
            //如果是目录的话就继续遍历这个目录
            if (is_dir($fullName)) {
                //将这个目录中的文件信息存入到数组中
                $result[$fullName . '/'] = $this->getImageList($fullName);
            } else {
                //如果是文件就先存入临时变量
                $temp[] = $fileName;
            }
        }
        //取出文件
        if ($temp) {
            foreach ($temp as $f) {
                $result[] = $f;
            }
        }
        return $result;
    }

    /**
     * 遍历原始文件夹,并且按照修改时间过滤文件,并且复制到临时文件夹
     * @param string $originFile
     */
    public function filterMoveImageToTemp(string $originFile)
    {
        //判断传入的变量是否是目录
        if (!(!is_dir($originFile) || !is_readable($originFile))) {
            //取出目录中的文件和子目录名,使用scandir函数
            $allFiles = scandir($originFile);
            //遍历他们
            foreach ($allFiles as $fileName) {
                //判断是否是.和..因为这两个东西神马也不是。。。
                if (in_array($fileName, array('.', '..'))) {
                    continue;
                }
                //路径加文件名
                $fullName = $originFile . '/' . $fileName;
                //如果是目录的话就继续遍历这个目录
                if (is_dir($fullName)) {
                    //将这个目录中的文件信息存入到数组中
                    $this->filterMoveImageToTemp($fullName);
                } else {
                    $tempPathName = str_replace(self::FILE_PATH, self::FILE_PATH_TEMP, $originFile);
                    //创建临时文件夹
                    $this->mkdirs($tempPathName);
                    $shell = `find $originFile -mtime -5 -type f | xargs -i cp {} $tempPathName`;
                    echo $shell;
                }
            }
        }
    }


    /**扁平化数组
     * @param $arr
     * @return array
     */
    public function flatten($arr): array
    {
        $result = [];
        global $a;
        foreach ($arr as $k => $v) {
            if (is_array($v)) {
                $a = $k;
                $result = array_merge($result, $this->flatten($v));
            } else if (is_numeric($k)) {
                $result[$a][] = $v;
            } else {

                $result[$k] = $v;
            }
        }
        return $result;
    }

    /**
     * 整理获取到的树状文件夹图片列表成一维数组
     * @param array $imagePathLists
     * @return array
     */
    public function formatImagePathMap(array $imagePathLists): array
    {
        $imagePathLists = $this->flatten($imagePathLists);
        $imageList = [];
        foreach ($imagePathLists as $path => $imageNameLists) {
            foreach ($imageNameLists as $imageName)
                $imageList[] = $path . $imageName;
        }
        return $imageList;
    }

    /**
     * 判断文件夹是否存在,不存在就创建
     * @param $dir
     * @param int $mode
     * @return bool
     */
    public function mkdirs($dir, int $mode = 0777): bool
    {
        if (is_dir($dir) || @mkdir($dir, $mode))
            return TRUE;
        if (!$this->mkdirs(dirname($dir), $mode))
            return FALSE;
        return @mkdir($dir, $mode);
    }
}