首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 相關知識

PHP 利用 APC 實現文件上傳進度條:原理、配置與完整代碼實戰

     編輯:PHP 相關知識

PHP 利用 APC 實現文件上傳進度條:原理、配置與完整代碼實戰

在 HTML5 <input type="file"> 原生支持 progress 事件之前,PHP 5.2+ 引入的 APC 擴展提供了 APC_UPLOAD_PROGRESS 機制,成為當時實現上傳進度條的主流方案。雖然現代項目多改用前端 XMLHttpRequest.upload.onprogress 或 fetch 配合後端流式處理,但理解 APC 方案有助於掌握 PHP 會話級緩存與文件上傳底層機制。本文系統梳理原理、配置、前後端完整代碼及避坑指南。

核心原理:APC_UPLOAD_PROGRESS 機制

APC(Alternative PHP Cache)是 PHP 的 opcode 緩存擴展。自 PHP 5.2 起,APC 新增 APC_UPLOAD_PROGRESS 功能:

  1. 上傳表單隱藏域攜帶唯一標識:<input type="hidden" name="APC_UPLOAD_PROGRESS" value="唯一ID">
  2. PHP 接收上傳時,自動檢查 $_POST['APC_UPLOAD_PROGRESS'],以 upload_唯一ID 為鍵在 APC 共享內存中寫入進度數據
  3. 前端通過 AJAX 輪詢後端接口,讀取 APC 緩存中的 current(已上傳字節)、total(總字節)、rate(速度)等字段
  4. 進度條達 100% 或上傳完成後停止輪詢

關鍵優勢:臨時文件達閾值自動落盤,避免大文件佔滿內存;無需修改 PHP 核心,僅需擴展配置。

APC 關鍵配置參數詳解

在 php.ini 中配置 [apc] 區段,核心參數如下:

參數默認值說明建議生產值
apc.enabled1啟用/禁用 APC1
apc.shm_size32M共享內存大小,單位 MB128M~256M(視上傳並發)
apc.upload_progress.enabled1啟用上傳進度追蹤1
apc.upload_progress.prefixupload_緩存鍵前綴upload_
apc.upload_progress.freq0更新頻率(0=盡可能頻繁)0 或 1024
apc.upload_progress.min_size1M啟用進度追蹤的最小文件1M
apc.ttl0緩存條目生存秒數,0=永不失效(風險)3600~7200
apc.gc_ttl3600垃圾回收生存時間3600
apc.max_file_size1M單文件緩存上限按需調大

注意:APC 在 PHP 5.5+ 被 Zend OpCache 取代,上傳進度功能已移至 PHP 核心 Session 上傳進度(session.upload_progress.enabled),新項目請優先使用 Session 方案或前端原生進度事件。

完整實戰代碼(前端 + 後端)

1. 上傳表單頁面

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>文件上傳進度演示</title>
    <style>
        #progressouter { display:none; width:400px; height:20px; border:1px solid #ccc; border-radius:4px; overflow:hidden; }
        #progressinner { width:0%; height:100%; background:#4CAF50; transition:width 0.3s; }
        #upstatus { margin-top:8px; font-size:14px; color:#333; }
    </style>
</head>
<body>
<form id="upload_form" action="upload.php" method="POST" enctype="multipart/form-data">
    <input type="hidden" name="APC_UPLOAD_PROGRESS" id="progress_key" value="upid_<?php echo uniqid(); ?>">
    視頻標題:<input type="text" name="subject" required><br>
    視頻說明:<input type="text" name="content"><br>
    視頻文件:<input type="file" name="upfile" id="upfile" required><br>
    <input type="submit" value="開始上傳" onclick="startProgress('upid_<?php echo uniqid(); ?>'); return true;">
</form>

<div id="upstatus"></div>
<div id="progressouter"><div id="progressinner"></div></div>

<script src="https://cdn.jsdelivr.net/npm/jquery@3.6.0/dist/jquery.min.js"></script>
<script>
function startProgress(upid) {
    $('#progressouter').show();
    $('#upstatus').html('準備上傳...');
    setTimeout(function(){ getProgress(upid); }, 100);
}

function getProgress(upid) {
    $.getJSON('progress.php', { progress_key: upid }, function(json) {
        if (!json || json.total === 0) {
            $('#upstatus').html('等待上傳開始...');
            setTimeout(function(){ getProgress(upid); }, 500);
            return;
        }
        var percent = Math.min(100, Math.round(json.current / json.total * 100));
        $('#progressinner').css('width', percent + '%');
        $('#upstatus').html('文件大小: ' + (json.total/1024/1024).toFixed(2) + ' MB | 已上傳: ' + (json.current/1024/1024).toFixed(2) + ' MB (' + percent + '%)');

        if (percent < 100) {
            setTimeout(function(){ getProgress(upid); }, 200);
        } else {
            $('#upstatus').html('上傳完成,服務器處理中...');
        }
    }).fail(function() {
        $('#upstatus').html('進度查詢失敗,重試中...');
        setTimeout(function(){ getProgress(upid); }, 1000);
    });
}
</script>
</body>
</html>

2. 進度查詢接口 — progress.php

<?php
// progress.php
header('Content-Type: application/json; charset=utf-8');

if (empty($_GET['progress_key'])) {
    echo json_encode(['error' => 'Missing progress_key']);
    exit;
}

$key = 'upload_' . $_GET['progress_key'];
$status = apc_fetch($key);

if ($status === false || !isset($status['total'])) {
    // 上傳未開始或已結束
    echo json_encode(['total' => 0, 'current' => 0, 'rate' => 0, 'done' => false]);
    exit;
}

echo json_encode([
    'total'     => (int)$status['total'],
    'current'   => (int)$status['current'],
    'rate'      => (int)($status['rate'] ?? 0),
    'done'      => $status['done'] ?? false
]);
?>

3. 文件處理邏輯 — upload.php

<?php
// upload.php
// 實際業務邏輯:驗證、移動、入庫、生成縮略圖等
$uploadDir = __DIR__ . '/uploads/';
if (!is_dir($uploadDir)) mkdir($uploadDir, 0755, true);

if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_FILES['upfile'])) {
    $file = $_FILES['upfile'];
    if ($file['error'] !== UPLOAD_ERR_OK) {
        die('上傳錯誤代碼: ' . $file['error']);
    }
    // 簡單驗證:類型、大小
    $allowed = ['video/mp4', 'video/x-msvideo', 'video/quicktime'];
    $finfo = finfo_open(FILEINFO_MIME_TYPE);
    $mime = finfo_file($finfo, $file['tmp_name']);
    if (!in_array($mime, $allowed)) {
        die('不支持的文件類型');
    }
    if ($file['size'] > 500 * 1024 * 1024) { // 500MB
        die('文件過大');
    }
    $ext = pathinfo($file['name'], PATHINFO_EXTENSION);
    $dest = $uploadDir . uniqid('vid_') . '.' . $ext;
    if (move_uploaded_file($file['tmp_name'], $dest)) {
        echo "上傳成功:$dest";
        // 這裡可入庫、發送隊列任務轉碼等
    } else {
        echo "移動文件失敗";
    }
}
?>

常見問題與避坑指南

  • 進度條卡在 0%:檢查 apc.upload_progress.enabled=1、表單 enctype="multipart/form-data"、隱藏域 name="APC_UPLOAD_PROGRESS" 在 <input type="file"> 之前。
  • APC 緩存鍵不存在:上傳尚未觸發 PHP 處理(Nginx/Apache 緩衝)、apc.rfc1867 未啟用(舊版參數)、文件過小未達 min_size 閾值。
  • 大文件上傳失敗:調大 upload_max_filesize、post_max_size、max_execution_time、max_input_time、Nginx client_max_body_size。
  • 併發上傳進度錯亂:確保每次上傳生成唯一 progress_key(用 uniqid() 或 UUID),前端輪詢攜帶正確 key。
  • PHP 7+ 環境:APC 已不維護,改用 pecl install apcu(僅用戶緩存)+ 核心 session.upload_progress,或直接用前端 xhr.upload.onprogress 方案。

現代替代方案對比

方案原理優點缺點適用場景
APC_UPLOAD_PROGRESSPHP 擴展共享內存無需前端特殊支持、PHP 5.2+ 兼容依賴擴展、PHP 7+ 棄用、調試困難老項目維護、無法升級 PHP 版本
Session Upload ProgressPHP 核心 Session 存儲無額外擴展、配置簡單、PHP 5.4+ 內置需啟用 Session、文件鎖可能阻塞中小型項目、傳統表單上傳
XHR/Fetch 原生進度瀏覽器原生事件無後端依賴、實時性最好、支持暫停續傳需前端 JS 實現、不支持傳統 form 提交現代 SPA、大文件分片上傳、斷點續傳
WebUploader / Uppy / FineUploader成熟前端庫封裝分片、並發、斷點、預覽、國際化開箱即用引入體積、學習成本生產級文件上傳系統

總結

APC 上傳進度方案是 PHP 早期解決大文件上傳用戶體驗的經典實踐,展示了「共享內存 + 輪詢」的架構思想。現代項目建議採用 前端原生進度事件 + 後端分片/流式處理 架構,結合 Uppy/WebUploader 等成熟庫,實現斷點續傳、並發分片、秒傳等企業級功能。理解歷史方案有助於在遷移重構時平滑過渡,也能在面試中展現技術演進視野。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved