devilyouwei

使用thinkphp开发app后端中,需要实现一个处理上传图片队列的功能

这是个上传多图片保存并且需要对其中一张图片进行压缩的功能
(使用的html5 mui框架开发app,如果直接载入原图,app客户端列表中图稍微多几张就会使得webview十分卡顿,建议在开发中,一定要对用户上传的图片进行服务器端的压缩)

之前已经写过一篇关入如何使用html5+的uploader上传多张图片到服务器的博客:
http://www.cnblogs.com/devilyouwei/p/6790255.html
并且实现了在前端进行压缩的功能(这是第一次压缩,有用户手机端进行)

上传到服务器后我们还需对图片进行处理,我这里使用的php作为后端语言,框架为thinkphp5

需要用到thinkphp的File类和Image类
File类:前者获取到用户上传的file(文件)并进行路径和文件名的操作
File类下的方法众多,主要有以下一些,使用之前应该先打印看一下,随意找一个文件测试一下效果,可以看的更加明白些:

$file = new File(ROOT_PATH."/public/uploads/20170506/abc.jpg");
$arr = [
	\'getATime\' => $file->getATime(), //最后访问时间 
    \'getBasename\' => $file->getBasename(), //获取无路径的basename 
    \'getCTime\' => $file->getCTime(), //获取inode修改时间 
    \'getExtension\' => $file->getExtension(), //文件扩展名 
    \'getFilename\' => $file->getFilename(), //获取文件名 
    \'getGroup\' => $file->getGroup(), //获取文件组 
    \'getInode\' => $file->getInode(), //获取文件inode 
    \'getLinkTarget\' => $file->getLinkTarget(), //获取文件链接目标文件 
    \'getMTime\' => $file->getMTime(), //获取最后修改时间 
    \'getOwner\' => $file->getOwner(), //文件拥有者 
    \'getPath\' => $file->getPath(), //不带文件名的文件路径 
    \'getPathInfo\' => $file->getPathInfo(), //上级路径的SplFileInfo对象 
    \'getPathname\' => $file->getPathname(), //全路径 
    \'getPerms\' => $file->getPerms(), //文件权限 
    \'getRealPath\' => $file->getRealPath(), //文件绝对路径 
    \'getSize\' => $file->getSize(),//文件大小,单位字节 
    \'getType\' => $file->getType(),//文件类型 file dir link 
    \'isDir\' => $file->isDir(), //是否是目录 
    \'isFile\' => $file->isFile(), //是否是文件 
    \'isLink\' => $file->isLink(), //是否是快捷链接 
    \'isExecutable\' => $file->isExecutable(), //是否可执行 
    \'isReadable\' => $file->isReadable(), //是否可读 
    \'isWritable\' => $file->isWritable()  //是否可写 
    ];

print_r($arr);
return false;

print_r打印到浏览器后:

Array
(
    [getATime] => 1494041766
    [getBasename] => abc.jpg
    [getCTime] => 1494041766
    [getExtension] => jpg
    [getFilename] => abc.jpg
    [getGroup] => 0
    [getInode] => 0
    [getLinkTarget] => D:\wamp\www\dashen\public\uploads\20170506\abc.jpg
    [getMTime] => 1494041766
    [getOwner] => 0
    [getPath] => D:\wamp\www\dashen\/public/uploads/20170506
    [getPathInfo] => SplFileInfo Object
        (
            [pathName:SplFileInfo:private] => D:\wamp\www\dashen\/public/uploads/20170506
            [fileName:SplFileInfo:private] => 20170506
        )

    [getPathname] => D:\wamp\www\dashen\/public/uploads/20170506/abc.jpg
    [getPerms] => 33206
    [getRealPath] => D:\wamp\www\dashen\public\uploads\20170506\abc.jpg
    [getSize] => 571800
    [getType] => file
    [isDir] => 
    [isFile] => 1
    [isLink] => 
    [isExecutable] => 
    [isReadable] => 1
    [isWritable] => 1
)

关于如何用thinkphp5处理上传的多张图片文件,专门写一个private方法来处理,最后返回处理好的图片的路径的数组
图片处理包括:

  1. 将用户上传的图片保存到public/uploads/目录下
  2. 按照日期建立目录,md5的编码时间作为文件名
  3. 压缩其中第一张图片为缩略图,专门用于预览(也要保存到数据库)
  4. 返回所有处理过图片的路径数组,由调用者处理后保存数据库

代码:


 // 用户提交疑难
    public function addQues() {
        // 验证登陆session
        if (!session("?user_info"))
            return [\'info\'=>\'登录状态失效\', \'login\'=>0];

        $ajax[\'title_id\'] = input(\'post.title_id/d\');
        $ajax[\'content\'] = input(\'post.content/s\');
        $ajax[\'star\'] = input("post.star/s");
        $ajax[\'reward\'] = input(\'post.reward/s\');
        $ajax[\'message\'] = input(\'post.message/s\');
        $ajax[\'price\'] = input(\'post.price/f\');

        if($ajax[\'reward\']!="金钱悬赏")
            $ajax[\'price\'] = 0;

        if ($ajax[\'title_id\'] == null || $ajax[\'content\'] == null || $ajax[\'star\'] == null || $ajax[\'reward\'] == null)
            return [\'info\'=>\'标题,内容,难度,悬赏方式不能为空\', \'status\'=>0];

        if ($ajax[\'title_id\'] == 0 || $ajax[\'content\'] == "" || $ajax[\'star\'] == "" || $ajax[\'reward\'] == "")
            return [\'info\'=>\'标题,内容,难度,悬赏方式不能为空\', \'status\'=>0];

        // 上传图片
        $ajax[\'uid\'] = session("user_info")[\'id\'];
        $ajax[\'create_time\'] = time();
        $ajax[\'update_time\'] = $ajax[\'create_time\'];
        $ajax[\'ip\'] = get_client_ip();
        if (request()->file() != null) {
            $imgs = $this->upload();

            //根据imgs是否为数组鉴定是否有上传错误和超范围
            if(!is_array($imgs))
                return[\'status\'=>0,\'info\'=>$imgs];

            $ajax = array_merge($ajax, $imgs);
        }
        $ajax[\'is_del\'] = 0;
        $ajax[\'is_effect\'] = 1;
        $ajax[\'state\'] = 0;
        $f = db("Ques")->insert($ajax);
        if ($f >= 1)
            return [\'status\'=>1, \'info\'=>\'需求单提交成功\'];
        else
            return [\'status\'=>0, \'info\'=>\'数据插入失败\'];
    }

此方法中,压缩第一张图时需要用到原图的文件路径,名称等,我保存在原图同一目录下,在原图前面加上“thumb_”前缀作为区别。

调用upload方法的控制器方法(外部方法):


    // 文件上传转储(多文件)
    // 暂且按照UNIX路径存储
    private function upload() {
        //创建的目录名称,日期(相对项目目录。用于数据库保存)
        $dirName = "public".DS."uploads".DS.(date(\'Ymd\'));
        //创建保存目录(绝对路径。用于保存文件)
        $saveDir = ROOT_PATH.DS.$dirName;

        if (!file_exists($saveDir)){
            mkdir($saveDir);
        }

        $files = request()->file();//取得上传文件名


        foreach ( $files as $key => $file ) {
            // 自动生成文件名
            $info = $file->rule("get_rand")->validate([\'ext\'=>UPLOAD_EXT,\'size\'=>UPLOAD_SIZE])->move($saveDir,true,false);

            if ($info) {
                //保存到数据的值
                $data[$key] = $dirName .DS. $info->getFileName();
            } else {
                return $file->getError();
            }

            // 压缩第一张图
            if ($key == \'img0\') {
                $image = Image::open($info->getRealPath());
                // 保存
                $thumbName = $saveDir.DS.$info->getFilename();

                // 绝对路径加上文件名
                $save = $saveDir.DS."thumb_" . $info->getFilename();
                $image->thumb(300, 200, Image::THUMB_CENTER)->save($save);

                $data[\'thumbnail\'] = $dirName.DS.$thumbName;
            }
        }
        return $data;
    }
}

注意:thinkphp批量上传可能会出现同名文件覆盖的问题,因此我们重新规定了rule()方法使用的函数,在common.php写下一个随机生成函数,加上时间戳,这样重复的可能性就几乎为0了,极不易出现重名覆盖问题
同名覆盖问题可能来源于上传文件并发的转储,时间太短了,因为php是秒级别的UNIX时间戳,导致了产生了相同的时间戳,从而用thinkphp官方的文档中的上传示例代码导致了重名覆盖问题

//随机生成器
function get_rand(){
    $now = $_SERVER[\'REQUEST_TIME\'];//当前系统时间,比time()多5秒
    return rand().$now;
}

分类:

技术点:

相关文章: