前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >ajax+php上传图片,等比压缩,canvas压缩减少上传带宽,优化上传速度

ajax+php上传图片,等比压缩,canvas压缩减少上传带宽,优化上传速度

作者头像
躺平程序员老修
发布2023-09-05 16:10:46
2570
发布2023-09-05 16:10:46
举报
文章被收录于专栏:躺平程序员老修

后端处理上传文件并等比压缩

  • 后端等比压缩代码

./upload.php

代码语言:javascript
复制
<?php

class UploadImageServer
{
    /**
     * 默认上传根目录
     *
     * @Author huaixiu.zhen
     * http://litblc.com
     * @var string
     */
    private $uploadPath = './upload';

    /**
     * 默认最大宽度,计算缩略比使用
     *
     * @Author huaixiu.zhen
     * http://litblc.com
     * @var int
     */
    private $maxWidth = 1920;

    /**
     * 当前上传文件的实际宽度,方便瀑布流的图片预加载
     *
     * @Author huaixiu.zhen
     * http://litblc.com
     * @var int
     */
    private $imgWidth = 0;

    /**
     * 当前上传文件的实际高度,方便瀑布流的图片预加载
     *
     * @Author huaixiu.zhen
     * http://litblc.com
     * @var int
     */
    private $imgHeight = 0;

    /**
     * 当前上传文件的后缀名(类型)
     *
     * @Author huaixiu.zhen
     * http://litblc.com
     * @var string
     */
    private $imageExtension = 'jpeg';

    /**
     * 默认缩略比
     *
     * @Author huaixiu.zhen
     * http://litblc.com
     * @var int
     */
    private $percent = 1;

    /**
     * 错误信息
     *
     * @Author huaixiu.zhen
     * http://litblc.com
     * @var string
     */
    private $errorMsg = '';

    /**
     * 限制上传文件的最大Size
     *
     * @Author huaixiu.zhen
     * http://litblc.com
     * @var int
     */
    public $limitSize = 10485760;

    /**
     * 限制上传文件的后缀类型
     *
     * @Author huaixiu.zhen
     * http://litblc.com
     * @var array
     */
    public $limitExtension = ['gif','jpg','jpeg','bmp','png', 'PNG', 'JPG', 'JPEG', 'GIF', 'BMP'];

    /**
     * 限制上传文件的宽度
     *
     * @Author huaixiu.zhen
     * http://litblc.com
     * @var int
     */
    public $limitWeight = 1000;

    /**
     * 限制上传文件的高度
     *
     * @Author huaixiu.zhen
     * http://litblc.com
     * @var int
     */
    public $limitHeight = 500;

    /**
     * 上传操作
     *
     * @Author huaixiu.zhen
     * http://litblc.com
     * @return array
     */
    public function uploadImg()
    {
        if (array_key_exists('post_image', $_FILES)) {
            $file = $_FILES['post_image'];

            // 检查上传文件格式
            if ($this->checkUploadImg($file)) {

                // 本地存储路径
                $destFileName = $this->uploadPath . '/image/' . $this->code('image-' . time() . '-') . '.' . $this->imageExtension;

                // 本地等比例压缩
                if ($this->saveImg($file['tmp_name'], $destFileName)) {

                    /*
                    // 上传图片到文件系统(七牛、cdn等),可选
                    try {
                    } catch (\Exception $e) {
                        return ['code' => '-1', 'message' => '上传文件系统出错!'];
                    }

                    // 删除本地的图,仅保留文件系统上的一份,可选
                    @unlink($destFileName);
                    */

                    return [
                        'code' => '0',
                        'data' => [
                            'img_url' => $destFileName,
                            'img_width' => $this->imgWidth,
                            'img_height' => $this->imgHeight
                        ]
                    ];

                } else {
                    return ['code' => '-1', 'message' => $this->errorMsg];
                }
            } else {
                return ['code' => '-1', 'message' => $this->errorMsg];
            }
        } else {
            return ['code' => '-1', 'message' => '上传文件不存在!'];
        }
    }

    /**
     * 随机6位数字
     *
     * @Author huaixiu.zhen
     * http://litblc.com
     *
     * @param string $prefix
     *
     * @return string
     */
    private function code($prefix = '')
    {
        $code = str_pad(mt_rand(0, 999999), 6, '0', STR_PAD_BOTH);
        return $prefix . $code;
    }

