
php 的 `$_files` 仅支持 `multipart/form-data` 表单上传,无法直接解析 json 中的文件内容;若需通过 raw json(如 application/json)传递文件,必须先将文件编码为 base64 字符串嵌入字段,并在后端解码还原为临时文件。
在标准 REST 实践中,文件上传与结构化数据传输应分离处理:multipart/form-data 是唯一原生支持二进制文件 + 元数据混合提交的 HTTP 编码方式,而 application/json 仅适用于纯文本数据。但若因前端限制(如某些 SDK 不支持 multipart)、网关策略或统一接口设计等原因,必须将文件“嵌入” JSON 请求体,则唯一可行方案是 Base64 编码传输。
✅ 正确做法:JSON 中嵌入 Base64 文件数据
前端需将文件读取为二进制并编码为 Base64 字符串,填入 JSON 字段:
{
"id": 1,
"mydata": [
{
"entity_id": 1,
"upload_file": "UEsDBBQAAAAIAJi..." // 完整 Base64 字符串(无换行、无前缀)
},
{
"entity_id": 2,
"upload_file": "iVBORw0KGgoAAAANSUhEUg..."
}
]
}⚠️ 注意:不要添加 data:image/png;base64, 等 Data URL 前缀——PHP 后端只需原始 Base64 内容。
? 后端 PHP 解码与保存示例
// 1. 获取原始 JSON 输入
$jsonInput = file_get_contents('php://input');
$data = json_decode($jsonInput, true);
if (json_last_error() !== JSON_ERROR_NONE) {
http_response_code(400);
echo json_encode(['error' => 'Invalid JSON']);
exit;
}
// 2. 遍历 mydata 数组,解码并保存每个 upload_file
foreach ($data['mydata'] as $item) {
$entityId = $item['entity_id'] ?? null;
$base64Data = $item['upload_file'] ?? '';
if (empty($base64Data)) continue;
// 移除可能的空白符和换行
$base64Data = str_replace(['\n', '\r', ' ', "\t"], '', $base64Data);
// 验证 Base64 格式(可选)
if (!preg_match('/^[a-zA-Z0-9\/+]*={0,2}$/', $base64Data)) {
error_log("Invalid Base64 for entity_id: $entityId");
continue;
}
// 解码为二进制
$binaryData = base64_decode($base64Data);
if ($binaryData === false) {
error_log("Base64 decode failed for entity_id: $entityId");
continue;
}
// 生成唯一文件名(示例)
$fileName = "upload_{$entityId}_" . date('YmdHis') . '.bin';
$filePath = sys_get_temp_dir() . '/' . $fileName;
// 保存为临时文件(模拟 $_FILES 行为)
if (file_put_contents($filePath, $binaryData) !== false) {
// 可选:构造类 $_FILES 的模拟结构用于后续业务逻辑
$simulatedFiles[] = [
'name' => $fileName,
'type' => mime_content_type($filePath),
'tmp_name' => $filePath,
'error' => UPLOAD_ERR_OK,
'size' => filesize($filePath),
];
}
}
// 此时 $simulatedFiles 可替代 $_FILES 用于校验、移动等操作❌ 常见误区提醒
- $_FILES 永远为空:当 Content-Type: application/json 时,PHP 完全忽略 $_FILES —— 它只在 multipart/form-data 请求中由 CGI/SAPI 层自动填充。
- 不要尝试伪造 $_FILES 数组:直接赋值 $_FILES = [...] 不会触发 PHP 文件上传机制(如 move_uploaded_file() 的安全校验),且存在安全隐患。
- 大文件风险:Base64 编码会使体积膨胀约 33%,并增加内存占用;建议限制单文件 ≤ 5MB,并在 php.ini 中调高 post_max_size 和 memory_limit。
-
安全性必做:
- 验证 Base64 字符合法性;
- 使用 mime_content_type() 或 finfo 校验真实 MIME 类型(勿信扩展名或 type 字段);
- 保存路径须使用 sys_get_temp_dir() 或受控目录,禁止拼接用户输入。
✅ 最佳实践建议
| 场景 | 推荐方案 |
|---|---|
| 前端可控(如 Web/Postman) | ✅ 使用 multipart/form-data + FormData.append('mydata[0][upload_file]', file),服务端直接用 $_FILES['mydata'][...]['tmp_name'] |
| 必须走 JSON(如移动端 SDK 限制) | ✅ Base64 编码 + 后端解码 + 临时文件模拟 |
| 高并发/大文件上传 | ⚠️ 改用分片上传 + 预签名 URL(如对接 AWS S3/MinIO),绕过 PHP 内存瓶颈 |
通过 Base64 嵌入虽可行,但本质是“妥协方案”。优先回归 multipart/form-data 才是符合 HTTP 规范、安全高效的标准路径。











