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 的继承
Oct 01 Javascript
Javascript操作URL函数修改版
Nov 07 Javascript
js类型转换与引用类型详解(Boolean_Number_String)
Mar 07 Javascript
javascript引用类型指针的工作方式
Apr 13 Javascript
JavaScript实现向setTimeout执行代码传递参数的方法
Apr 16 Javascript
JavaScript简单修改窗口大小的方法
Aug 03 Javascript
JavaScript必知必会(九)function 说起 闭包问题
Jun 08 Javascript
Bootstrap Modal对话框如何在关闭时触发事件
Dec 02 Javascript
解决拦截器对ajax请求的拦截实例详解
Dec 21 Javascript
纯js代码生成可搜索选择下拉列表的实例
Jan 11 Javascript
AngularJS标签页tab选项卡切换功能经典实例详解
May 16 Javascript
jQuery实现简单日历效果
Jul 05 jQuery
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如何通过date() 函数格式化显示时间
2020/11/13 PHP
JavaScript创建命名空间(namespace)的最简实现
2007/12/11 Javascript
利用Ext Js生成动态树实例代码
2008/09/08 Javascript
javascript 面向对象全新理练之继承与多态
2009/12/03 Javascript
js浏览器本地存储store.js介绍及应用
2014/05/13 Javascript
JavaScript 学习笔记之操作符(续)
2015/01/14 Javascript
JS实现的3D拖拽翻页效果代码
2015/10/31 Javascript
JS中的phototype详解
2017/02/04 Javascript
JavaScript中数组Array.sort()排序方法详解
2017/03/01 Javascript
JS实现数组去重方法总结(六种方法)
2017/07/14 Javascript
一次记住JavaScript的6个正则表达式方法
2018/02/22 Javascript
vue2.0 computed 计算list循环后累加值的实例
2018/03/07 Javascript
从零开始搭建vue移动端项目到上线的步骤
2018/10/15 Javascript
JS使用Chrome浏览器实现调试线上代码
2020/07/23 Javascript
Python实现的金山快盘的签到程序
2013/01/17 Python
闭包在python中的应用之translate和maketrans用法详解
2014/08/27 Python
跟老齐学Python之用Python计算
2014/09/12 Python
python实现简单的计时器功能函数
2015/03/14 Python
Matplotlib 生成不同大小的subplots实例
2018/05/25 Python
Python2与Python3的区别实例总结
2019/04/17 Python
python针对mysql数据库的连接、查询、更新、删除操作示例
2019/09/11 Python
python多进程并行代码实例
2019/09/30 Python
基于python操作ES实例详解
2019/11/16 Python
详解Django关于StreamingHttpResponse与FileResponse文件下载的最优方法
2021/01/07 Python
介绍一下RMI的基本概念
2016/12/17 面试题
英文导游欢迎词
2014/01/11 职场文书
甜美蛋糕店创业计划书
2014/01/30 职场文书
给实习单位的感谢信
2014/02/01 职场文书
房地产推广策划方案
2014/05/19 职场文书
四风对照检查材料范文
2014/09/27 职场文书
2016党员干部政治学习心得体会
2016/01/23 职场文书
《观察物体》教学反思
2016/02/17 职场文书
咖啡厅里的创业计划书
2019/08/21 职场文书
Nginx代理同域名前后端分离项目的完整步骤
2021/03/31 Servers
如何理解PHP核心特性命名空间
2021/05/28 PHP
Python Django / Flask如何使用Elasticsearch
2022/04/19 Python