必须先剥离Data URI前缀再base64_decode,推荐正则提取并用finfo_buffer校验真实MIME类型,结合白名单映射扩展名,路径需basename净化且使用绝对路径,确保目录不可执行。

如何从Data URI中提取Base64内容并还原为文件
直接用 base64_decode() 解码前,必须先剥离Data URI前缀(如 data:image/png;base64,),否则解码会失败或产生乱码。PHP不会自动识别和跳过这个头部,得手动截取。
常见错误现象:base64_decode() 返回 false,或解码后文件无法打开、报“文件已损坏”。根本原因几乎都是传入了带前缀的完整字符串。
- 用
strpos()找到第一个,的位置,再用substr()截取逗号之后的内容 - 更稳妥的做法是用正则匹配:
preg_match('/^data:(?<type>[^;]+);base64,(?<data>.+)$/i', $uri, $matches)</data></type>,这样还能顺便拿到MIME类型 - 注意:有些Data URI可能含URL编码(如空格变
%20),但标准Base64 Data URI不编码,遇到时需先urldecode()
保存为指定格式文件时如何校验和控制扩展名
仅靠Data URI里的MIME类型(如 image/jpeg)决定扩展名不可靠——它可能被伪造,也可能缺失(比如写成 data:base64,...)。实际保存前应双重验证:先检查解码后二进制是否符合该格式特征(magic bytes),再映射可信扩展名。
- 推荐用
finfo_open(FILEINFO_MIME_TYPE)检查解码后的原始字节流,得到真实MIME类型 - 建立白名单映射,例如
['image/jpeg' => 'jpg', 'image/png' => 'png', 'application/pdf' => 'pdf'],避免把恶意上传的.php冒充成图片 - 不要直接拼接用户提供的扩展名;若URI中无MIME,可设默认值(如
bin),或拒绝保存
写入文件时要注意的权限与路径安全问题
保存路径若拼接了用户输入(比如从URI里解析出文件名),极易引发路径遍历漏洞(如 ../../etc/passwd)。PHP的 file_put_contents() 不做路径净化,必须主动防御。
立即学习“PHP免费学习笔记(深入)”;
- 用
basename()强制提取最后一级文件名,丢弃所有路径信息 - 生成保存路径时,用绝对路径常量(如
__DIR__ . '/uploads/')开头,禁止拼接用户可控的完整路径 - 确保上传目录无执行权限(Web服务器不应解析该目录下的PHP文件),Linux下建议
chmod 755目录 +644文件 - 写入前用
is_dir()和is_writable()显式检查目标目录状态,避免静默失败
完整示例:安全保存Data URI为图片文件
$uri = 'data:image/png;base64,iVBORw0KGgo...';
if (!preg_match('/^data:(?<mime>[^;]+);base64,(?<data>.+)$/i', $uri, $matches)) {
throw new InvalidArgumentException('Invalid data URI format');
}
$decoded = base64_decode($matches['data']);
if ($decoded === false) {
throw new InvalidArgumentException('Base64 decode failed');
}
$finfo = finfo_open(FILEINFO_MIME_TYPE);
$mimeType = finfo_buffer($finfo, $decoded);
finfo_close($finfo);
$allowedTypes = ['image/jpeg' => 'jpg', 'image/png' => 'png', 'image/gif' => 'gif'];
if (!isset($allowedTypes[$mimeType])) {
throw new InvalidArgumentException("Unsupported MIME type: {$mimeType}");
}
$ext = $allowedTypes[$mimeType];
$filename = 'upload_' . uniqid() . '.' . $ext;
$filepath = __DIR__ . '/uploads/' . basename($filename);
if (!is_dir(__DIR__ . '/uploads')) {
mkdir(__DIR__ . '/uploads', 0755, true);
}
if (file_put_contents($filepath, $decoded) === false) {
throw new RuntimeException('Failed to write file');
}
真正容易被忽略的是:finfo_buffer() 必须作用于原始二进制数据,而不是解码前的字符串;且 base64_decode() 失败时返回 false 而非空字符串,用 === 判断才可靠。



















