如何在 JSON 请求体中传递文件数据以供 PHP REST 接口接收

13次阅读

如何在 JSON 请求体中传递文件数据以供 PHP REST 接口接收

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 规范、安全高效的标准路径。

text=ZqhQzanResources