    /**
     * 检查文件类型大小和合法性
     *
     * @Author huaixiu.zhen
     * http://litblc.com
     *
     * @param $file
     *
     * @return bool
     */
    private function checkUploadImg($file)
    {
        $image = getimagesize($file['tmp_name']);
        $imageExtension = ltrim(strrchr($image['mime'], '/'), '/');

        //文件上传失败
        if ($file['error'] !== 0) {
            $this->errorMsg= '文件上传失败!';
            return false;
        }

        // 是否是真实图片文件
        if (!$image) {
            $this->errorMsg = '非法图像文件';
            return false;
        }

        // 检查文件类型
        if (!in_array($imageExtension, $this->limitExtension)) {
            $this->errorMsg = '上传文件类型不允许!';
            return false;
        }

        // 检查文件大小
        if ($file['size'] > $this->limitSize) {
            $this->errorMsg = '上传文件大小超出限制!';
            return false;
        }

        // 检查是否合法上传
        if (!is_uploaded_file($file['tmp_name'])) {
            $this->errorMsg = '非法上传文件!';
            return false;
        }

        // 图像宽高限制
        if ($image[0] < $this->limitWeight || $image[1] < $this->limitHeight) {
            $this->errorMsg = '请选择宽度大于1000px、高度大于500px的图片';
            return false;
        }

        $this->imageExtension = $imageExtension;
        return true;
    }

    /**
     * 等比压缩并保存
     *
     * @Author huaixiu.zhen
     * http://litblc.com
     *
     * @param $file
     * @param $name
     *
     * @return mixed
     */
    private function saveImg($file, $name)
    {
        list($width, $height, $type, $attr) = getimagesize($file);
        $imageInfo = [
            'width' => $width,
            'height' => $height,
            'type' => image_type_to_extension($type, false),
            'attr' => $attr,
        ];
        $this->percent = $this->setPercent($this->maxWidth, $imageInfo['width']);
        $create = 'imagecreatefrom' . $imageInfo['type'];
        $image = $create($file);
        $newWidth = $imageInfo['width'] * $this->percent;
        $newHeight = $imageInfo['height'] * $this->percent;
        $newThump = imagecreatetruecolor($newWidth, $newHeight);
        imagecopyresampled($newThump, $image, 0, 0, 0, 0, $newWidth, $newHeight, $imageInfo['width'], $imageInfo['height']);

        // 同类型压缩
        $save = "image" . $imageInfo['type'];

        // 固定类型jpeg压缩(空间占用小)
        // $save = 'imagejpeg';

        if ($save($newThump, $name)) {
            @chmod($name, 0777);
            imagedestroy($newThump);
            imagedestroy($image);

            // 保存图片的实际高度宽度,方便前端瀑布流展示
            $this->imgWidth = $newWidth;
            $this->imgHeight = $newHeight;

            return true;
        } else {
            $this->errorMsg= '文件等比压缩失败!';
            
            return false;
        }
    }

    /**
     * 设置缩放比例
     *
     * @Author huaixiu.zhen
     * http://litblc.com
     *
     * @param $maxWidth
     * @param $width
     *
     * @return float|int
     */
    private function setPercent($maxWidth, $width)
    {
        if ($width > $maxWidth) {
            $percent = $maxWidth / $width;
        } else {
            $percent = 1;
        }

        return $percent;
    }
}

后端调用示例 ./index.php

代码语言:javascript
复制
<?php

require_once './upload.php';

$uploadImgServer = new UploadImageServer();

$result = $uploadImgServer->uploadImg();
print_r(json_encode($result, JSON_UNESCAPED_UNICODE));

至此后端已经压缩完毕,但是如果上传的图片大多是几M的大图,难免浪费上传带宽,而且会导致速度非常慢,影响用户体验,于是可以使用canvas在上传之前压缩一遍,解决速度慢的问题。

前端使用canvas压缩再上传

  • 前端示例代码:

./index.html

代码语言:javascript
复制
<html>
<head>
    <script src="https://libs.baidu.com/jquery/1.9.0/jquery.min.js"></script>
</head>
<input type="file" class="js-upload-local">

