thinkphp5实现ajax图片上传,压缩保存到服务器

字体:
<div class="warp">
      <input type="file" id="file"  accept="image/*" onchange="upimg(this)" />
 </div>
<img src="" />
<script>
    //上传图片方法
     function upimg(obj){
         var fileData = obj.files[0];//这是我们上传的文件
         console.log(fileData)
         var formData = new FormData();
         // 服务端要求参数是 pic1
         formData.append('image',fileData);
         $.ajax({
             url:"{:url('user/upmemberphoto')}",
             type:'post',
             data:formData,
             cache: false, //上传文件不需要缓存
             processData: false, // 告诉jQuery不要去处理发送的数据
             contentType: false, // 告诉jQuery不要去设置Content-Type请求头
             success:function(data){
	             // 关键:清空 input 的 value,允许再次上传同一文件
	             $('#file').val('')
                 console.log(data);
                 // 设置图片预览功能
                 $('.head-img').attr('src',data.picAddr);
             }
         })
     }
</script>

vue2的写法

<template>
  <div>
    <!-- 文件上传按钮 -->
    <input 
      type="file" 
      ref="fileInput"
      accept="image/*" 
      @change="upimg"
    />
    <!-- 预览图片 -->
    <img :src="previewImage" alt="预览" />
  </div>
</template>
<script>
// 假设你的项目中已安装 axios,或直接使用原生 fetch
import axios from 'axios'

export default {
  data() {
    return {
      previewImage: '', // 存储预览图片地址(服务器返回的)
    }
  },
  methods: {
    //上传图片
            upimg(event){
                var fileData = event.target.files[0];//这是我们上传的文件
                console.log(fileData)
                var formData = new FormData();
                // 服务端要求参数是 pic1
                formData.append('image',fileData);
                $.ajax({
                    url:"{:url('user/upmemberphoto')}",
                    type:'post',
                    data:formData,
                    cache: false, //上传文件不需要缓存
                    processData: false, // 告诉jQuery不要去处理发送的数据
                    contentType: false, // 告诉jQuery不要去设置Content-Type请求头
                    success:(data)=>{
                    	// 关键:清空 input 的 value,允许再次上传同一文件
			            this.$refs.fileInput.value = ''
                        console.log(data);
                        // 设置图片预览功能
                        $('.head-img').attr('src',data.picAddr);
                    }
                })
            }
  }
}
</script>


thinkphp压缩图片插件官方地址

使用Composer安装ThinkPHP5的图像处理类 库:


