php的文件上传入门教程(实例讲解)


Posted in Javascript onApril 10, 2014

一、文件上传

为了让客户端的用户能够上传文件,我们必须在用户界面中提供一个表单用于提交上传文件的请求。由于上传的文件是一种特殊数据,不同于其它的post数据,所以我们必须给表单设置一个特殊的编码:

<form action="upload.php" method="POST" enctype="multipart/form-data"></form>

以上的enctype属性,你可能不太熟悉,因为这常常会被忽略掉。但是,如果http post请求中既有常规数据,又包含文件类数据的话,这个属性就应该显示加上,这样可以提高针对各种浏览器的兼容性。

接下来,我们得向表单中添加一个用于上传文件的字段:

<input type="file" name="attachment">

上述文件字段在各种浏览器中可能表现会有所不同。对于大多数的浏览器,上述字段都会被渲染成一个文本框加上一个浏览按钮。这样,用户既可以自行输入文件的路径到文本框中,也可以通过浏览按钮从本地硬盘上选择所要上传的文件。但是,在苹果的Safari中,貌似只能使用浏览这种方式。当然,你也可以自定义这个上传框的样式,使它看起来比默认的样式优雅些。

下面,为了更好的阐述怎么样处理文件上传,举一个完整的例子。比如,以下一个表单允许用户向我的本地服务器上上传附件:

<p>请上传你的附件:</p>
<form action="upload.php" method="POST" enctype="multipart/form-data"> 
<input type="file" name="attachment"> 
<input type="submit" value="上传附件"> 
</form>

提示:可以通过php.ini中的upload_max_filesize来设置允许上传文件的最大值。另外,还有一个post_max_size也可以用来设置允许上传的最大表单数据,具体意思就是表单中各种数据之和,所以你也可以通过设置这个字段来控制上传文件的最大值。但是,注意后者的值必须大于前者,因为前者属于后者的一部分表单数据。

 

图1. 显示在在firefox中的上传表单

当这个表单提交的时候,http请求会被发送到upload.php。为了显示具体哪些信息可以在upload.php中使用,我在upload.php将其打印出来:

header('Content-Type: text/plain');
print_r($_FILES);

下面来做个试验,假如我通过以上表单上传一个本博客的logo到我的本地服务器www.360weboy.me/upload.php,看看在upload.php中会输出什么信息:

Array
        (
            [attachment] => Array
                (
                    [name] => boy.jpg
                    [type] => image/jpeg
                    [tmp_name] => D:\xampp\tmp\php1168.tmp
                    [error] => 0
                    [size] => 11490
                )        )

以上就是文件上传后,在全局数组中的关于当前上传文件的所有信息。但是,我们是否能够保证这些信息是安全的,假如name或者其它信息被篡改过了呢?我们时刻需要对来自客户端的信息保持警惕!

具体的http请求的各个部分
为了更好的理解文件上传,我们必须核对下客户端发送的http请求中到底包含了那些具体的信息。先前我上传的附件是本博客的logo,因为是图片,不太适合我们做以上实验。所以,我重新上传一个test.text文本文件,其中具体包含了以下内容:

    360w
    360days
    Life Of A Web Boy

Okay。现在我上传这个文本文件,在upload.php中会输出:
        Array
        (
            [attachment] => Array
                (
                    [name] => test.txt
                    [type] => text/plain
                    [tmp_name] => D:\xampp\tmp\php51C0.tmp
                    [error] => 0
                    [size] => 40
                )
        )
 
  
我们再来看下相关的浏览器发送的http post请求(一些可选的头部我省略了):
        POST /upload.php HTTP/1.1
        Host: www.360weboy.me
        Referer: http://www.360weboy.me/
        multipart/form-data; boundary=---------------------------24464570528145
        Content-Length: 234
        -----------------------------24464570528145 
        Content-Disposition: form-data; name="attachment"; filename="test.txt" 
        Content-Type: text/plain 
        360weboy 
        360days 
        Life Of A Web Boy 
        -----------------------------24464570528145--
   
从上面的请求格式中有几个字段我们要关注下的,分别是name, filename以及Content-Type.它们分别表示上传文件框在form表单中的字段名-attachment,用户从本地硬盘中上传的文件名 ? test.txt,以及上传的文件格式 ? text/plain(代表文本文件)。然后,我们看到一行空行下面的,就是这个上传文件中的具体内容。

