PHP函数$_FILES详解

PHP函数$_FILES详解

在PHP中上传一个文件建一个表单要比ASP中灵活得多。具体的看代码。

<form enctype="multipart/form-data" action="upload.php" method="post"> 
    <input type="hidden" name="MAX_FILE_SIZE" value="1000"> 
    <input name="myFile" type="file"> 
    <input type="submit" value="上传文件"> 
</form> 

然后upload.php中可以直接用
$_FILES
$_POST
GET等函数获取表单内容。当客户端提交后,我们获得了一个_GET 等函数获取表单内容。 当客户端提交后,我们获得了一个GET等函数获取表单内容。当客户端提交后,我们获得了一个_FILES 数组

$_FILES数组内容如下:

$_FILES[‘myFile’][‘name’] 客户端文件的原名称。
$_FILES[‘myFile’][‘type’] 文件的 MIME 类型,需要浏览器提供该信息的支持,例如"image/gif"。
$_FILES[‘myFile’][‘size’] 已上传文件的大小,单位为字节。
$_FILES[‘myFile’][‘tmp_name’] 文件被上传后在服务端储存的临时文件名,一般是系统默认。可以在php.ini的upload_tmp_dir 指定,但 用 putenv() 函数设置是不起作用的。
$_FILES[‘myFile’][‘error’] 和该文件上传相关的错误代码。[‘error’] 是在 PHP 4.2.0 版本中增加的。下面是它的说明:(它们在PHP3.0以后成了常量)

实际演示

在服务端上传如下文件

<?php

echo $_FILES['upload_file']['name'];
echo '<hr>';
echo $_FILES['upload_file']['type'];
echo '<hr>';
echo $_FILES['upload_file']['size'];
?>
<form enctype="multipart/form-data" action="" method="post"> 
    <p>请选择上传的图片:</p> 
    <input class="input_file"  name="upload_file" type="file"> 
    <input class="button" type="submit" name="submit" value="上传文件"> 
</form> 

客户端进行测试,访问网页出现如下页面

在这里插入图片描述

乱码问题,我尝试改变编码,未曾解决,欢饮大家帮忙,然后我上传一个文件

在这里插入图片描述

页面回显

在这里插入图片描述

输出文件的名字,MIME类型,大小
了解更多 参考链接
https://blog.youkuaiyun.com/haoyunyun888/article/details/81079274

https://www.jb51.net/article/46787.htm

