文件上传是所有web应用中最常见的功能,而PHP实现这一功能也非常的简单,只需要前端设置表单的 enctype 值为 multipart/form-data 之后,我们就可以通过 $_FILES 获得表单中的 file 控件中的内容。

同时,我们还可以将 file 控件的名称写成带 [] 的数组形式,这样我们就可以接收到多个上传的文件。比如下面这个测试用的表单:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<form action="" enctype="multipart/form-data" method="post"> myfile1:<input type="file" name="myfile[]"/><br/>
myfile2:<input type="file" name="myfile[a][]"/><br/>
myfile3:<input type="file" name="myfile[a][b][]"/><br/>
myfile4:<input type="file" name="myfile[c][]"/><br/>
myfile5:<input type="file" name="myfile[]"/><br/>
myfile6:<input type="file" name="myfile[][]"/><br/>
<br/>
newfile1:<input type="file" name="newfile[][]"/><br/>
newfile2:<input type="file" name="newfile[s]"/><br/> singlefile: <input type="file" name="singlefile"/><br/>
<input type="submit" value="submit"/>
</form>
</body>
</html>

一共有9个 file 控件,其中 myfile 和 newfile 都是数组类型的表单名,而 singlefile 则是一个单独的。先简单的看一下 $_FILES 所获得的内容。


print_r($_FILES); Array
(
[myfile] => Array
(
[name] => Array
(
[0] => 2591d8b3eee018a0a84f671933ab6c74.png
[a] => Array
(
[0] => 12711584942474_.pic_hd 1.jpg
[b] => Array
(
[0] => 12721584942474_.pic_hd 1.jpg
) ) [c] => Array
(
[0] => 12731584942474_.pic_hd.jpg
) [1] => background1.jpg
[2] => Array
(
[0] => adliu_pip_data.xlsx
) ) [type] => Array
(
[0] => image/png
[a] => Array
(
[0] => image/jpeg
[b] => Array
(
[0] => image/jpeg
) ) [c] => Array
(
[0] => image/jpeg
) [1] => image/jpeg
[2] => Array
(
[0] => application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
) ) [tmp_name] => Array
(
[0] => /private/var/folders/wj/t2z1cfhs0m9gq48krm8nc0vm0000gn/T/phphD88ZY
[a] => Array
(
[0] => /private/var/folders/wj/t2z1cfhs0m9gq48krm8nc0vm0000gn/T/phpNY8MzY
[b] => Array
(
[0] => /private/var/folders/wj/t2z1cfhs0m9gq48krm8nc0vm0000gn/T/php3MX5tk
) ) [c] => Array
(
[0] => /private/var/folders/wj/t2z1cfhs0m9gq48krm8nc0vm0000gn/T/phpjgrHMj
) [1] => /private/var/folders/wj/t2z1cfhs0m9gq48krm8nc0vm0000gn/T/phppXRtnc
[2] => Array
(
[0] => /private/var/folders/wj/t2z1cfhs0m9gq48krm8nc0vm0000gn/T/phpekSY1M
) ) [error] => Array
(
[0] => 0
[a] => Array
(
[0] => 0
[b] => Array
(
[0] => 0
) ) [c] => Array
(
[0] => 0
) [1] => 0
[2] => Array
(
[0] => 0
) ) [size] => Array
(
[0] => 4973
[a] => Array
(
[0] => 3007
[b] => Array
(
[0] => 1156
) ) [c] => Array
(
[0] => 6068
) [1] => 393194
[2] => Array
(
[0] => 36714
) ) ) [newfile] => Array
(
[name] => Array
(
[0] => Array
(
[0] => 数据列表 (2).xlsx
) [s] => background1.jpg
) [type] => Array
(
[0] => Array
(
[0] => application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
) [s] => image/jpeg
) [tmp_name] => Array
(
[0] => Array
(
[0] => /private/var/folders/wj/t2z1cfhs0m9gq48krm8nc0vm0000gn/T/phplSsRfM
) [s] => /private/var/folders/wj/t2z1cfhs0m9gq48krm8nc0vm0000gn/T/phpuQAvRb
) [error] => Array
(
[0] => Array
(
[0] => 0
) [s] => 0
) [size] => Array
(
[0] => Array
(
[0] => 77032
) [s] => 393194
) ) [singlefile] => Array
(
[name] => timg (8).jpeg
[type] => image/jpeg
[tmp_name] => /private/var/folders/wj/t2z1cfhs0m9gq48krm8nc0vm0000gn/T/phpxtSQ4J
[error] => 0
[size] => 10273
) )

