<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/