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

CodeIgniter框架下PHP+JQuery上传AWS S3时进度条仅跟踪本地问题

我太懂这个痛点了!之前帮朋友调过类似的CodeIgniter+S3上传场景,进度条唰一下跑到100%,结果用户盯着空等半天——因为前端只跟踪了「文件传到自己服务器」的那一小段,真正耗时的「服务器传去S3」的过程完全黑箱,体验糟透了。

下面给你两种针对性的解决方案,按需选:

方案一:PHP中转+实时推送S3上传进度(适合必须经过服务器处理的场景)

如果你的业务逻辑要求文件必须先过PHP(比如验证权限、修改文件内容、写入数据库记录),那可以利用AWS SDK的进度回调,结合PHP的输出缓冲,把S3上传的进度实时推给前端。

1. CodeIgniter控制器代码调整

先关闭CI的输出缓冲,避免进度被缓存,然后给S3客户端添加进度监控中间件:

public function upload_to_s3() {
    // 关闭输出缓冲,确保进度实时返回
    $this->output->set_header('Content-Type: text/plain');
    $this->output->set_header('Cache-Control: no-cache');
    $this->output->set_header('Connection: keep-alive');
    
    // 获取前端上传的文件
    $uploadedFile = $_FILES['user_file'];
    $bucketName = '你的存储桶名称';
    $s3Key = 'uploads/' . uniqid() . '_' . $uploadedFile['name'];
    
    // 初始化S3客户端
    $s3 = new Aws\S3\S3Client([
        'version' => 'latest',
        'region'  => '你的存储桶区域',
    ]);
    
    // 创建上传命令
    $uploadCommand = $s3->getCommand('PutObject', [
        'Bucket' => $bucketName,
        'Key'    => $s3Key,
        'SourceFile' => $uploadedFile['tmp_name'],
    ]);
    
    // 添加进度监控中间件
    $handler = $s3->getHandlerList()->resolve();
    $handler = Aws\Middleware::monitor($handler, function ($transferStats) {
        if ($transferStats->hasResponse()) {
            // 上传完成
            echo "progress:100\n";
            flush();
        } else {
            // 计算并输出当前进度
            $progressData = $transferStats->getTransferProgress();
            $percent = round(($progressData['bytes_uploaded'] / $progressData['total_bytes']) * 100);
            echo "progress:$percent\n";
            flush(); // 强制输出缓冲区内容
        }
    });
    $s3->getHandlerList()->setHandler($handler);
    
    try {
        $s3->execute($uploadCommand);
        echo "done:success\n";
        flush();
    } catch (Exception $e) {
        echo "done:error\n";
        flush();
    }
}

2. 前端代码调整

监听AJAX的onreadystatechange事件,实时解析PHP返回的进度数据:

const fileInput = document.getElementById('file-input');
const progressBar = document.getElementById('progress-bar');

fileInput.addEventListener('change', function(e) {
    const file = e.target.files[0];
    const formData = new FormData();
    formData.append('user_file', file);
    
    const xhr = new XMLHttpRequest();
    xhr.open('POST', '/upload/upload_to_s3', true);
    
    xhr.onreadystatechange = function() {
        if (xhr.readyState === XMLHttpRequest.LOADING) {
            // 分割多次返回的进度数据
            const responseParts = xhr.responseText.split('\n');
            responseParts.forEach(part => {
                if (part.startsWith('progress:')) {
                    const percent = parseInt(part.split(':')[1]);
                    progressBar.style.width = `${percent}%`;
                    progressBar.textContent = `${percent}%`;
                } else if (part.startsWith('done:')) {
                    const status = part.split(':')[1];
                    status === 'success' 
                        ? alert('上传完成!') 
                        : alert('上传失败,请重试');
                }
            });
        }
    };
    
    xhr.send(formData);
});

关键配置注意

在CodeIgniter的config.php里关闭输出压缩,否则进度会被缓存:

$config['compress_output'] = FALSE;

同时调整PHP的max_execution_time,给大文件上传留足够时间。

方案二:预签名URL直接前端上传(更高效,推荐)

如果你的业务不需要服务器额外处理文件,直接让前端用预签名URL上传到S3是最优解——不仅减轻服务器压力,还能让前端的XHR直接监控到上传到S3的完整进度。

1. CodeIgniter生成预签名URL

public function get_presigned_url() {
    $fileName = $this->input->post('file_name');
    $fileType = $this->input->post('file_type');
    
    $s3 = new Aws\S3\S3Client([
        'version' => 'latest',
        'region'  => '你的存储桶区域',
    ]);
    
    $command = $s3->getCommand('PutObject', [
        'Bucket' => '你的存储桶名称',
        'Key'    => 'uploads/' . uniqid() . '_' . $fileName,
        'ContentType' => $fileType,
        'ACL' => 'public-read' // 根据需求设置文件权限
    ]);
    
    // 生成15分钟内有效的预签名URL
    $presignedUrl = $s3->createPresignedRequest($command, '+15 minutes')->getUri();
    
    echo json_encode(['url' => (string)$presignedUrl]);
}

2. 前端直接上传

const fileInput = document.getElementById('file-input');
const progressBar = document.getElementById('progress-bar');

fileInput.addEventListener('change', function(e) {
    const file = e.target.files[0];
    
    // 先请求预签名URL
    fetch('/upload/get_presigned_url', {
        method: 'POST',
        headers: {
            'Content-Type': 'application/x-www-form-urlencoded',
        },
        body: `file_name=${encodeURIComponent(file.name)}&file_type=${encodeURIComponent(file.type)}`
    })
    .then(res => res.json())
    .then(data => {
        // 用预签名URL直接上传到S3
        const xhr = new XMLHttpRequest();
        xhr.open('PUT', data.url, true);
        xhr.setRequestHeader('Content-Type', file.type);
        
        // 直接监控S3上传进度
        xhr.upload.onprogress = function(e) {
            if (e.lengthComputable) {
                const percent = Math.round((e.loaded / e.total) * 100);
                progressBar.style.width = `${percent}%`;
                progressBar.textContent = `${percent}%`;
            }
        };
        
        xhr.onload = function() {
            xhr.status === 200 
                ? alert('上传完成!') 
                : alert('上传失败,请重试');
        };
        
        xhr.send(file);
    });
});

两种方案各有适用场景:方案一适合需要服务器介入的业务,方案二更轻量高效。如果遇到SDK版本兼容、CI配置的细节问题,随时提!

内容的提问来源于stack exchange,提问作者David

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.20 08:57:16