You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

AJAX图片上传请求无法终止,后端PHP持续写入文件直至重启Apache

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.07 07:25:30