<script>
    // 上传本地图片
    $('.js-upload-local').change(function(){
        var files = $(this)[0].files;
        if (files.length) {
            var file = files[0];
            if (/(gif|jpeg|\png|jpg|bmp)$/.test(file.type)) {
                compress(file, 1920, updateImg);
            }
        }
    });

    // 压缩图片 & 上传
    function compress(data, maxWidth, callbackFunc) {
        var reader = new FileReader();
        reader.readAsDataURL(data)
        reader.onload = function(e) {
            //新建一个img标签(还没嵌入DOM节点)
            var image = new Image()
            image.src = e.target.result;
            image.onload = function() {

                var percent = 1,
                    canvas = document.createElement('canvas'),
                    context = canvas.getContext('2d'),
                    imageWidth,    //压缩后图片的大小
                    imageHeight,
                    result = '';

                // 计算压缩比
                if(image.width > maxWidth) {
                    percent = maxWidth / image.width;
                }

                // 对图片进行等比压缩
                imageWidth = image.width * percent;
                imageHeight = image.height * percent;
                canvas.width = imageWidth;
                canvas.height = imageHeight;
                context.drawImage(image, 0, 0, imageWidth, imageHeight);
                result = canvas.toDataURL('image/jpeg');

                callbackFunc(dataURLtoBlob(result), data.name);
            }
        }
    }

    /**
     * base64转二进制
     * @param dataurl
     * @returns {Blob}
     */
    function dataURLtoBlob(dataurl) {
        var arr = dataurl.split(','), mime = arr[0].match(/:(.*?);/)[1],
            bstr = atob(arr[1]), n = bstr.length, u8arr = new Uint8Array(n);
        while (n--){
            u8arr[n] = bstr.charCodeAt(n);
        }
        return new Blob([u8arr], {type:mime});
    }

    /**
     * 上传到后端
     * @param res
     * @param filename
     */
    function updateImg(res, filename) {
        $('.js-upload-local').val();
        var imageFile = new FormData();
        imageFile.append("post_image", res, filename);
        $.ajax({
            type: "post",
            url: './index.php',
            data: imageFile,
            dataType: 'json',
            processData: false,  //tell jQuery not to process the data
            contentType: false,  //tell jQuery not to set contentType
            beforeSend: function (XMLHttpRequest) {
                // 加载loading信息
            },
            success: function (res) {
                console.log(res)
            },
            complete: function () {
                // 取消loading效果
            },
            error: function (res) {
                console.log(res)
            }
        });
    }
</script>
</html>

后记

如果不需要前端canvas压缩,或者兼容性不允许,可以直接使用后端的代码。

扩展学习(blob,二进制,base64)

代码语言:javascript
复制
<!DOCTYPE HTML>
<html>
<meta charset="utf-8">
<head>
</head>
<body>
// multiple属性可以让用户能选择多个文件

<input id="myfiles" multiple type="file">

</body>

<script>

var pullfiles = function(){ 
    // 获取到input元素
    var fileInput = document.querySelector("#myfiles");

    // 所有type属性(attribute)为file的 <input> 元素都有一个files属性(property),用来存储用户所选择
    var files = fileInput.files;



    // 普通方式上传二进制文件
    if (files[0]) {
        uploadImg(files[0])
    }



    // 将file生成blob链接,可用于本地预览,缩略图
    if (files[0]) {
        var blobUrl = URL.createObjectURL(files[0]);
        console.log('blob链接:', blobUrl)
    }



    // 生成data:base64的字符串
    if (files[0]) {
        var reader = new FileReader();
        reader.readAsDataURL(files[0])

        reader.onload = function (e) {
            // 这里操作生成base64字符串
            var base64str = e.target.result;
            // 上传二进制文件(由base64转换成的二进制)
            uploadImg(dataURLtoBlob(base64str))
        }
    }

}


// 设置change事件处理函数
document.querySelector("#myfiles").onchange=pullfiles;



/**
 * base64转二进制
 * @param dataurl
 * @returns {Blob}
 */
function dataURLtoBlob(dataurl) {
    var arr = dataurl.split(','), mime = arr[0].match(/:(.*?);/)[1],
        bstr = atob(arr[1]), n = bstr.length, u8arr = new Uint8Array(n);
    while (n--){
        u8arr[n] = bstr.charCodeAt(n);
    }
    return new Blob([u8arr], {type:mime});
}

/**
 * 上传文件
 */
function uploadImg(files) {
    var formData = new FormData();
    formData.append('username', 'abc123');
    formData.append('post_image', files);
    fetch('http://localhost:81/test/mozilla-test/upload.php', {
        method: 'POST',
        body: formData
    })
    .then(response => response.json())
    .catch(error => console.error('Error:', error))
    .then(response => console.log('Success:', response));
}

</script>

</html>
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 后端处理上传文件并等比压缩
  • 前端使用canvas压缩再上传
  • 后记
  • 扩展学习(blob,二进制,base64)
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档