看出有什么问题了吗?

$_FILE['singlefile']['name'];
$_FILE['singlefile']['type'];
$_FILE['singlefile']['tmp_name'];
$_FILE['singlefile']['error'];
$_FILE['singlefile']['error']; $_FILE['myfile']['name']['a']['b'][0];
$_FILE['myfile']['type']['a']['b'][0];
$_FILE['myfile']['tmp_name']['a']['b'][0];
$_FILE['myfile']['error']['a']['b'][0];
$_FILE['myfile']['error']['a']['b'][0];

单个表单是一个 singlefile 为键名的数组,里面是对应的 name 、 type 等属性。这个非常简单也清晰明了,但是数组形式上传的内容就比较坑了,每一个属性下面都有多个值,而且这些值还有可能是嵌套的数组。就比如说我们要获得 myfile[a][b][] 的上传文件内容,我们就要通过 \$_FILE['myfile']['name']['a']['b'][0] 、 $_FILE['myfile']['type']['a']['b'][0] 这样的形式获得相关的内容。这个可真的不是很友好,那么我们今天的主题就来了,我们把这种内容进行一下格式化,让他变成和 singlefile 类似的结构,也就是一个文件的相关内容都在一个键名结构下,比如 myfile[a][b][] 的内容就全部都在 $_FILE['myfile'][a][b][0]下面。

$files = [];
// 开始数据格式化
foreach ($_FILES as $uploadKey => $uploadFiles) {
// 需要将 $_FILES 中的五个字段都拿出来
$files[$uploadKey] = formatUploadFiles($uploadFiles['name'], $uploadFiles['type'], $uploadFiles['tmp_name'], $uploadFiles['error'], $uploadFiles['size']);
} // 格式化上传文件数组
function formatUploadFiles($fileNamesArray, $type, $tmp_name, $error, $size)
{
$tmpFiles = [];
// 文件名是否是数组,如果不是数组,就是单个文件上传
if (is_array($fileNamesArray)) {
// 数组形式上传
foreach ($fileNamesArray as $idx => $fileName) {
// 如果还是嵌套的数组,递归遍历接下来的内容
if (is_array($fileName)) {
$tmpFiles[$idx] = formatUploadFiles($fileName, $type[$idx] ?? [], $tmp_name[$idx] ?? [], $error[$idx] ?? [], $size[$idx] ?? []);
} else {
// 组合多维的格式化内容
$tmpFiles[$idx] = [
'name' => $fileName,
'type' => $type[$idx] ?? '',
'tmp_name' => $tmp_name[$idx] ?? '',
'error' => $error[$idx] ?? '',
'size' => $size[$idx] ?? '',
];
}
}
} else {
// 组合单个的内容
$tmpFiles = [
'name' => $fileName,
'type' => $type ?? '',
'tmp_name' => $tmp_name ?? '',
'error' => $error ?? '',
'size' => $size ?? '',
];
} return $tmpFiles;
} print_r($files);

代码还是非常好理解的,就是通过一段递归来遍历整个 $_FILES 目录树,相当于一个深度遍历。当然,这样也会带来性能的下降,毕竟是需要进行循环+递归的遍历。不过好在大部分情况下我们上传的文件并不会那么的多。不过反过来说,如果不事先进行格式化,当你想获得所有的上传内容时,一样还是需要进行多层或者递归遍历的。

接下来我们看看格式化之后的输出:

Array
(
[myfile] => Array
(
[0] => Array
(
[name] => 2591d8b3eee018a0a84f671933ab6c74.png
[type] => image/png
[tmp_name] => /private/var/folders/wj/t2z1cfhs0m9gq48krm8nc0vm0000gn/T/phpV7A2yC
[error] => 0
[size] => 4973
) [a] => Array
(
[0] => Array
(
[name] => 12711584942474_.pic_hd 1.jpg
[type] => image/jpeg
[tmp_name] => /private/var/folders/wj/t2z1cfhs0m9gq48krm8nc0vm0000gn/T/php5q2d1Z
[error] => 0
[size] => 3007
) [b] => Array
(
[0] => Array
(
[name] => 12721584942474_.pic_hd 1.jpg
[type] => image/jpeg
[tmp_name] => /private/var/folders/wj/t2z1cfhs0m9gq48krm8nc0vm0000gn/T/phpdvv8No
[error] => 0
[size] => 1156
) ) ) [c] => Array
(
[0] => Array
(
[name] => 12731584942474_.pic_hd.jpg
[type] => image/jpeg
[tmp_name] => /private/var/folders/wj/t2z1cfhs0m9gq48krm8nc0vm0000gn/T/php9tfGmp
[error] => 0
[size] => 6068
) ) [1] => Array
(
[name] => background1.jpg
[type] => image/jpeg
[tmp_name] => /private/var/folders/wj/t2z1cfhs0m9gq48krm8nc0vm0000gn/T/phplUVpzA
[error] => 0
[size] => 393194
) [2] => Array
(
[0] => Array
(
[name] => adliu_pip_data.xlsx
[type] => application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
[tmp_name] => /private/var/folders/wj/t2z1cfhs0m9gq48krm8nc0vm0000gn/T/phpNRtiaC
[error] => 0
[size] => 36714
) ) ) [newfile] => Array
(
[0] => Array
(
[0] => Array
(
[name] => 数据列表 (2).xlsx
[type] => application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
[tmp_name] => /private/var/folders/wj/t2z1cfhs0m9gq48krm8nc0vm0000gn/T/phpBLG7aG
[error] => 0
[size] => 77032
) ) [s] => Array
(
[name] => background1.jpg
[type] => image/jpeg
[tmp_name] => /private/var/folders/wj/t2z1cfhs0m9gq48krm8nc0vm0000gn/T/phpjyqCFY
[error] => 0
[size] => 393194
) ) [singlefile] => Array
(
[name] =>
[type] => image/jpeg
[tmp_name] => /private/var/folders/wj/t2z1cfhs0m9gq48krm8nc0vm0000gn/T/phpuYJXiE
[error] => 0
[size] => 10273
) )

和上面原始的 $_FILES 相比是不是清晰明了的很多?这回我们如果需要 myfile[a][b][] 里面全部的内容时,就可以使用下面的方式方便的获取了:

$files['myfile']['a']['b'][0]['name'];
$files['myfile']['a']['b'][0]['type'];
$files['myfile']['a']['b'][0]['tmp_name'];
$files['myfile']['a']['b'][0]['error'];
$files['myfile']['a']['b'][0]['size'];

当然,这种需求在我们的日常工作中并不多见,这里也只是提供一个思路,将数据提前转化成我们需要的格式是一种非常好的习惯,能够让我们的后续操作变得非常简单。

测试代码:

https://github.com/zhangyue0503/dev-blog/blob/master/php/202004/source/PHP%E5%A4%9A%E6%96%87%E4%BB%B6%E4%B8%8A%E4%BC%A0%E6%A0%BC%E5%BC%8F%E5%8C%96.php

参考文档:

https://www.php.net/manual/zh/features.file-upload.php

