
本文详解在 Laravel 文件上传场景中,如何精准替换文件名中的阿拉伯数字(٠١٢٣٤٥٦٧٨٩)为标准 ASCII 数字(0-1-2-3-4-5-6-7-8-9),避免误改文件对象本身或生成临时文件名(如 phpF77E.tmp)。
本文详解在 laravel 文件上传场景中,如何精准替换文件名中的阿拉伯数字(٠١٢٣٤٥٦٧٨٩)为标准 ascii 数字(0-1-2-3-4-5-6-7-8-9),避免误改文件对象本身或生成临时文件名(如 `phpf77e.tmp`)。
在 Laravel 中处理用户上传的文件时,若原始文件名包含阿拉伯语数字(即东阿拉伯数字,如 ١٢٣.pdf),直接使用 str_replace() 或 preg_replace() 作用于 $file 对象(而非其名称)会导致逻辑错误——因为 $file 是一个 UploadedFile 实例,不是字符串。你调用 $this->convert($file) 实际上是在对对象调用正则替换,PHP 会尝试将其转为字符串(结果类似 phpXXXXXX.tmp),从而造成文件名被意外覆盖。
✅ 正确做法是:仅对文件原始名称(getClientOriginalName())进行数字替换,再通过 storeAs() 显式指定新文件名保存。
以下是推荐的完整实现方案:
✅ 修正后的 storeCv 方法(关键修改已加注释)
public function storeCv(Request $request, $trainee_id)
{
$request->validate([
'cv' => 'required_without:file',
'file' => 'required_without:cv',
]);
$trainee = Trainee::findOrFail($trainee_id);
$file = $request->file('cv') ?: $request->file('file');
// ✅ 关键修复:只对原始文件名做转换,不传入 UploadedFile 对象
$originalName = $file->getClientOriginalName();
$safeFilename = $this->convert($originalName); // 转换阿拉伯数字
// ✅ 使用 storeAs() 将文件以转换后的名称存入磁盘(非 media library 直接调用)
$storedPath = $file->storeAs('', $safeFilename, [
'disk' => 'uploads_cv' // 对应 config/filesystems.php 中定义的磁盘
]);
// ✅ 然后将存储路径(或 basename)传给 uploadToFolder,确保媒体库使用正确文件名
$uploaded_file = $trainee->uploadToFolder(
storage_path('app/' . $storedPath), // 本地路径供 addMedia() 读取
'cv'
);
if ($trainee->cv_url) {
$trainee->status = Trainee::STATUS_PENDING_APPROVAL;
$trainee->save();
}
return $uploaded_file;
}
✅ 改进版 convert() 方法(更健壮、支持 Unicode 安全匹配)
原 preg_replace() 写法存在隐患:$arabic_eastern 是数组,但 preg_replace() 第一个参数需为模式数组(带分隔符),直接传入字符数组会触发警告或静默失败。应改用 str_replace()(简单高效)或 preg_replace() 的正确正则写法:
public function convert(string $str): string
{
$arabic_eastern = ['٠', '١', '٢', '٣', '٤', '٥', '٦', '٧', '٨', '٩'];
$western_digits = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'];
// ✅ 推荐:使用 str_replace —— 简单、高效、无正则开销
return str_replace($arabic_eastern, $western_digits, $str);
// ⚠️ 若坚持用 preg_replace,需构造 Unicode 模式:
// return preg_replace('/[\x{0660}-\x{0669}]/u', function ($m) use ($western_digits) {
// return $western_digits[ord($m[0]) - 0x0660];
// }, $str);
}
✅ 配置自定义磁盘(config/filesystems.php)
确保 storeAs() 使用的磁盘已正确定义,例如:
'disks' => [
'uploads_cv' => [
'driver' => 'local',
'root' => public_path('uploads/cv'),
'url' => env('APP_URL') . '/uploads/cv',
'visibility' => 'public',
],
// ... 其他磁盘
],
⚠️ 注意事项
-
永远不要对
UploadedFile对象本身调用字符串函数:它不是一个字符串,强制转换会丢失原始信息并产生临时文件名。 -
验证文件有效性:上传前建议检查
if (!$file->isValid()) { /* handle error */ }。 -
文件名安全性:
convert()仅处理数字;如需进一步净化(如移除控制字符、限制长度、过滤路径遍历),应叠加basename()、preg_replace('/[^a-zA-Z0-9._-]/', '_', $name)等防护。 -
媒体库兼容性:Spatie Media Library 的
addMedia($path)接收的是本地文件系统路径,不是上传对象,因此必须先storeAs()得到真实路径再传入。
通过以上调整,即可在保留原有业务逻辑的同时,精准、安全地实现阿拉伯数字文件名的标准化转换,彻底规避 phpXXXXXX.tmp 类错误。










