当前位置: 首页 > news >正文

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

<divclass="warp"><input type="file"id="file"accept="image/*"onchange="upimg(this)"/></div><img src=""/><script>//上传图片方法functionupimg(obj){varfileData=obj.files[0];//这是我们上传的文件console.log(fileData)varformData=newFormData();// 服务端要求参数是 pic1formData.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><!-- 文件上传按钮 --><inputtype="file"ref="fileInput"accept="image/*"@change="upimg"/><!-- 预览图片 --><img:src="previewImage"alt="预览"/></div></template><script>// 假设你的项目中已安装 axios,或直接使用原生 fetchimportaxiosfrom'axios'exportdefault{data(){return{previewImage:'',// 存储预览图片地址(服务器返回的)}},methods:{//上传图片upimg(event){varfileData=event.target.files[0];//这是我们上传的文件console.log(fileData)varformData=newFormData();// 服务端要求参数是 pic1formData.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

/** * 会员照片上传 - 直接压缩保存 + 自动修正旋转 */publicfunctionupmemberphoto(){// 获取上传文件$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) */privatefunctionprocessImage($imgPath,$maxWidth=620,$quality=85){// 1️⃣ 检查文件if(!file_exists($imgPath)){thrownew\Exception("文件不存在:{$imgPath}");}// 2️⃣ 打开图片$image=\think\Image::open($imgPath);if(!$image){thrownew\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){thrownew\Exception("图片保存失败,请检查目录权限");}}/** * 修正图片EXIF方向(解决手机拍照旋转90度问题) * @param string $imgPath 图片路径 * @param \think\Image $image ThinkPHP Image对象 */privatefunctionfixExifOrientation($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']){case3:// 180°$image->rotate(180);break;case6:// 顺时针90°(最常见)$image->rotate(90);break;case8:// 逆时针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"/>//上传图片获取base64functiongetBase64(e){// 选择的文件letfile=e.target.files[0];console.log(file.name)// 文件名称,有需求可处理console.log(file.type)// 文件类型,有需求可处理// 判断文件是否读取完毕,读取完毕后执行if(window.FileReader){letreader=newFileReader();reader.readAsDataURL(file);reader.onload=function(e){letbase64String=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){case3:$im=imagerotate($im,180,0);break;case6:$im=imagerotate($im,-90,0);break;case8:$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/

http://www.jsqmd.com/news/732576/

相关文章:

  • 别再死记硬背星座图了!用Python+Matplotlib手动画出64QAM调制全过程
  • Mina Archive节点部署与维护:存储历史数据的完整解决方案
  • BIOS密码忘了别急着抠电池!试试这几款免拆机清密码工具
  • 3步彻底解决Visual C++运行库报错:让电脑程序启动不再失败
  • 视频对象中心学习中的过分割问题与解决方案
  • 在多日连续使用中感受 Taotoken 平台 API 服务的稳定与可靠
  • 保姆级教程:用Python脚本一键将选股结果导入通达信自选股(附完整代码)
  • 基于MCP协议与混合搜索的AI Agent持久化记忆系统palaia实践指南
  • 保姆级教程:在Windows 11上从零搭建Mask2Former环境(含CUDA、PyTorch版本选择避坑)
  • 终极Visual C++运行库一键修复指南:告别程序启动失败的5个专业方案
  • ChatGPT插件开发全解析:从核心原理到实战构建
  • 基于Chrome扩展网关的LINE消息自动化客户端开发指南
  • CarPlay有线连接避坑指南:iPhone 0x53指令响应、NCM网络断连等常见问题解析
  • 通过 curl 命令直接测试 Taotoken 大模型 API 的连通性
  • 观察Taotoken用量看板如何清晰展示各项目与模型的Token消耗
  • Geek Cookbook完整指南:如何从零开始搭建高可用自托管平台
  • 从STM32到汽车电子:一个嵌入式工程师的DTC实战入门笔记(含代码示例)
  • 把迷宫走成‘时空穿梭’:用分层图BFS解决蓝桥杯AB交替路径问题
  • FF14技能特效优化:TexTools模组实战指南与视觉干扰解决方案
  • 浏览器端Node.js运行时实现原理与模拟技术详解
  • Android电池小部件完整指南:优雅监控电量的开源解决方案
  • 手把手教你用西门子博图组态SLM1320-P网关,实现Profinet与AS-I总线通信
  • 3步搭建免费开源翻译API:LibreTranslate私有化部署完整指南
  • 初创团队如何借助 Taotoken 统一管理多个 AI 模型 API 调用
  • 告别原生JSON的繁琐:用Delphi Helper实现SuperObject式的优雅操作(附完整uJSON_Helper单元)
  • 3步快速解密音乐文件:免费浏览器工具完全使用手册
  • 免费在线法线贴图生成器:3步创建专业3D纹理
  • 如何通过n8n-nodes-puppeteer实现无代码浏览器自动化?
  • NotionNext:基于Notion API与Next.js的静态博客搭建指南
  • Linux常用命令--持续更新