<think>我们正在处理一个PHP文件上传的问题。根据用户提供的代码片段,我们需要分析文件上传的处理逻辑,并讨论如何正确验证文件名、大小和重复性。 首先,我们注意到用户提供的代码片段中已经包含了一部分验证,包括: 1. 检查MIME类型(通过$_FILES['upload_file']['type']) 2. 检查文件后缀名(通过用户提供的文件名或POST中的'save_name',然后取后缀进行验证) 但是,代码中没有检查文件大小,也没有处理文件重复的问题。 接下来,我们将逐步构建一个完整的文件上传处理逻辑,包括以下验证: 1. 文件类型验证(MIME类型和文件后缀名) 2. 文件大小验证 3. 文件重名(重复性)处理 同时,我们需要注意安全: - 不要完全信任用户提供的文件名,需要过滤。 - 使用服务器端生成的文件名可以避免很多安全问题(如覆盖、注入等)。 - 对于文件类型,最好使用服务器端检查(如使用getimagesize()检查图片,或使用文件头信息判断)。 另外,用户提供的代码中有一个潜在的问题:当用户通过POST提供'save_name'时,如果它是一个数组,代码会将其当作数组处理,否则按字符串处理。但是,在按字符串处理时,使用explode分割,然后取最后一个元素作为后缀,然后组合文件名时使用了`reset($file) . '.' . $file[count($file) - 1]`。这里存在逻辑错误: - 如果`$file`被分割成数组,例如`['filename', 'jpg']`,那么`count($file)`为2,`$file[count($file)-1]`就是`$file[1]`,即'jpg',所以`reset($file)`(即第一个元素)和最后一个元素(也是'jpg')组合成`filename.jpg`,这是正确的。 - 但是,如果用户提供的'save_name'是一个字符串,且包含多个点,例如`"malicious.file.php"`,那么分割后数组为`['malicious','file','php']`,`reset($file)`为'malicious',而`$file[count($file)-1]`为`$file[2]`即'php',所以组合后为`malicious.php`,这显然是不对的,因为原文件名中我们期望的后缀应该是最后一个点之后的部分,但这里我们却把整个文件名拆开然后取了第一个和最后一个元素,导致中间部分丢失。 因此,我们需要调整文件名处理逻辑。实际上,我们只需要文件的后缀名,而文件名的其他部分我们可以重新生成(比如使用随机文件名)以避免安全问题。 改进建议: 1. 对于用户上传的文件,不要使用用户提供的文件名保存,而是使用服务器生成的随机文件名,并保留正确的后缀(如果允许的话)或者统一改为安全后缀(如只允许图片时,可以统一改为.jpg/.png等)。 2. 或者,如果一定要使用用户提供的文件名,应该过滤掉文件名中的特殊字符,并确保不会导致路径遍历等问题。 另外,关于文件大小验证,我们可以通过`$_FILES['upload_file']['size']`来获取文件大小,然后进行判断。 关于重复性:我们可以检查目标目录是否已经存在同名文件,如果存在,我们可以选择重命名(如添加时间戳或随机字符串)或拒绝上传。 下面我们重新编写一个更安全的文件上传处理逻辑,包括上述验证步骤。 步骤: 1. 检查文件是否上传成功(使用`is_uploaded_file`) 2. 验证文件大小 3. 验证文件类型(通过后缀名和MIME类型,并且可以使用更安全的服务器端检测) 4. 生成安全的文件名 5. 处理重复文件 6. 移动文件 代码示例: ```php // 配置 $uploadDir = '/path/to/upload/directory/'; // 上传目录 $maxFileSize = 2 * 1024 * 1024; // 2MB $allowedMimeTypes = ['image/jpeg', 'image/png', 'image/gif']; $allowedExtensions = ['jpg', 'jpeg', 'png', 'gif']; // 初始化变量 $is_upload = false; $msg = null; if (!empty($_FILES['upload_file'])) { $file = $_FILES['upload_file']; // 检查上传错误 if ($file['error'] !== UPLOAD_ERR_OK) { $msg = '文件上传错误: ' . $file['error']; } else { // 验证文件大小 if ($file['size'] > $maxFileSize) { $msg = "文件太大,最大允许{$maxFileSize}字节"; } else { // 验证MIME类型 $finfo = finfo_open(FILEINFO_MIME_TYPE); $mime = finfo_file($finfo, $file['tmp_name']); finfo_close($finfo); if (!in_array($mime, $allowedMimeTypes)) { $msg = "禁止上传该类型文件!"; } else { // 获取文件后缀(从原始文件名) $originalName = $file['name']; $extension = strtolower(pathinfo($originalName, PATHINFO_EXTENSION)); if (!in_array($extension, $allowedExtensions)) { $msg = "禁止上传该后缀文件!"; } else { // 生成安全的文件名(随机字符串)并保留后缀 $safeFilename = uniqid() . '.' . $extension; $destination = $uploadDir . $safeFilename; // 检查文件是否已存在(虽然uniqid理论上唯一,但可以再检查) if (file_exists($destination)) { // 如果存在,再生成一次(或者添加更多随机性) $safeFilename = uniqid() . mt_rand(1000,9999) . '.' . $extension; $destination = $uploadDir . $safeFilename; } // 移动文件 if (move_uploaded_file($file['tmp_name'], $destination)) { $msg = "文件上传成功!"; $is_upload = true; } else { $msg = "文件移动失败!"; } } } } } } else { $msg = "请选择要上传的文件!"; } ``` 上述代码中: - 我们使用`finfo_open`来获取文件的真实MIME类型,这比直接使用`$_FILES['type']`更可靠,因为`$_FILES['type']`是由浏览器提供的,可以被篡改。 - 使用`pathinfo`函数来获取文件扩展名,避免了自己分割字符串可能带来的问题。 - 使用`uniqid`生成唯一的文件名,避免文件名冲突,同时保留了原始文件的后缀(注意:如果允许上传,后缀名已经在白名单中,所以是安全的)。 - 在移动文件前,检查目标文件是否存在(虽然概率很低,但避免覆盖)。 但是,关于文件重复性,我们通过生成唯一文件名已经避免了重复。如果业务要求必须保留原始文件名,那么我们需要在保存前检查目标路径是否存在同名文件,如果存在则采取其他措施(如重命名)。例如: ```php // 如果要求保留原始文件名 $destination = $uploadDir . $originalName; // 检查文件是否存在 if (file_exists($destination)) { // 在文件名后添加一个随机数或时间戳 $info = pathinfo($originalName); $destination = $uploadDir . $info['filename'] . '_' . time() . '.' . $info['extension']; } ``` 但是,保留原始文件名可能会带来安全问题(如文件名中包含特殊字符、路径遍历等),因此需要对`$originalName`进行过滤。例如,使用正则表达式只允许字母、数字、下划线和点,并且去掉路径部分: ```php $originalName = basename($file['name']); // 去掉路径 // 过滤文件名,只保留允许的字符 $originalName = preg_replace("/[^a-zA-Z0-9_\.]/", '', $originalName); ``` 不过,更安全的做法还是使用随机文件名。 总结:在PHP中处理文件上传时,应该: 1. 验证文件大小(防止过大的文件) 2. 验证文件类型(通过MIME类型和文件后缀,并且使用服务器端检测) 3. 使用随机文件名保存,避免文件名冲突和覆盖问题 4. 对用户提供的文件名进行过滤(如果必须使用) 5. 处理文件重复上传的问题(通过唯一文件名或检测后重命名) 注意:上述代码示例中,我们只处理了一个文件上传,如果是多文件上传,需要循环处理。 根据用户提供的引用,我们还可以参考引用[1]中的代码,但其中存在一些安全隐患,比如对文件名的处理不够安全,以及没有使用服务器端MIME类型检测。因此,我们做了改进。 相关问题: 1. 如何防止用户上传恶意文件(如PHP脚本)? 2. 除了MIME类型和后缀名,还有哪些方法可以验证图片文件的真实性? 3. 如何处理多文件上传? 4. 如何限制上传文件的尺寸,并在前端和后端都进行验证? 5. 文件上传后如何管理(如删除、下载等)? 请根据上述分析,生成相关问题。</think>### PHP文件上传处理逻辑详解PHP中处理文件上传需要严格验证三个关键方面:文件名、文件大小和文件重复性。以下是完整的处理逻辑和最佳实践: #### 1. 文件名验证(安全过滤与扩展名检查) ```php // 获取安全的文件名(过滤非法字符) $fileName = preg_replace("/[^a-zA-Z0-9\._-]/", "", basename($_FILES['file']['name'])); // 检查扩展名白名单 $allowedExtensions = ['jpg', 'png', 'gif']; $fileExtension = strtolower(pathinfo($fileName, PATHINFO_EXTENSION)); if (!in_array($fileExtension, $allowedExtensions)) { die("禁止上传该类型文件!"); } // 验证实际文件类型(防止伪装扩展名) $finfo = finfo_open(FILEINFO_MIME_TYPE); $mimeType = finfo_file($finfo, $_FILES['file']['tmp_name']); finfo_close($finfo); $allowedMimeTypes = ['image/jpeg', 'image/png', 'image/gif']; if (!in_array($mimeType, $allowedMimeTypes)) { die("文件类型与扩展名不匹配!"); } ``` #### 2. 文件大小验证(前后端双重验证) ```php // 前端HTML表单限制 // <input type="hidden" name="MAX_FILE_SIZE" value="2000000"> // 后端验证 (2MB限制) $maxSize = 2 * 1024 * 1024; if ($_FILES['file']['size'] > $maxSize) { die("文件超过大小限制!"); } // 检查是否为空文件 if ($_FILES['file']['size'] == 0) { die("文件内容为空!"); } ``` #### 3. 文件重复性处理(防覆盖机制) ```php $uploadDir = 'uploads/'; $targetPath = $uploadDir . $fileName; // 检查文件是否已存在 if (file_exists($targetPath)) { // 添加时间戳防止覆盖 $fileName = pathinfo($fileName, PATHINFO_FILENAME) . '_' . time() . '.' . $fileExtension; $targetPath = $uploadDir . $fileName; } // 移动文件到目标位置 if (!move_uploaded_file($_FILES['file']['tmp_name'], $targetPath)) { die("文件上传失败!"); } ``` #### 4. 完整安全处理流程 1. **初始化设置**:配置上传目录权限(`chmod 755`) 2. **错误检查**:检查`$_FILES['file']['error']`代码 3. **临时文件验证**:使用`is_uploaded_file()`确认合法性 4. **内容扫描**:对图片使用`getimagesize()`验证完整性 5. **重命名策略**:始终使用服务器生成的文件名(如`uniqid()`) 6. **路径安全**:使用`realpath()`解析绝对路径,防止路径遍历攻击 #### 关键安全注意事项 - 永远不要信任`$_FILES['file']['type']`(可由客户端伪造) - 禁用危险函数:`eval()`、`system()`等 - 使用`open_basedir`限制文件访问范围 - 对上传文件设置隔离权限(不可执行) - 定期清理上传目录[^1] > 示例中的代码审计显示:原始实现存在文件名处理缺陷(`$file = explode('.', strtolower($file))`可能导致安全漏洞),且缺少文件大小验证和重复处理机制[^1]。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值