PHP多文件上传格式化的更多相关文章

  1. SpringMVC(三)-- 视图和视图解析器、数据格式化标签、数据类型转换、SpringMVC处理JSON数据、文件上传

    1.视图和视图解析器 请求处理方法执行完成后,最终返回一个 ModelAndView 对象 对于那些返回 String,View 或 ModeMap 等类型的处理方法,SpringMVC 也会在内部将 ...

  2. 小兔Java教程 - 三分钟学会Java文件上传

    今天群里正好有人问起了Java文件上传的事情,本来这是Java里面的知识点,而我目前最主要的精力还是放在了JS的部分.不过反正也不麻烦,我就专门开一贴来聊聊Java文件上传的基本实现方法吧. 话不多说 ...

  3. Struts中文件上传的一些规则...

    1.action中定义规范 如果jsp中file的name="xxx",那么后台action中的属性要做相应更改为 private File xxx; private String ...

  4. Web应用安全之文件上传漏洞详解

    什么是文件上传漏洞 文件上传漏洞是在用户上传了一个可执行的脚本文件,本通过此脚本文件获得了执行服务器端命令的功能,这种攻击方式是最为直接,最为有效的,有时候,几乎没有什么门槛,也就是任何人都可以进行这 ...

  5. 使用Uploadify(UploadiFive)多文件上传控件遇到的坑

    最近项目中需要实现多文件上传功能,于是结合需求最终选择了Uploadify这一款控件来实现.相比其他控件,Uploadify具有简洁的界面,功能API基本可以解决大多数需求,又是基于jquery的,配 ...

  6. 19、文件上传与下载/JavaMail邮件开发

    回顾: 一. 监听器 生命周期监听器 ServletRequestListener HttpSessionListener ServletContextListener 属性监听器 ServletRe ...

  7. Fine Uploader文件上传组件

    最近在处理后台数据时需要实现文件上传.考虑到对浏览器适配上采用Fine Uploader. Fine Uploader 采用ajax方式实现对文件上传.同时在浏览器中直接支持文件拖拽[对浏览器版本有要 ...

  8. Web---文件上传-用apache的工具处理、打散目录、简单文件上传进度

    我们需要先准备好2个apache的类: 上一个博客文章只讲了最简单的入门,现在来开始慢慢加深. 先过渡一下:只上传一个file项 index.jsp: <h2>用apache的工具处理文件 ...

  9. PHP文件上传与安全

    文件上传的流程 上传必须由POST方式的file类型表单提交,被提交的地方 一定是一个php程序,用户在表单使用file类型的域.选在一个自己电脑上的文件,提交到php程序以后 其实就已经完成了一个上 ...

随机推荐

  1. 【笔记】逻辑回归中使用多项式(sklearn)

    在逻辑回归中使用多项式特征以及在sklearn中使用逻辑回归并添加多项式 在逻辑回归中使用多项式特征 在上面提到的直线划分中,很明显有个问题,当样本并没有很好地遵循直线划分(非线性分布)的时候,其预测 ...

  2. php本地文件包含 Writeup

    目录 本地文件包含 LFI本地文件包含案例一 LFI本地文件包含案例二 本地文件包含简介 文件包含函数加载的参数没有经过过滤或者严格的定义,可以被用户控制,包含其他恶意文件,导致了执行了非预期的代码. ...

  3. 将白码平台数据存储到MySQL数据库

    概述: 此前在白码平台上搭建并使用系统,若想要将白码平台上搭建的系统的数据存储到自己本地的MySQL数据库中的话,需要将数据导出后再对数据进行处理.如今想要实现这一需求,直接通过使用白码的数据库对接功 ...

  4. 基于typescript编写vue的ts文件语法模板

    1 <template> 2 <div> 3 <input v-model="msg"> 4 <p>prop: {{ propMes ...

  5. 输入URL后浏览器的过程

    In this article, I want my readers to get a picture of a very basic concept of the web world. Previo ...

  6. jekins

    上面是:maven配置 git安装: 容器安装: 容器配置与发布: 添加安全认证(如果tomcat没有设置密码这里也不需要设置:) 访问尝试:本地测试前置practice_war的影响 Jekins实 ...

  7. 面试官问我MySQL索引,我

    面试官:我看你简历上写了MySQL,对MySQL InnoDB引擎的索引了解吗? 候选者:嗯啊,使用索引可以加快查询速度,其实上就是将无序的数据变成有序(有序就能加快检索速度) 候选者:在InnoDB ...

  8. css写法

    id选择器 > 类选择器 > 标签选择器 @charset "utf-8"; charset=utf-8   表示当前文档的字符集是采用utf-8的字符,也就是我们常说 ...

  9. SpringBoot快速集成SpringBootAdmin管控台监控服务

    SpringBootAdmin是一个针对 Spring Boot 的 Actuator 接口进行 UI 美化封装的监控工具,它可以在列表中浏览所有被监控 spring-boot 项目的基本信息.详细的 ...

  10. Selenium自动化实现web自动化-1

    框架搭建 基于maven+jdk8+junit5+seleium 构建 <dependencies> <dependency> <groupId>org.junit ...