二、安全性的加强
为了加强文件上传中的安全性,我们需要检查下$_FILES全局数组中的tmp_name和size。为了确保tmp_name指向的文件确实是刚刚用户在客户端上传的文件,而不是指向的类似/etc/passwd,可以使用php中的函数is_uploaded_file()来进行下判断:

        $filename = $_FILES['attachment']['tmp_name'];
        if (is_uploaded_file($filename)) { 
            /* 是一个上传的文件. */
        }
 
  
某些情况下,用户上传文件后,可能会将上传成功的文件的内容显示给用户看下,那么上述代码的检查尤其重要。

另外一个需要检查的就是上传文件的mime-type, 也就是上述upload.php中输出数组的type字段。 我在第一个例子中上传的是一个图片,所以$_FILES['attachment']['type']的值为'image/jpeg'。 如果打算在服务器端只接受image/png, image/jpeg, image/gif, image/x-png 以及 image/p-jpeg这些mime-type的图片,可以用类似下面的代码了进行检查(只是举个例子,具体代码,比如报错等,应该遵循你的系统中的机制):

    
        $allow_mimes = array(
            'image/png',
            'image/x-png',
            'image/gif',
            'image/jpeg',
            'image/pjpeg'
        );        $image = $_FILES['attachment'];
        if(!in_array($image['type'], $allow_mimes)) {
            die('对不起, 你上传的文件格式不准确;我们只接受图片文件.');
        }
        // 继续处理上传的图片文件
   
正如你看到的,我们已经保准了文件的mime-type是符合服务器端的要求的。但是,这样是不是就可以防止恶意用户上传其它有害文件,还是不够的,因为这个mime-type恶意用户是可以伪装的。 比如用户做了一张jpg图片,在图片的元数据中写入了一些恶意的php代码,然后保存为后缀名为php的文件。当这个恶意文件上传的时候,将顺利通过服务器端对于mime-type的检查,被认为是一张图片,里面的危险的php代码将会被执行。具体的图片的元数据类似如下:
        File name    : image.jpg
        File size    : 182007 bytes
        File date    : 2012:11:27 7:45:10
        Resolution   : 1197 x 478
        Comment      : passthru($_POST['cmd']); __halt_compiler();
 
  
我们可以看到,在图片元数据的Comment字段中加入了php代码。所以,很显然,为了防止类似危险情况发生,还必须对上传文件的扩展名进行一次必要的检查。下面的代码对前面的检查Mime-type的代码进行了加强:
        $allow_mimes = array(
            'image/png' => '.png',
            'image/x-png' => '.png',
            'image/gif' => '.gif',
            'image/jpeg' => '.jpg',
            'image/pjpeg' => '.jpg'
        );
        $image = $_FILES['attachment'];
        if(!array_key_exists($image['type'], $allow_mimes )) {
            die('对不起, 你上传的文件格式不准确;我们只接受图片文件.');
        }
        // 获取略去后缀名的文件名:
        $filename = substr($image['name'], 0, strrpos($image['name'], '.'));
        // 添加后缀名
        $filename .= $allow_mimes[$image['type']];
        // 继续处理上传的文件
   
通过上述的代码,我们确保即使上传的图片的元文件中包含了php代码的话,图片文件会被重名为后缀名为图片格式的文件,所以其中的php代码也不会被执行了。上述代码对正常的上传的图片也不会有任何负面影响。

进行了上述的几步提高安全性的检查步骤后,如果你只是要把上传的文件保存到一个指定的目录中,那么就可以使用php的默认函数move_uploaded_file来实现了:

        $tmp_filename = $_FILES['attachment']['tmp_name'];
        $filename = '/path/to/attachment.txt';
        if (move_uploaded_file(tmp_filename, $filename)) { 
            /* $temp_filename 保存在临时目录中的上传文件, 然后成功将其保存到对应目录下的attachment.txt文件中. */
        }
  
你也许还要对上传文件的大小进行限制,那么你可以通过filesize函数来获取上传文件的大小,进行判断后做进一步处理,这具体就不在这将了,自己去折腾吧。

好了,关于文件上传暂时就写到这里吧。希望这篇入门篇文章对你有所帮助。

