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

PHP 強制下載函數精簡版:ThinkPHP 風格實作

     編輯:PHP 綜合知識

針對 ThinkPHP / Laravel 等框架常見的資料表結構(id, filename, savename, url, downloads),提供一個精簡的 down() 動作:驗證記錄、累計下載數、輸出檔案。

資料表結構假設

CREATE TABLE `downloads` (
  `id` INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
  `filename` VARCHAR(255) NOT NULL COMMENT '原始檔名',
  `savename` VARCHAR(255) NOT NULL COMMENT '儲存檔名',
  `url` VARCHAR(500) DEFAULT NULL COMMENT '外部連結或相對路徑',
  `downloads` INT UNSIGNED DEFAULT 0,
  `status` TINYINT DEFAULT 1
);

控制器動作

public function down() {
    $id = (int)input('id');
    if (!$id) exit('參數錯誤');

    $model = model('Downloads');
    $data = $model->where('id', $id)->where('status', 1)->find();
    if (!$data) exit('記錄不存在或已下架');

    // 解決中文檔名編碼
    $filename = iconv('UTF-8', 'GBK//IGNORE', $data['filename']);
    $savePath = $data['url'] ?: 'Uploads/file/' . $data['savename'];
    $absPath = realpath($savePath);

    if (!$absPath || !is_file($absPath)) exit('檔案不存在');

    // 累計下載數(原子操作)
    $model->where('id', $id)->setInc('downloads');

    // 輸出 Header
    header('Content-Type: application/octet-stream');
    header('Content-Disposition: attachment; filename="' . rawurlencode($filename) . '"');
    header('Content-Length: ' . filesize($absPath));
    header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
    header('Pragma: public');

    // 分塊輸出
    $fp = fopen($absPath, 'rb');
    while (!feof($fp)) {
        echo fread($fp, 8192);
        @ob_flush(); flush();
    }
    fclose($fp);
    exit;
}

關鍵點解析

  1. iconv 轉碼:中文檔名在 Windows 伺服器需轉 GBK,Linux 可保持 UTF-8,建議雙重 filename* 模式。
  2. realpath 防遍歷:確保路徑在允許目錄內。
  3. setInc 原子遞增:避免併發下計數錯失。
  4. 分塊輸出:8KB 緩衝區平衡記憶體與效能。
  5. application/octet-stream:通用二進位類型,強制下載不預覽。

進階:支援 Range 斷點續傳

$fileSize = filesize($absPath);
$range = $_SERVER['HTTP_RANGE'] ?? '';
if ($range) {
    list(, $range) = explode('=', $range);
    list($start, $end) = explode('-', $range);
    $start = (int)$start;
    $end = $end ? (int)$end : $fileSize - 1;
    $length = $end - $start + 1;

    header('HTTP/1.1 206 Partial Content');
    header('Content-Range: bytes ' . $start . '-' . $end . '/' . $fileSize);
    header('Content-Length: ' . $length);
    header('Accept-Ranges: bytes');

    $fp = fopen($absPath, 'rb');
    fseek($fp, $start);
    $remaining = $length;
    while (!feof($fp) && $remaining > 0) {
        $chunk = min(8192, $remaining);
        echo fread($fp, $chunk);
        $remaining -= $chunk;
        @ob_flush(); flush();
    }
    fclose($fp);
    exit;
}

Nginx X-Accel-Redirect 卸載(高併發推薦)

# nginx.conf
location /protected/ {
    internal;                    # 僅內部重導向可存取
    alias /var/www/files/;       # 實體檔案根目錄
}

# PHP 只需驗證權限後:
header('X-Accel-Redirect: /protected/' . $relativePath);
header('Content-Disposition: attachment; filename="..."');
exit;

此精簡版下載動作可直接整合至任何 MVC 控制器,並可平滑升級為斷點續傳或 Web Server 卸載模式。

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