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

如何实现PHP向Javascript定期返回响应?

实现单次请求的实时进度反馈方案

核心思路

你要的是单次HTTP请求下的流式响应——PHP边执行文件复制任务边输出进度,JS实时接收这些输出,而非等任务全完成才拿到结果。这和“JS定时发请求查进度”是完全不同的方案,更适配你的长耗时场景。

PHP端改造要点

你原来用return返回结果的方式必须调整,因为return只有在脚本执行完毕后才会把内容输出给客户端,没法做到实时反馈。必须改成echo输出,配合流式传输实现进度推送:

  1. 开启流式传输:设置响应头告知客户端这是分块传输的内容
  2. 关闭/刷新输出缓冲:确保每次输出的进度能立刻发送到客户端
  3. 计算并输出进度:先统计总文件数,每复制一部分就计算完成比例,输出结构化的进度数据(比如JSON)
  4. 延长脚本执行时间:文件复制耗时久,必须避免PHP超时中断任务

示例PHP代码:

<?php
// 关闭超时限制,确保任务能完整执行
set_time_limit(0);
// 设置响应头,开启分块传输
header('Transfer-Encoding: chunked');
header('Content-Type: application/json');
// 禁用输出缓冲,自动刷新内容
ob_implicit_flush(true);
ob_end_flush();

// 1. 统计沙箱中需要复制的总文件数(跳过不需要同步的文件)
$sandboxDir = '/path/to/your/sandbox';
$excludeFiles = ['robots.txt', 'sitemap.xml'];
$totalFiles = 0;
$iterator = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($sandboxDir));
foreach ($iterator as $file) {
    if ($file->isFile() && !in_array($file->getFilename(), $excludeFiles)) {
        $totalFiles++;
    }
}

// 2. 开始复制文件并推送进度
$copiedFiles = 0;
foreach ($iterator as $file) {
    if (!$file->isFile() || in_array($file->getFilename(), $excludeFiles)) continue;
    
    // 执行复制操作
    $targetPath = '/path/to/production/' . $iterator->getSubPathName();
    if (!is_dir(dirname($targetPath))) {
        mkdir(dirname($targetPath), 0755, true);
    }
    copy($file->getPathname(), $targetPath);
    
    // 3. 计算并输出进度(进度变化时才推送,减少冗余)
    $copiedFiles++;
    $progress = round(($copiedFiles / $totalFiles) * 100);
    static $lastProgress = 0;
    if ($progress !== $lastProgress) {
        $lastProgress = $progress;
        echo json_encode(['progress' => $progress]) . "\n";
        flush();
    }
}

// 任务完成,推送100%进度
echo json_encode(['progress' => 100]) . "\n";
flush();
?>

JS端改造要点

你不想改动现有fetch逻辑的核心结构,可在then里用流读取器处理实时响应,替代原来等待全量内容的response.text():

  1. 从response.body获取读取器,逐块读取PHP输出的内容
  2. 解析每块内容,提取进度信息并更新UI/日志
  3. 保留原来的catch逻辑处理请求错误

示例JS代码:

await fetch(_url_of_site_and_name_of_PHP_file_receiving_the_javascript_request, {
    method: 'POST',
    body: 'mirrorsites',
    headers: { 'Content-type': 'application/json; charset=UTF-8', },
}).then(async response => {
    // 获取流读取器,逐块读取响应内容
    const reader = response.body.getReader();
    const decoder = new TextDecoder('utf-8');
    
    while (true) {
        const { done, value } = await reader.read();
        if (done) break;
        
        // 解析PHP输出的每一行JSON进度数据
        const chunks = decoder.decode(value).split('\n');
        chunks.forEach(chunk => {
            if (!chunk.trim()) return;
            try {
                const data = JSON.parse(chunk);
                console.log(`${data.progress}% of your site is done.`);
                // 这里可添加进度条更新等UI逻辑
            } catch (e) {
                console.error('进度解析失败:', e);
            }
        });
    }
}).catch(error => {
    console.log('Whoops. Something went wrong.');
});

和你提到的StackOverflow方案的区别

那个方案是JS定时发起请求查询进度,需要PHP把进度存在缓存(如Session、Redis)中,每次请求读取最新状态。而我们用的是单次请求流式响应,PHP直接实时推送进度,不需要额外存储进度,也减少了HTTP请求次数,更匹配你的需求。

注意事项

  • 确保服务器支持分块传输:Apache、Nginx等现代服务器默认支持,无需额外配置
  • 避免多余输出:PHP脚本里不要有空白字符或无关echo,否则会破坏流式响应结构
  • 处理中断场景:如果用户中途关闭页面,PHP可能仍在后台执行,可新增任务中断逻辑(比如用Session标记任务状态)

内容的提问来源于stack exchange,提问作者Steve G.

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.21 20:25:54