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 相关文章推荐
javascript addBookmark 加入收藏 多浏览器兼容
Aug 15 Javascript
jquery mobile动态添加元素之后不能正确渲染解决方法说明
Mar 05 Javascript
angularjs基础教程
Dec 25 Javascript
javascript倒计时效果实现
Nov 12 Javascript
分享两款带遮罩的jQuery弹出框
Dec 30 Javascript
getElementById().innerHTML与getElementById().value的区别
Oct 27 Javascript
JS中解决谷歌浏览器记住密码输入框颜色改变功能
Feb 13 Javascript
JS运动特效之同时运动实现方法分析
Jan 24 Javascript
layer.open提交子页面的form和layedit文本编辑内容的方法
Sep 27 Javascript
关于Vue中axios的封装实例详解
Oct 20 Javascript
微信小程序如何实现点击图片放大功能
Jan 21 Javascript
创建与框架无关的JavaScript插件
Dec 01 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
我的论坛源代码(六)
2006/10/09 PHP
javascript 常用关键字列表集合
2007/12/04 Javascript
使用jQuery简化Ajax开发 Ajax开发入门
2009/10/14 Javascript
JavaScript Event学习第五章 高级事件注册模型
2010/02/07 Javascript
基于Jquery的仿Windows Aero弹出窗(漂亮的关闭按钮)
2010/09/28 Javascript
如何让easyui gridview 宽度自适应窗口改变及fitColumns应用
2013/01/25 Javascript
获取元素距离浏览器周边的位置的方法getBoundingClientRect
2013/04/17 Javascript
javascript的渐进增强与平稳退化浅谈
2013/11/12 Javascript
JavaScript 模块的循环加载实现方法
2015/12/13 Javascript
jQuery动态添加可拖动元素完整实例(附demo源码下载)
2016/06/21 Javascript
微信小程序 wx.request(object) API详解及实例代码
2016/09/30 Javascript
js+css3制作时钟特效
2016/10/16 Javascript
详解微信第三方小程序代开发
2017/06/23 Javascript
bootstrap响应式导航条模板使用详解(含下拉菜单,弹出框)
2017/11/17 Javascript
基于dataset的使用和图片延时加载的实现方法
2017/12/11 Javascript
vue 解决循环引用组件报错的问题
2018/09/06 Javascript
详解Vue一个案例引发「内容分发slot」的最全总结
2018/12/02 Javascript
简单说明Python中的装饰器的用法
2015/04/24 Python
Python之多线程爬虫抓取网页图片的示例代码
2018/01/10 Python
很酷的python表白工具 你喜欢我吗
2019/04/11 Python
关于Python-faker的函数效果一览
2019/11/28 Python
tensorflow 初始化未初始化的变量实例
2020/02/06 Python
keras读取h5文件load_weights、load代码操作
2020/06/12 Python
python pygame 愤怒的小鸟游戏示例代码
2021/02/25 Python
CSS3+JavaScript实现炫酷呼吸效果的示例代码
2020/06/15 HTML / CSS
英国最大的海报商店:GB Posters
2018/03/20 全球购物
毕业生自我鉴定
2013/12/04 职场文书
2014年大班元旦活动方案
2014/02/26 职场文书
《大自然的语言》教学反思
2014/04/08 职场文书
媒矿安全生产承诺书
2014/05/23 职场文书
2014年小学数学工作总结
2014/12/12 职场文书
工程部岗位职责
2015/02/10 职场文书
2015年音乐教师个人工作总结
2015/05/20 职场文书
如何计划开一家便利店?
2019/07/31 职场文书
Python WSGI 规范简介
2021/04/11 Python
手把手教你实现PyTorch的MNIST数据集
2021/06/28 Python