PHP 上传图片的处理


PHP 上传图片的处理


正文

我们在网站上时常会涉及到图片的上传,所以最好能有个实例:

<?php
// 接受数据
$req_pic = $_FILES['input_pic'];
// 保存目录
$uploadDir = 'pic'.DIRECTORY_SEPARATOR.date("Ymd").DIRECTORY_SEPARATOR;
$dir_db = DIRECTORY_SEPARATOR.'Uploads'.DIRECTORY_SEPARATOR.$uploadDir;
$dir = '.'.$dir_db;
// 生成日期文件夹
file_exists($dir) || (mkdir($dir,0777,true) && chmod($dir,0777));
// 生成文件名
$fileName = time().uniqid().'.'.pathinfo($req_pic["name"])['extension'];
// 移动到目的文件夹下
move_uploaded_file($req_pic["tmp_name"], $dir.$fileName);
// 完整保存地址及文件名
$req_pic_file = $dir_db.$fileName;
// 存入数据库前赋值
$data['pic'] = $req_pic_file; 

我们打印一下$req_pic,看一下有什么内容:

如果我们上传的是docx文件呢:

我们可以看出name是上传时的文件名,type是文件类型,tmp_name是上传后临时保存的目录,error是错误数,size是文件大小。

我们最后把临时文件转移到目标文件夹下就算是上传成功了。

在这里我们要分清相对路径和绝对路劲。

Base64

Base64是网络上最常见的用于传输8Bit字节码的编码方式之一,Base64就是一种基于64个可打印字符来表示二进制数据的方法。

Base64要求把每三个8Bit的字节转换为四个6Bit的字节(38 = 46 = 24),然后把6Bit再添两位高位0,组成四个8Bit的字节, 也就是说,转换后的字符串理论上将要比原来的长1/3。

关于这个编码的规则:

  1. 把3个字节变成4个字节。
  2. 每76个字符加一个换行符。
  3. 最后的结束符也要处理。

RFC2045转换表,The Base64 Alphabet:

看个例子:

转换前 10101101,10111010,01110110

转换后 00101011, 00011011 ,00101001 ,00110110

十进制 43 27 41 54

对应码表中的值 r b p 2

所以上面的24位编码,编码后的Base64值为 rbp2

解码同理,把 rbq2 的二进制位连接上再重组得到三个8位值,得出原码。

上传图片或文件

我们可以读取一个文件的内容,然后用 base_encode() 编码,传送后,接收端使用 base_decode() 解码,再写入文件。 理论上 Base64 只有文件内容,为了区分是什么类型文件,我们要在 Base64文件内容 前加一个文件类型描述。

base64_encode(string $string): string
 
base64_decode(string $string, bool $strict = false): string|false

如,发送端:

<?php
$content = file_get_contents('a.png');
$base64_str = base64_encode($content);
// 加一个文件类型标识符
$base64_png = 'data:image/png;base64,' .$base64_str;
// $base64_png 为发送到的字符串

接收端:

<?php
// $img 为接收到的字符串
// 判断图片后缀,并还原图片base64内容
if(!(strpos($img, 'data:image/png;base64,') === false)){
    $base64_str = str_replace('data:image/png;base64,', '', $img);
    $ext = '.png';
}
$content = base64_decode($base64_str);
// 本地保存
file_put_contents(time() .$ext, $content);

上传多张图

下面加一个很有意思的上传功能,一次可以上传多张图:

<load href="/Public/js/jquery.min.js"/>
<load href="/Public/js/layermobile/layer.js"/>

<div class="upfile">
    <input type="file" name="input_pic" onchange="imgPreview(this)">
    <img src="/Public/images/shop/file-style.png">
</div>

<div class="bottom" id="submit">
    <a>确认发布</a>
</div>
<script type="text/javascript">
// 上传图片
function imgPreview(fileDom)
{
    //判断是否支持FileReader
    if (window.FileReader) {
        var reader = new FileReader();
    } else {
        layer.open({
            content: '您的设备不支持图片预览功能,如需该功能请升级您的设备!'
            ,style: 'background-color:#2248ab; color:#ecfcff; border:none;font-size:1.2rem;border-radius:0.5rem;' //自定风格
            ,time: 2
        });
        
        return false;
    }

    //获取文件
    var file = fileDom.files[0];
    var imageType = /^image\//;
    //是否是图片
    if (!imageType.test(file.type)) {
        layer.open({
            content: '请选择图片'
            ,style: 'background-color:#2248ab; color:#ecfcff; border:none;font-size:1.2rem;border-radius:0.5rem;' //自定风格
            ,time: 2
        });
        return false;
    }
    if(file.size > 500000){ //图片限制大小
        layer.open({
            content: '图片文件太大'
            ,style: 'background-color:#2248ab; color:#ecfcff; border:none;font-size:1.2rem;border-radius:0.5rem;' //自定风格
            ,time: 2
        });
        return false;
    }

    //读取完成
    reader.onload = function(e) {
        //获取图片dom
        // var img = document.getElementByIdx_x_x_x("preview");
        //图片路径设置为读取的图片
        // img.src = e.target.result;
        var str = "php <wbr>上传图片的处理" style='width: 150px;'/>";
        var input =''">';
        $("#input_img").find('input').remove();
        $("#img_container").find('img').remove();
        $("#src_images").before(str);
        $("#input_img").append(input);
    };
    reader.readAsDataURL(file);
}

// 提交检查
$("#submit").click( function ()
{
    var input_pic = {};
    var pic_num = 0;
    $("input[name='images[]']").each(function(index, el) {
        input_pic[index] =$(el).val();
        pic_num++;
    });
    if ( pic_num == 0 ) {
        layer.open({
            content: '商品图片不能为空'
            ,style: 'background-color:#2248ab; color:#ecfcff; border:none;font-size:1.2rem;border-radius:0.5rem;' //自定风格
            ,time: 2
        });
        return false;
    }
    
    var postdata = {};
    postdata['input_pic'] = input_pic;
    $.ajax({
        url: "{:U('Product/creatProduct')}",
        type: 'POST',
        dataType: 'json',
        data: postdata,
        success:function(data){
            if ( data.status == 1 ) {
                layer.open({
                    content: data.info
                    ,style: 'background-color:#2248ab; color:#ecfcff; border:none;font-size:1.2rem;border-radius:0.5rem;' //自定风格
                    ,time: 2
                });
                if ( data.url != '' ) {
                    setTimeout(function(){
                        window.location.href = data.url;
                    },2000)
                }
            } else {
                layer.open({
                    content: data.info
                    ,style: 'background-color:#2248ab; color:#ecfcff; border:none;font-size:1.2rem;border-radius:0.5rem;' //自定风格
                    ,time: 2
                });
                if ( data.url != '' ) {
                    setTimeout(function(){
                        window.location.href = data.url;
                    },2000)
                }
            }
        }
    })

});
</script>

后台php页:

<?php
public function creatProduct ()
{
    // 商品图片
    $req_pic = $_POST['input_pic'];
    foreach ( $req_pic as $key => $img )
    {
        // 判断后缀名
        $ext = '';
        if( !(strpos($img, 'data:image/jpeg;base64,') === false)){
            $img = str_replace('data:image/jpeg;base64,', '', $img);
            $ext = '.jpg';
        }
        if(!(strpos($img, 'data:image/jpg;base64,') === false)){
            $img = str_replace('data:image/jpg;base64,', '', $img);
            $ext = '.jpg';
        }
        if(!(strpos($img, 'data:image/png;base64,') === false)){
            $img = str_replace('data:image/png;base64,', '', $img);
            $ext = '.png';
        }
        // 生成文件名
        $name = time().uniqid();
        $filename = "/Uploads/shop_product/" . date('Ymd') . "/" . $name . $ext;
        $img_arr[] = $filename;
        // 文件写入
        put(APP_PATH.'../www'.$filename, base64_decode($img));
    }
    
    $data['prod_pic'] = $img_arr[0];
}

function put ( $filename, $content, $type = '' )
{
    $dir = dirname($filename);
    if ( !is_dir($dir) ) {
        mkdir($dir,0777,true);
    }
    if ( false === file_put_contents($filename,$content) ) {
        // 写入错误日志文件
    }
}

图片压缩

有时用户上传的图片很大,如15M,会导致页面加载速度变慢,我们需要对用户上传的图片进行压缩。如:

<?php
function compress($image_src, $image_dist, $dist_width = 320, $dist_height = 240, $quality = 70, $filename = "")
{
    $imagecreate_list = [
        1 => function ($path) {
            return imagecreatefromgif($path);
        },
        2 => function ($path) {
            return imagecreatefromjpeg($path);
        },
        3 => function ($path) {
            return imagecreatefrompng($path);
        },
        // 4   =>  function($path) {return imagecreatefromswf($path);},
        // 5   =>  function($path) {return imagecreatefrompsd($path);},
        6 => function ($path) {
            return imagecreatefrombmp($path);
        },
        // 7   =>  function($path) {return imagecreatefromtiff($path);},
        // 8   =>  function($path) {return imagecreatefromtiff($path);},
        9 => function ($path) {
            return imagecreatefromjpeg($path);
        },
        10 => function ($path) {
            return imagecreatefromjpeg($path);
        },
        11 => function ($path) {
            return imagecreatefromjpeg($path);
        },
        12 => function ($path) {
            return imagecreatefromjpeg($path);
        },
        // 13   =>  function($path) {return imagecreatefromswc($path);},
        // 14   =>  function($path) {return imagecreatefromiff($path);},
        15 => function ($path) {
            return imagecreatefromwbmp($path);
        },
        16 => function ($path) {
            return imagecreatefromxbm($path);
        },
        // 17   =>  function($path) {return imagecreatefromico($path);},
        18 => function ($path) {
            return imagecreatefromwebp($path);
        },
    ];

    // 后缀名
    $file_extension = null;
    if (!empty($filename) && strrpos($filename, '.') !== false) {
        $file_extension = substr($filename, strrpos($filename, '.') + 1);
    }

    try {
        list($src_width, $src_height, $stype) = getimagesize($image_src);
        if ($dist_width && ($src_width < $src_height)) {
            $dist_width = ($dist_height / $src_height) * $src_width;
        } else {
            $dist_height = ($dist_width / $src_width) * $src_height;
        }

        $image_resource = imagecreatetruecolor($dist_width, $dist_height);

        $src_image = $imagecreate_list[$stype]($image_src);

        imagecopyresampled($image_resource, $src_image, 0, 0, 0, 0, $dist_width, $dist_height, $src_width, $src_height);

        if (strtolower($file_extension) == 'jpg' || strtolower($file_extension ) == 'jpeg') {
            imagejpeg($image_resource, $image_dist, $quality);
        } else {
            imagepng($image_resource, $image_dist, intval(($quality - 10) / 10));
        }

        imagedestroy($image_resource);
        imagedestroy($src_image);

        return true;
    } catch (\Exception $e) {
        throw new UploadException($e->getMessage());

        return false;
    }
}






参考资料

百度百科 base64 https://baike.baidu.com/item/base64

PHP 手册 函数参考 其它基本扩展 URL URL 函数 base64_decode https://www.php.net/manual/zh/function.base64-decode.php


返回