composer  require topthink/think-image

    /**
     * 会员照片上传 - 直接压缩保存 + 自动修正旋转
     */
    public function upmemberphoto()
    {
        // 获取上传文件
        $file = request()->file('image');
        if (!$file) {
            return ['code' => 400, 'message' => '未检测到上传文件'];
        }

        $uploadPath = ROOT_PATH . 'public' . DIRECTORY_SEPARATOR . 'uploads' . DIRECTORY_SEPARATOR . 'member';

        // 确保上传目录存在
        if (!is_dir($uploadPath)) {
            mkdir($uploadPath, 0755, true);
        }

        // 移动到服务器(先保存临时原图)
        $info = $file->move($uploadPath);
        if (!$info) {
            return ['code' => 400, 'message' => '文件移动失败: ' . $file->getError()];
        }

        // 处理路径(统一使用正斜杠)
        $saveName = str_replace(DIRECTORY_SEPARATOR, '/', $info->getSaveName());
        $imgPath = $uploadPath . '/' . $saveName;

        try {
            // ? 核心处理:修正旋转 + 压缩保存(不保留原图)
            $this->processImage($imgPath, 620, 85);

            return [
                'code' => 200,
                'message' => '上传成功',
                'data' => '/uploads/member/' . $saveName
            ];

        } catch (\Exception $e) {
            // 记录日志 + 清理失败文件
            \think\Log::record('图片处理失败: ' . $e->getMessage(), 'error');
            if (file_exists($imgPath)) {
                @unlink($imgPath);
            }
            return ['code' => 500, 'message' => '图片处理失败: ' . $e->getMessage()];
        }
    }

    /**
     * 图片处理:修正EXIF旋转 + 等比压缩 + 覆盖保存
     * @param string $imgPath 图片绝对路径
     * @param int $maxWidth 最大宽度
     * @param int $quality JPEG压缩质量(1-100)
     */
    private function processImage($imgPath, $maxWidth = 620, $quality = 85)
    {
        // 1️⃣ 检查文件
        if (!file_exists($imgPath)) {
            throw new \Exception("文件不存在: {$imgPath}");
        }

        // 2️⃣ 打开图片
        $image = \think\Image::open($imgPath);
        if (!$image) {
            throw new \Exception("无法打开图片,请检查GD/Imagick扩展");
        }

        // 3️⃣ 【关键】修正手机拍照的EXIF旋转问题
        $this->fixExifOrientation($imgPath, $image);

        // 4️⃣ 获取修正后的尺寸,判断是否需要压缩
        $width = $image->width();
        $height = $image->height();

        // 只有当宽度超过设定值时才压缩(避免小图被强行放大)
        if ($width > $maxWidth) {
            $image->thumb($maxWidth, $maxWidth, \think\Image::THUMB_SCALING);
        }

        // 5️⃣ 【关键】覆盖保存原路径(不保留原图)
        // 第三个参数 $quality 控制压缩质量,越小文件越小
        $saveResult = $image->save($imgPath, null, $quality);

        if (!$saveResult) {
            throw new \Exception("图片保存失败,请检查目录权限");
        }
    }

    /**
     * 修正图片EXIF方向(解决手机拍照旋转90度问题)
     * @param string $imgPath 图片路径
     * @param \think\Image $image ThinkPHP Image对象
     */
    private function fixExifOrientation($imgPath, $image)
    {
        // 检查exif扩展是否启用
        if (!extension_loaded('exif')) {
            return; // 没扩展就跳过,至少保证流程继续
        }

        // 仅处理JPEG图片(其他格式一般不含EXIF方向)
        $mime = strtolower(mime_content_type($imgPath));
        if (!in_array($mime, ['image/jpeg', 'image/jpg'])) {
            return;
        }

        $exif = @exif_read_data($imgPath);
        if (!$exif || empty($exif['Orientation'])) {
            return;
        }

        // 根据Orientation值旋转图片(旋转后像素数据就"正"了)
        switch ($exif['Orientation']) {
            case 3: // 180°
                $image->rotate(180);
                break;
            case 6: // 顺时针90°(最常见)
                $image->rotate(90);
                break;
            case 8: // 逆时针90°
                $image->rotate(-90);
                break;
            // 2/4/5/7 涉及镜像翻转,手机照片极少出现,可按需扩展
        }
    }


另外一种方法,传递base64图片,提交图片数据的字符串

<img id="memberHeadimg" src="" alt=""/>
<input  type="file" id="imgopipt" accept="image/*"  onchange="getBase64(event)"  />
<input type="hidden" id="photo" name="photo"/>
    //上传图片获取base64
    function getBase64(e){
        // 选择的文件
        let file = e.target.files[0];
        console.log(file.name) // 文件名称,有需求可处理
        console.log(file.type) // 文件类型,有需求可处理
        // 判断文件是否读取完毕,读取完毕后执行
        if (window.FileReader) {
            let reader = new FileReader();
            reader.readAsDataURL(file);
            reader.onload = function(e) {
                let base64String = e.target.result;
                // 此处可对该base64进行获取赋值传入后端
                console.log("bese64编码:", base64String);
                $("#photo").val(base64String)
                $("#memberHeadimg").attr('src',base64String)
            }
        }
    }

tp5.0接收base64的字符串保存为文件,宽度超过600的压缩,并且图片不旋转90度

