AJAX图片上传请求无法终止,后端PHP持续写入文件直至重启Apache
看起来你遇到了一个特别棘手的上传问题——AJAX请求一直处于pending状态,后端PHP还在不断生成一个越来越大的文件,只能重启Apache才能终止,这实在太闹心了!我来帮你梳理可能的原因和解决思路,先把你的代码整理清楚方便分析:
前端AJAX上传代码
$("body").on("change", "#product-image", function(e) { if ($.inArray($(this).val().split('.').pop().toLowerCase(), allowedExt) == -1) { alert("JPG JPEG PNG only"); $(this).val(null); } else { var formData = new FormData(); var file = $(this)[0].files[0]; formData.append("Image", file); formData.append("ProductID", <?= $ProductID; ?>); $.ajax({ url: "/rest/private/product/image/upload", method: "POST", data: formData, processData: false, contentType: false, cache: false, success: function(response) { var resp = JSON.parse(response); if (resp.Status == 1) { var url = resp.Data.URL; var order = resp.Data.Order; var ID = resp.Data.ID; self.showPreview("<?= base_url() ?>" + url, order, ID); } else { self.errorToast(resp.Error.Image); } }, error: function(resp) { self.errorToast("Error"); }, }); } });
后端CodeIgniter处理代码
$Image = $this->request->getFile("Image"); $maxW = "5000"; $maxH = "5000"; $maxSize = 4; $validationRule = [ "Image" => [ 'label' => 'Your Image', 'rules' => [ 'uploaded[Image]', 'is_image[Image]', 'mime_in[Image,image/jpg,image/jpeg,image/gif,image/png,image/webp,image/bmp,image/tiff]', 'max_size[Image,' . $maxSize * 1024 . ']', 'max_dims[Image,' . $maxW . "," . $maxH . ']', ], 'errors' => [ 'max_dims' => '{field} is bigger than (' . $maxW . "x" . $maxH . ')', 'max_size' => '{field} is bigger than ' . $maxSize . 'MB', ], ], ]; if (!$this->validateData(["Image" => $Image], $validationRule)) { $this->resp["Status"] = 0; $this->resp["Error"] = $this->validator->getErrors(); return; } if ($Image->isValid() && !$Image->hasMoved()) { $imageName = $Image->getRandomName(); $Image->move(ROOTPATH . 'public/uploads/product/', $imageName); }
可能的原因及解决思路
1. 后端move()方法异常未被捕获
CodeIgniter的move()方法在遇到磁盘权限不足、磁盘空间满、目录不存在等问题时,可能会抛出异常或进入异常状态,但如果没有捕获这些异常,程序可能会卡住并不断尝试写入,导致文件无限增大。
解决办法:
给move()方法添加异常捕获和日志记录,明确错误原因:
try { $moveSuccess = $Image->move(ROOTPATH . 'public/uploads/product/', $imageName); if (!$moveSuccess) { log_message('error', 'File move failed: ' . $Image->getErrorString()); $this->resp["Status"] = 0; $this->resp["Error"]["Image"] = '文件移动失败: ' . $Image->getErrorString(); return $this->response->setJSON($this->resp); } } catch (\Exception $e) { log_message('error', 'Upload exception: ' . $e->getMessage()); $this->resp["Status"] = 0; $this->resp["Error"]["Image"] = '上传异常: ' . $e->getMessage(); return $this->response->setJSON($this->resp); }
同时检查CodeIgniter的日志文件(通常在writable/logs/目录),查看具体的错误信息。
2. 上传目录权限或磁盘问题
如果上传目录ROOTPATH . 'public/uploads/product/'的权限不正确,或者磁盘空间不足、存在坏道,PHP在写入文件时可能会出现阻塞或无限重试的情况,导致文件持续增大。
解决办法:
- 确认上传目录的所有者为Apache/PHP的运行用户(如
www-data、apache),权限设置为755(目录)和644(文件)。 - 检查磁盘剩余空间,确保有足够的存储容量。
- 尝试将文件移动到其他目录(如系统临时目录),测试是否是当前目录的问题。
3. 程序执行流程异常,未正确返回响应
从你的代码看,验证通过并执行move()后,没有明确的响应返回逻辑,这可能导致Apache一直等待PHP输出响应,从而让请求一直处于pending状态。
解决办法:
在move()成功后,补充完整的响应返回逻辑,确保程序能正常结束:
if ($Image->isValid() && !$Image->hasMoved()) { try { $imageName = $Image->getRandomName(); $Image->move(ROOTPATH . 'public/uploads/product/', $imageName); // 补充返回逻辑 $this->resp["Status"] = 1; $this->resp["Data"] = [ "URL" => 'uploads/product/' . $imageName, "Order" => 0, // 根据你的业务逻辑设置正确的Order值 "ID" => 0 // 根据你的业务逻辑设置正确的ID值 ]; return $this->response->setJSON($this->resp); } catch (\Exception $e) { log_message('error', 'Upload failed: ' . $e->getMessage()); $this->resp["Status"] = 0; $this->resp["Error"]["Image"] = $e->getMessage(); return $this->response->setJSON($this->resp); } }
4. 前端事件重复绑定
你使用了$("body").on("change", "#product-image", ...)的事件委托方式,如果这段代码被多次执行(比如页面局部刷新后重新加载脚本),会导致同一个文件输入框的change事件被重复绑定,触发多个AJAX请求,进而导致后端出现异常。
解决办法:
- 在绑定事件前先解绑旧的事件:
$("body").off("change", "#product-image").on("change", "#product-image", ...) - 或者在前端AJAX请求发送前,添加一个“正在上传”的状态锁,防止重复发送:
// 在外部定义一个锁变量 let isUploading = false; $("body").on("change", "#product-image", function(e) { if (isUploading) return; // 正在上传时忽略新的请求 // ... 其他逻辑 ... $.ajax({ // ... 其他配置 ... beforeSend: function() { isUploading = true; }, complete: function() { isUploading = false; } }); });
5. ProductID输出异常
检查<?= $ProductID; ?>的输出是否正确,如果$ProductID包含特殊字符、HTML标签或未正确转义,可能会导致FormData格式异常,进而让后端处理逻辑出现问题。
解决办法:
- 在前端添加
console.log(formData),查看FormData的内容是否正确。 - 在后端开头添加日志,确认
ProductID的值:
log_message('debug', 'Received ProductID: ' . $this->request->getPost('ProductID'));
内容来源于stack exchange

