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

又拍雲表單 API 異步上傳實戰:前端 FormData + 後端簽名驗證 + 直傳雲存儲架構

     編輯:PHP 相關知識

零後端流量轉發:瀏覽器直傳又拍雲,後端僅簽發策略簽名

傳統上傳「前端→後端→又拍雲」佔用帶寬、增加延遲。又拍雲表單 API 支援瀏覽器直傳,後端僅負責生成 policy + signature,實現真正的異步直傳架構。本文提供完整前後端實現。

架構流程

  1. 前端請求後端 /api/upyun/policy 獲取簽名參數
  2. 前端構造 FormData,包含 policy、signature、file
  3. 前端 fetch 直發 https://v0.api.upyun.com/<bucket>
  4. 又拍雲驗證簽名,存儲文件,返回 JSON 結果
  5. 前端獲取文件 URL,提交業務表單

後端:簽名服務

<?php
// config/upyun.php
return [
    'bucket'   => 'your-bucket',
    'operator' => 'your-operator',
    'password' => 'your-password', // 運營者密碼
    'api_host' => 'v0.api.upyun.com',
    'expire'   => 3600, // 策略有效期(秒)
];

// app/Controller/Api/UpyunController.php
public function policy()
{
    $cfg = config('upyun');
    $policy = [
        'bucket'     => $cfg['bucket'],
        'expiration' => time() + $cfg['expire'],
        'save-key'   => '/{year}{mon}{day}/{random32}{.suffix}', // 自定義命名規則
        'allow-file-type' => 'image/*',
        'content-length-range' => '0,5242880', // 5MB
        // 'x-gmkerl' => 'thumbnail/200x200', // 可選:上傳預處理
    ];
    $policyJson = json_encode($policy);
    $policyBase64 = base64_encode($policyJson);
    $signature = md5($policyBase64 . '&' . $cfg['password']);

    return json(['policy' => $policyBase64, 'signature' => $signature, 'url' => 'https://' . $cfg['api_host'] . '/' . $cfg['bucket']]);
}

前端:直傳邏輯 (Vue 3 Composition API 示例)

<script setup>
import { ref } from 'vue';

const file = ref(null);
const preview = ref('');
const uploading = ref(false);
const uploadedUrl = ref('');

const pickFile = () => document.getElementById('fileInput').click();

const handleFileChange = async (e) => {
  const f = e.target.files[0];
  if (!f) return;
  if (!f.type.startsWith('image/')) return alert('請選擇圖片');
  if (f.size > 5 * 1024 * 1024) return alert('文件超過 5MB');

  file.value = f;
  preview.value = URL.createObjectURL(f);
};

const upload = async () => {
  if (!file.value) return alert('請先選擇文件');
  uploading.value = true;

  try {
    // 1. 獲取簽名
    const { policy, signature, url } = await $http.get('/api/upyun/policy');

    // 2. 構造 FormData
    const formData = new FormData();
    formData.append('policy', policy);
    formData.append('signature', signature);
    formData.append('file', file.value);

    // 3. 直傳又拍雲
    const res = await fetch(url, { method: 'POST', body: formData });
    const result = await res.json();

    if (result.code === 200) {
      uploadedUrl.value = 'https://' + result.url; // 又拍雲返回相對路徑
      alert('上傳成功:' + uploadedUrl.value);
    } else {
      throw new Error(result.message || '上傳失敗');
    }
  } catch (err) {
    console.error(err);
    alert('上傳異常:' + err.message);
  } finally {
    uploading.value = false;
  }
};
</script>

<template>
  <div class="uploader">
    <input type="file" id="fileInput" @change="handleFileChange" accept="image/*" hidden>
    <button @click="pickFile" :disabled="uploading">選擇圖片</button>
    <div v-if="preview" class="preview">
      <img :src="preview" alt="預覽">
      <button @click="upload" :disabled="uploading">{{ uploading ? '上傳中...' : '開始上傳' }}</button>
    </div>
    <div v-if="uploadedUrl" class="result">
      <a :href="uploadedUrl" target="_blank">查看文件</a>
    </div>
  </div>
</template>

關鍵參數說明

參數含義示例
save-key存儲路徑模板,支援 {year} {mon} {day} {random32} {.suffix}/{year}{mon}{day}/{random32}{.suffix}
allow-file-typeMIME 白名單,支援通配符image/*
content-length-range文件大小範圍 (字節)0,5242880
x-gmkerl上傳預處理參數(縮圖、水印、格式轉換)thumbnail/200x200
return-url同步回調 URL(表單上傳模式用)https://your.com/callback

安全與最佳實踐

  • 策略最小權限:expiration 設 5-10 分鐘,allow-file-type、content-length-range 嚴格限制。
  • 運營者密碼:絕不 前端暴露,僅後端簽名時使用,定期輪換。
  • 防重放:策略包含 expiration,過期自動失效。
  • 異步通知:設置 notify-url 異步回調,後端入庫文件元數據,避免前端丟包導致數據不一致。
  • CDN 回源:配置又拍雲 CDN 加速域名,前端直接使用 CDN 域名訪問。

常見錯誤碼對照

代碼含義處理
400策略解析失敗/參數缺失檢查 policy JSON 格式、必填字段
401簽名驗證失敗核對 password、policy base64 編碼、拼接順序
413文件超過大小限制前端攔截、調整 content-length-range
415類型不允許前端 MIME 檢查、調整 allow-file-type
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved