┌──────────────┐ SWF 上傳 ┌─────────────┐ Base64 POST ┌──────────┐
│ 瀏覽器 │ ──────────────▶ │ Flash SWF │ ──────────────────▶ │ PHP │
│ 加載 SWF │ 選圖/裁剪/縮放 │ 裁剪預覽 │ pic/pic1/pic2/pic3 │ upfile.php │
└──────────────┘ └─────────────┘ └──────────┘
│
生成 3 尺寸 PNG
返回 JSON 狀態
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Flash 頭像上傳</title>
<style>
html, body { height: 100%; background: #fff; margin: 0; }
#flashContent { width: 100%; height: 100%; }
#avatar_preview img { max-width: 120px; margin: 5px; border: 1px solid #ddd; }
</style>
<script>
function uploadCallback(status) {
status += '';
const preview = document.getElementById('avatar_preview');
const ts = Date.now();
switch (status) {
case '1': // 成功
preview.innerHTML =
"頭像1 (162px): <img src='1.png?" + ts + "'/><br>" +
"頭像2 (48px): <img src='2.png?" + ts + "'/><br>" +
"頭像3 (20px): <img src='3.png?" + ts + "'/>";
break;
case '-1': // 失敗/重試
default:
location.reload();
}
}
</script>
</head>
<body>
<div id="altContent">
<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"
codebase="https://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=10,0,0,0"
width="650" height="450" id="avatarUploader">
<param name="movie" value="avatar.swf" />
<param name="quality" value="high" />
<param name="bgcolor" value="#FFFFFF" />
<param name="wmode" value="transparent" />
<param name="allowScriptAccess" value="always" />
<param name="flashvars"
value="imgUrl=./default.jpg&uploadUrl=./upfile.php&uploadSrc=false" />
<embed src="avatar.swf" quality="high" bgcolor="#FFFFFF"
width="650" height="450" wmode="transparent"
flashvars="imgUrl=./default.jpg&uploadUrl=./upfile.php&uploadSrc=false"
allowScriptAccess="always"
type="application/x-shockwave-flash"
pluginspage="https://www.adobe.com/go/getflashplayer">
</embed>
</object>
</div>
<div id="avatar_preview"></div>
</body>
</html>
<?php
header('Content-Type: application/json; charset=utf-8');
// 配置
$saveDir = __DIR__ . '/uploads/';
$maxSize = 5 * 1024 * 1024; // 5MB
$allowedMime = ['image/jpeg', 'image/png', 'image/gif'];
if (!is_dir($saveDir)) {
mkdir($saveDir, 0755, true);
}
// 驗證請求
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
exit(json_encode(['status' => -1, 'msg' => 'Invalid method']));
}
// 解碼 Base64 圖片數據
$decode = function($key) use ($maxSize, $allowedMime) {
if (empty($_POST[$key])) return false;
$data = base64_decode($_POST[$key], true);
if ($data === false || strlen($data) > $maxSize) return false;
// 簡單 MIME 檢測
$finfo = new finfo(FILEINFO_MIME_TYPE);
$mime = $finfo->buffer($data);
if (!in_array($mime, $allowedMime)) return false;
return $data;
};
$src = $decode('pic'); // 原圖
$pic1 = $decode('pic1'); // 162px
$pic2 = $decode('pic2'); // 48px
$pic3 = $decode('pic3'); // 20px
if (!$pic1 || !$pic2 || !$pic3) {
exit(json_encode(['status' => -1, 'msg' => 'Invalid image data']));
}
// 保存文件
$timestamp = time();
$files = [
'src' => $saveDir . $timestamp . '_src.png',
'162' => $saveDir . $timestamp . '_162.png',
'48' => $saveDir . $timestamp . '_48.png',
'20' => $saveDir . $timestamp . '_20.png',
];
$success = true;
foreach ($files as $key => $path) {
$data = ${$key} ?? null;
if ($data && file_put_contents($path, $data) === false) {
$success = false;
break;
}
}
if ($success) {
echo json_encode([
'status' => 1,
'files' => array_map(fn($p) => basename($p), $files)
]);
} else {
// 回滾已寫入文件
foreach ($files as $path) { @unlink($path); }
echo json_encode(['status' => -1, 'msg' => 'Save failed']);
}
?>
| 技術點 | 說明 |
|---|---|
| Flash 繞過瀏覽器上傳限制 | 可跨域、支持大文件、顯示進度條、客戶端裁剪 |
| Base64 編碼傳輸 | Flash 將二進制圖片編碼為字符串 POST,避免 multipart 解析問題 |
| 客戶端多尺寸裁剪 | Flash 內部生成 162/48/20px 三套圖,服務端只存不處理 |
| 時間戳防緩存 | 預覽圖 URL 加 ?timestamp 強制刷新 |
純 HTML5 + JavaScript 方案,無需 Flash:
<!-- 現代 HTML5 版本片段 --> <input type="file" id="avatarInput" accept="image/*" capture> <canvas id="cropCanvas"></canvas> <script src="cropper.min.js"></script> <script> // Cropper.js 初始化 + Canvas toBlob 上傳 </script>
navigator.plugins['Shockwave Flash'] 決定加載哪套方案Flash 曾是上傳裁剪的「瑞士軍刀」,如今已成歷史。新項目請擁抱 HTML5 生態。