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

PHP 跨站文件上傳解決方案:API 設計、CURL 代理與雲存儲直傳三種架構

     編輯:PHP 相關知識

場景分析

用戶在站點 B 上傳文件,實際存儲到站點 A。核心挑戰:跨域限制、身份認證傳遞、大文件傳輸效能、安全性。

方案一:站點 A 提供上傳 API,站點 B 代理轉發(傳統模式)

站點 A:上傳接口

<?php
// A/api/upload.php
header('Content-Type: application/json');
header('Access-Control-Allow-Origin: https://site-b.com'); // 限定域名
header('Access-Control-Allow-Methods: POST, OPTIONS');
header('Access-Control-Allow-Headers: Content-Type, Authorization');

if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') exit(0);

// 驗證簽名/Token
$auth = validateApiToken($_SERVER['HTTP_AUTHORIZATION'] ?? '');
if (!$auth) {
    http_response_code(401);
    echo json_encode(['code' => 401, 'msg' => '未授權']);
    exit;
}

// 處理上傳
$file = $_FILES['file'] ?? null;
if (!$file || $file['error'] !== UPLOAD_ERR_OK) {
    echo json_encode(['code' => 400, 'msg' => '文件錯誤']);
    exit;
}

// 安全檢查
$allowedMimes = ['image/jpeg', 'image/png', 'video/mp4'];
$maxSize = 100 * 1024 * 1024; // 100MB
if (!in_array($file['type'], $allowedMimes) || $file['size'] > $maxSize) {
    echo json_encode(['code' => 400, 'msg' => '文件類型或大小不符']);
    exit;
}

// 存儲邏輯(本地/雲存儲)
$savePath = '/data/uploads/' . date('Y/m/d/');
@mkdir($savePath, 0755, true);
$filename = uniqid() . '_' . basename($file['name']);
$dest = $savePath . $filename;
move_uploaded_file($file['tmp_name'], $dest);

// 返回可訪問 URL
echo json_encode([
    'code' => 0,
    'data' => ['url' => 'https://cdn.site-a.com/' . $filename]
]);

function validateApiToken(string $header): bool
{
    // 實現:JWT 驗證、API Key 校驗、簽名驗證等
    return true; // 示例
}

站點 B:前端直傳 + 後端代理雙模式

<!-- B/upload.html -->
<input type="file" id="fileInput" accept="image/*,video/mp4">
<button onclick="uploadDirect()">直傳 A(推薦)</button>
<button onclick="uploadProxy()">代理上傳</button>

<script>
async function uploadDirect() {
    const file = document.getElementById('fileInput').files[0];
    if (!file) return alert('請選擇文件');

    // 1. 向 B 申請上傳憑證(簽名/臨時 Token)
    const {data: {token, uploadUrl}} = await fetch('/api/get-upload-token').then(r => r.json());

    // 2. 直接 POST 到 A(需 A 配置 CORS)
    const formData = new FormData();
    formData.append('file', file);
    const res = await fetch(uploadUrl, {
        method: 'POST',
        headers: {'Authorization': 'Bearer ' + token},
        body: formData
    });
    const result = await res.json();
    console.log('直傳結果:', result);
}

async function uploadProxy() {
    const file = document.getElementById('fileInput').files[0];
    const formData = new FormData();
    formData.append('file', file);
    // 經由 B 後端轉發
    const res = await fetch('/api/proxy-upload', {method: 'POST', body: formData});
    console.log('代理結果:', await res.json());
}
</script>
<?php
// B/api/proxy-upload.php - 代理轉發
$ch = curl_init('https://site-a.com/api/upload.php');
curl_setopt_array($ch, [
    CURLOPT_POST => true,
    CURLOPT_POSTFIELDS => ['file' => new CURLFile($_FILES['file']['tmp_name'], $_FILES['file']['type'], $_FILES['file']['name'])],
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . getInternalApiToken()],
    CURLOPT_TIMEOUT => 300, // 大文件需長超時
]);
$result = curl_exec($ch);
curl_close($ch);
echo $result; // 直接透傳 A 的 JSON 響應
?>

方案二:雲存儲直傳(現代最佳實踐)

雙方均不經過應用服務器,直接上傳至 OSS/S3/COS,應用服務器僅簽發簽名。

<?php
// 統一簽名服務(A/B 共用或各自部署)
function getOssUploadSignature(string $bucket, string $key, int $expire = 3600): array
{
    // 阿里云 OSS 範例
    $accessKeyId = 'YOUR_KEY';
    $accessKeySecret = 'YOUR_SECRET';
    $policy = json_encode([
        'expiration' => date('c', time() + $expire),
        'conditions' => [
            ['content-length-range', 0, 100 * 1024 * 1024],
            ['starts-with', '$key', 'uploads/'],
        ],
    ]);
    $policyBase64 = base64_encode($policy);
    $signature = base64_encode(hash_hmac('sha1', $policyBase64, $accessKeySecret, true));

    return [
        'host' => "https://{$bucket}.oss-cn-hangzhou.aliyuncs.com",
        'policy' => $policyBase64,
        'signature' => $signature,
        'accessKeyId' => $accessKeyId,
        'key' => $key,
        'expire' => $expire,
    ];
}
?>

方案三:分片上傳與斷點續傳(大文件專用)

  • 前端使用 WebUploader、Uppy、Plupload 等庫自動分片。
  • 後端需實現:POST /chunk 接收分片、POST /merge 合併分片、GET /check 斷點檢查。
  • 雲存儲原生支援分片上傳(Multipart Upload),推薦直接對接。

安全清單

  1. 文件類型驗證:檢查 MIME + 副檔名 + 魔數(finfo_file)。
  2. 文件名安全:重命名存儲,避免路徑遍歷、覆蓋、腳本執行。
  3. 存儲隔離:上傳目錄禁用腳本執行權限(Nginx: location ~ \.php$ { deny all; })。
  4. 防盜鏈/簽名 URL:私有文件採用帶簽名的臨時下載鏈接。
  5. 限流與審計:單用戶上傳頻率限制、記錄上傳日誌。

架構選型建議

場景推薦方案理由
中小文件、內網互信、快速上線方案一(代理轉發)開發簡單、可控性強
大文件、高併發、跨公網方案二(雲存儲直傳)減輕應用服務器壓力、CDN 加速
超大文件(>1GB)、弱網環境方案三(分片上傳)支援斷點續傳、分片並行

現代架構傾向於應用服務器不碰文件流,僅負責簽發授權憑證,將存儲壓力轉移至對象存儲服務,實現水平擴展與成本優化。

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