Javascript 相关文章推荐
JS 页面内容搜索,类似于 Ctrl+F功能的实现代码
Aug 13 Javascript
使用jQuery解决IE与FireFox下createElement方法的差异
Nov 14 Javascript
jquery实现的随机多彩tag标签随机颜色和字号大小效果
Mar 27 Javascript
a标签的href与onclick事件的区别详解
Nov 12 Javascript
jquery实现公告翻滚效果
Feb 27 Javascript
ajax如何实现页面局部跳转与结果返回
Aug 24 Javascript
大型JavaScript应用程序架构设计模式
Jun 29 Javascript
jquery对象与DOM对象转化
Feb 08 Javascript
ES5学习教程之Array对象
Apr 01 Javascript
微信小程序实现发红包功能
Jul 11 Javascript
利用Promise自定义一个GET请求的函数示例代码
Mar 20 Javascript
Vue.js路由实现选项卡简单实例
Jul 24 Javascript
JS使用replace()方法和正则表达式进行字符串的搜索与替换实例
Apr 10 #Javascript
javascript的propertyIsEnumerable()方法使用介绍
Apr 09 #Javascript
常见的原始JS选择器使用方法总结
Apr 09 #Javascript
jquery查找父元素、子元素(个人经验总结)
Apr 09 #Javascript
js控制iframe的高度/宽度让其自适应内容
Apr 09 #Javascript
JS、DOM和JQuery之间的关系示例分析
Apr 09 #Javascript
jQuery遍历Table应用示例
Apr 09 #Javascript
You might like
php快速url重写 更新版[需php 5.30以上]
2010/04/20 PHP
php 获取本机外网/公网IP的代码
2010/05/09 PHP
PHP压缩html网页代码(清除空格,换行符,制表符,注释标记)
2012/04/02 PHP
php需登录的文件上传管理系统
2020/03/21 PHP
PHP连接SQL server数据库测试脚本运行实例
2020/08/24 PHP
20个非常有用的PHP类库 加速php开发
2010/01/15 Javascript
一个报数游戏js版(约瑟夫环问题)
2010/08/05 Javascript
自定义右键属性覆盖浏览器默认右键行为实现代码
2013/02/02 Javascript
jquery中animate动画积累的解决方法
2013/10/05 Javascript
JS+CSS实现精美的二级导航效果代码
2015/09/17 Javascript
Node.js重新刷新session过期时间的方法
2016/02/04 Javascript
JS模拟bootstrap下拉菜单效果实例
2016/06/17 Javascript
js中动态创建json,动态为json添加属性、属性值的实例
2016/12/02 Javascript
jQuery获取所有父级元素及同级元素及子元素的方法(推荐)
2018/01/21 jQuery
django使用channels2.x实现实时通讯
2018/11/28 Javascript
Vue Echarts实现可视化世界地图代码实例
2019/05/07 Javascript
Vue 实现CLI 3.0 + momentjs + lodash打包时优化
2019/11/13 Javascript
微信小程序利用button控制条件标签的变量问题
2020/03/15 Javascript
Javascript数组及类数组相关原理详解
2020/10/29 Javascript
vue中的计算属性和侦听属性
2020/11/06 Javascript
使用Mixin设计模式进行Python编程的方法讲解
2016/06/21 Python
python类的继承实例详解
2017/03/30 Python
Django中的CBV和FBV示例介绍
2018/02/25 Python
python 读取.csv文件数据到数组(矩阵)的实例讲解
2018/06/14 Python
Django自带用户认证系统使用方法解析
2020/11/12 Python
pymysql模块使用简介与示例
2020/11/17 Python
Skechers越南官方网站:来自美国的运动休闲品牌
2021/02/22 全球购物
专业实习自我鉴定
2013/10/29 职场文书
新闻专业毕业生英文求职信
2014/03/19 职场文书
护士自我鉴定总结
2014/03/24 职场文书
《草原的早晨》教学反思
2014/04/08 职场文书
保密工作承诺书
2014/08/29 职场文书
2015年第十五个全民国防教育日宣传活动方案
2015/05/06 职场文书
python numpy中multiply与*及matul 的区别说明
2021/05/26 Python
解决sql server 数据库,sa用户被锁定的问题
2021/06/11 SQL Server
详解MySql中InnoDB存储引擎中的各种锁
2022/02/12 MySQL