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 DOM 学习第七章 表单的扩展
Feb 19 Javascript
详解JavaScript语法对{}处理的坑爹之处
Jun 05 Javascript
javascript操纵OGNL标签示例代码
Jun 16 Javascript
Vuejs第一篇之入门教程详解(单向绑定、双向绑定、列表渲染、响应函数)
Sep 09 Javascript
JavaScript 数据类型详解
Mar 13 Javascript
js基于FileSaver.js 浏览器导出Excel文件的示例
Aug 15 Javascript
前端把html表格生成为excel表格的实例
Sep 19 Javascript
详解如何用模块化的方式写vuejs
Dec 16 Javascript
初探Vue3.0 中的一大亮点Proxy的使用
Dec 06 Javascript
图文讲解vue的v-if使用方法
Feb 11 Javascript
Vue.js仿Select下拉框效果
Feb 18 Javascript
electron踩坑之remote of undefined的解决
Oct 06 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封装的连接Mysql类及用法分析
2015/12/10 PHP
PHP实现用session来实现记录用户登陆信息
2018/10/15 PHP
jquery 学习笔记一
2010/04/07 Javascript
仿百度联盟对联广告实现代码
2014/08/30 Javascript
js的toLowerCase方法用法实例
2015/01/27 Javascript
jQuery实现提交按钮点击后变成正在处理字样并禁止点击的方法
2015/03/24 Javascript
js实现网页图片延时加载 提升网页打开速度
2016/01/26 Javascript
javascript实现简单计算器效果【推荐】
2016/04/19 Javascript
基于JS代码实现导航条弹出式悬浮菜单
2016/06/17 Javascript
JS文件/图片从电脑里面拖拽到浏览器上传文件/图片
2017/03/08 Javascript
VUE实现日历组件功能
2017/03/13 Javascript
JS实现商品筛选功能
2020/08/19 Javascript
微信小程序开发之改变data中数组或对象的某一属性值
2018/07/05 Javascript
vue.js指令v-for使用以及下标索引的获取
2019/01/31 Javascript
详解关于webpack多入口热加载很慢的原因
2019/04/24 Javascript
详解JS判断页面是在手机端还是在PC端打开的方法
2019/04/26 Javascript
vue 自动化路由实现代码
2019/09/03 Javascript
JavaScript实现文件下载并重命名代码实例
2019/12/12 Javascript
js实现ajax的用户简单登入功能
2020/06/18 Javascript
[01:00]DOTA2 store: Collection of Artisan's Wonders
2015/08/12 DOTA
python动态网页批量爬取
2016/02/14 Python
Python学习笔记之解析json的方法分析
2017/04/21 Python
详解Django之admin组件的使用和源码剖析
2018/05/04 Python
python保存网页图片到本地的方法
2018/07/24 Python
python的xpath获取div标签内html内容,实现innerhtml功能的方法
2019/01/02 Python
如何通过雪花算法用Python实现一个简单的发号器
2019/07/03 Python
pytorch程序异常后删除占用的显存操作
2020/01/13 Python
小白教你PyCharm从下载到安装再到科学使用PyCharm2020最新激活码
2020/09/25 Python
解析HTML5中的新功能本地存储localStorage
2016/03/01 HTML / CSS
新学期国旗下演讲稿
2014/05/08 职场文书
工程技术负责人岗位职责
2015/04/13 职场文书
《山中访友》教学反思
2016/02/24 职场文书
教你利用python实现企业微信发送消息
2021/05/23 Python
MySql存储过程之逻辑判断和条件控制
2021/05/26 MySQL
5种方法告诉你如何使JavaScript 代码库更干净
2021/09/15 Javascript
ubuntu端向日葵键盘输入卡顿问题及解决
2022/12/24 Servers