/*判断图片是否存在  存在则保存图片路径  系统进程结束之后单独上传图片上传图片*/
$photo=!empty($_REQUEST['photo']) ? $_REQUEST['photo'] : '';
$img_url = '';//图片路径
$base64_image = str_replace(' ', '+', $photo);
//post方式接收的数据, 加号会被替换为空格, 需要重新替换回来, 若不是post数据, 不需要执行
//$result 是一个数组(引用传递),用于存储正则匹配的捕获结果。
if (preg_match('/^(data:\s*image\/(\w+);base64,)/', $base64_image, $result)) {
    $image_data = base64_decode(str_replace($result[1], '', $base64_image));
    $im = imagecreatefromstring($image_data);
    if ($im !== false) {
        $ext = strtolower($result[2]);
        if ($ext == 'jpeg') $ext = 'jpg';   // 统一后缀

        // ========== 1. 修正 JPEG 的方向(EXIF Orientation) ==========
        if ($ext == 'jpg' && function_exists('exif_read_data')) {
            // 将图片二进制数据写入临时文件(exif_read_data 需要读取文件)
            $tempFile = tmpfile();
            fwrite($tempFile, $image_data);
            $tempMeta = stream_get_meta_data($tempFile);
            $tempPath = $tempMeta['uri'];
            $exif = @exif_read_data($tempPath);
            fclose($tempFile);

            if (!empty($exif['Orientation'])) {
                $orientation = $exif['Orientation'];
                // 根据 Orientation 值旋转/翻转图片
                switch ($orientation) {
                    case 3:
                        $im = imagerotate($im, 180, 0);
                        break;
                    case 6:
                        $im = imagerotate($im, -90, 0);
                        break;
                    case 8:
                        $im = imagerotate($im, 90, 0);
                        break;
                    // 如果需要支持镜像(2,4,5,7),可继续补充,但绝大多数情况只用到 3,6,8
                }
            }
        }

        // ========== 2. 尺寸压缩(等比例限制最大宽度600) ==========
        $max_width = 600;
        $src_w = imagesx($im);
        $src_h = imagesy($im);
        if ($src_w > $max_width) {
            $dst_w = $max_width;
            $dst_h = intval($src_h * ($max_width / $src_w));
            $im2 = imagecreatetruecolor($dst_w, $dst_h);
            // 透明背景处理(PNG/GIF 保留透明通道)
            if ($ext == 'png') {
                imagealphablending($im2, false);
                imagesavealpha($im2, true);
                $trans = imagecolorallocatealpha($im2, 0, 0, 0, 127);
                imagefilledrectangle($im2, 0, 0, $dst_w, $dst_h, $trans);
            } elseif ($ext == 'gif') {
                $trans_index = imagecolortransparent($im);
                if ($trans_index >= 0) {
                    imagepalettecopy($im, $im2);
                    imagefill($im2, 0, 0, $trans_index);
                    imagecolortransparent($im2, $trans_index);
                }
            }
            imagecopyresampled($im2, $im, 0, 0, 0, 0, $dst_w, $dst_h, $src_w, $src_h);
            imagedestroy($im);
            $im = $im2;
        }

        // ========== 3. 保存文件(质量压缩) ==========
        $dir = './uploads/member/' . date("Ymd");
        if (!is_dir($dir)) mkdir($dir, 0777, true);
        $picname = date("his") . '_' . rand(10000, 99999);
        $picdir = $picname . '.' . $ext;
        $image_url = $dir . '/' . $picdir;

        switch ($ext) {
            case 'jpg':
                imagejpeg($im, $image_url, 80);
                break;
            case 'png':
                imagepng($im, $image_url, 6);
                break;
            case 'gif':
                imagegif($im, $image_url);
                break;
            case 'webp':
                imagewebp($im, $image_url, 80);
                break;
            default:
                file_put_contents($image_url, $image_data);
        }
        imagedestroy($im);
        $img_url = $image_url;
    }
}


免费在线工具网站 https://mantools.top/