第1章 php文件上传
1.1 文件上传的基本介绍、应用场景
点击上传时,照片会上传到哪里去?
会将图片、文件等上传到服务器上面,并返回图片的地址,这样,我们只需要给其他用户提供该文件的地址即可
1.2 文件上传的原理说明
文件上传,其实也是通过表单提交的,表单提交的数据分为如下两种:
(1)字节流数据:输入框、单选框、复选框、多行文本域等都是通过字节的数据传输到服务器,服务器通过$_GET $_POST接收
(2)二进制文件流的形式;当需要提交一些文件的时候,由于文件采用的二进制的形式进行编码,所以需要先将二进制文件转码,然后再提交到服务器
提交的时候,需要通过input type=”file” 文件域上传
服务器接收的时候,通过$_FILES进行接收
注意:如何将表单的数据,以二进制流的形式进行传输,必须在表单form标签增加一个属性:enctype=”multipart/form-data”
先创建表单文件:upload.html
提交之后,在服务器端接收并打印:
MIME类型详解:
多用途internet邮件扩展,mime类型的出现,跟着电子邮件出现的
早期,发送电子邮件的时候,里面可能会附带一些附件,计算机系统根据附件的类型找到对应的设备打开,例如:传输的是视频,就会找到视频播放器去打开,如果传输的是网页文件,那么会自动使用浏览器去打开
MIME类型,包括2个部分:第一个部分是该文件所属的一个大类,第二个部分是文件细节小类,例如:
text/html,属于文本文件,html这样的文本文件
image/jpg,属于图像大类,jpg这种类型的图像
tmp_name详解:
tmp------ temp----temporary,该单词的意思是临时的
文件上传的原理:
当我们点击提交表单时,会将file文件域里面的文件,先上传到临时的目录(可以在php的配置文件中设置),默认的临时目录是c:/windows/temp,如果需要持久的保存,需要通过php的函数move_uploaded_file()移动到指定的位置,如果不移动,当php脚本执行结束,临时文件就会消失
代码演示一下:
由于php脚本执行的太快,为了看到效果,我们让php脚本休眠10秒钟:
提交表单之后,就会在c:/windows/temp目录下面看到这个临时文件
为了不让该临时文件消失,我们需要在脚本结束之前,将该临时文件移动到服务器
使用php的move_uploaded_file()函数进行移动的
1.3 文件上传的入门案例
为了方便管理这些上传的文件,我们通常会将上传的文件保存到uploads目录下面
1.4 防止上传文件过大
思路:首先给定一个文件的最大限制,然后再拿上传的文件的大小 和 最大的限制进行比较,文件大小单位是字节,字节之间的转换:1024
1024字节 = 1KB
1024KB = 1MB
1024MB = 1GB
修改一下表单的提交地址:
确实阻止了文件的上传,但是还有一个小问题:
虽然上传时,拦截了,但是该文件还是在临时的文件夹中走了一圈,如何实现,如果文件过大的话,连临时目录都不让其进去
需要修改php的配置文件了,将其最大的限制修改为2MB,这样,让我们上传的文件大小超过2MB,就无法进入到临时目录
测试一把:
错误指的是提交的表单内容,超过了表单提交的最大限制,所以为了演示效果,我们临时将表单提交的最大限制修改为80MB
1.5 防止文件被覆盖
如果多个用户,同时上传文件,如果文件的名字固定死的话,所有用户上传的文件名字都会一样,将来就很麻烦了
思路:上传成功之后,给文件命名时,取一个唯一的、随机数
1.6 分目录存放上传的文件
为什么要分目录存储?
如果上传的文件不加任何修改,直接保存到uploads目录的话,uploads目录就会出现很多文件,不便于查找
通常我们会按照日期的格式分目录保存
1.7 控制上传的文件类型
假设有这样一个需求:上传用户的头像
这个时候我们只能上传图片(jpg、png、gif),所以我们就应该上传的时候做一个判断
思路:首先,定义一个数组保存支持的上传的文件的类型,然后再拿上传的文件的实际类型和支持的类型进行比较
但是,上面的代码存在一个小问题:
如果手动的把一个excel文件的后缀修改为.jpg了,如下所示:
上传的时候,就会把php蒙混过去,如果用户上传的是脚本(js等),就会很危险
所以我们还要做一个更加严格的过滤
需要借助PHP提供的类(php 内置的一个类)finfo,在手册的位置:
1.8 将文件上传代码封装成函数
为什么要封装到函数中?
提高上传文件的代码的重用性,保证这些代码能够在不同的场景中灵活的应用
封装到函数之前,先思考哪些部分将来可能会变化、修改,将这些可能变化的部分以参数形式传递进去,这样,我们的函数更加灵活
例如,将来上传分类的图标了,文件上传域的name是cat_logo
详细代码参考:2.upload_maxsize.php
<?php //参数是实际参数 upload($_FILES['cat_logo']); function upload($file) { // sleep(5); //将临时文件移动到服务器的目录中 //参数1:临时的文件名 //参数2:目的地文件名 //上传成功返回true,失败返回false $destination = 'uploads/'; //1. 限制上传文件的大小 $maxsize = 200*1024; //200KB if($file['size'] > $maxsize){ echo '图片太大了,服务器撑不下'; exit; } //2. 防止文件重复 //首先生成一个唯一的随机数作为文件的名字 //参数1:前缀 //参数2:布尔值,如果true化,更具有唯一性 $filename = uniqid('tn_',true); //确定文件的后缀 //strrchr()用来获得一个字符串中最后一次出现的字符,返回从该字符之后的部分 //参数1:look needle from haystack,大海捞针 $ext = strrchr($file['name'], '.'); $new_filename = $filename.$ext; //3. 分目录存储上传的文件 //按照日期创建子目录 $sub_path = date('Ymd').'/'; //创建目录,先判断下 uploads/20170330是否存在,如果不存在则创建该目录 if(!is_dir($destination.$sub_path)){ mkdir($destination.$sub_path,0777,true); } $destination .= $sub_path.$new_filename; //4. 上传的文件类型是否支持 $allow_type = array('image/jpeg','image/jpg','image/png','image/gif'); $true_type = $file['type']; if(!in_array($true_type, $allow_type)){ echo '不支持该类型的文件'; exit; } //实例化finfo对象,用来获得一个文件的真实的类型 $finfo = new finfo(FILEINFO_MIME_TYPE); $type = $finfo -> file($file['tmp_name']); if(!in_array($type, $allow_type)){ echo '不支持该类型的文件'; exit; } if(move_uploaded_file($file['tmp_name'], $destination)){ echo '上传成功'; }else{ echo '上传失败'; } }
1.9 文件上传类
为什么要将上面的代码封装类中呢?
因为现在主流的编程思想都是OOP(面向对象编程),那么面向对象编程和面向过程编程的区别是:
面向过程:文件里面的基本单位是函数
面向对象:文件里面基本的单位是类
如何封装到类中?
类包括:成员属性、成员方法
成员属性:和变量的联系、 区别:
联系是:都是用来存储数据的
区别是:属性有家,变量没有家
既然他们都是用来保存数据的,那么如果一个数据从脚本开始到结束,都不会变化的话,我们就没有必要将其保存,反过来,属性只保存哪些将来可能变化的数据
回到文件上传类中:
通过分析,发现上传的文件保存的路径、限制的大小、文件名的前缀、允许的文件类型等这些数据将来可能变化,所以我们将其保存到属性中
详细代码:
演示一下:
1.10 作业
图片墙
思路:
上传成功之后,要将上传的图片地址保存到数据库
当点击查看头像墙的时候,再从数据库查询上传的文件地址,将地址显示到img标签中即可
第2章 php文件下载
2.1 文件下载
如果下载的文件较多、文件大,通常就会使用百度云,如果下载的文件小、文件少的话,通常会使用php进行下载
通过php下载文件的原理:
先通过php读取下载的文件资源,读取到这些资源之后,再将其保存到文件中
代码演示:
在服务器文件中,先读取文件的内容,再告诉浏览器以附件的形式来接收这些内容
第3章 php分页类
3.1 分页基本介绍
讲一个知识点之前,先考虑为什么学习这个知识点
首先,先看一个没有使用分页的应用,在网页中显示所有的商品信息
(1) 先创建一个商品的数据表,并添加测试数据
(2) 通过php查询商品的信息,并在网页显示出来
创建模板文件,输出商品信息:
演示一把:
上面的写法,有如下不足之处:
(1) 所有的内容都显示到一页上,用户体验不好,内容太多看得眼花缭乱
(2) 程序一次性读取所有的内容,效率比较低
针对上面的不足,我们要采用分页显示,也就是将上面的内容分为多个页面来显示,每页显示几条内容
3.2 具体实现
(1) 先创建一个分页导航条,可以点击
(2) 动态创建分页导航条,直接采用面向对象思想实现
成员属性:保存数据的,保存将来可能会变化的数据
创建多少页,将来会变化,所以我们将其保存到属性上
总的页数 == 查询的数据库的记录数 / 每页显示的条数
成员方法:具体的创建分页导航的代码
演示一下:
(3) 分页导航只显示当前页的前3页、后3页
例如:当前页是第5页,显示:2 3 4 5 6 7 8
思路:
首先创建多少个li标签,取决于当前页,创建前3页:就是从当前页-3开始,后3页就是当前页+3结束
如果遇到这样的情况:当前页是第1页的时候,前3页:0 -1 -2,如果当前页是17最后一页的时候,后面就是:18 19 20,显然没有上面的3页
(4) 给当前页增加 active 这个类,让当前页高亮显示
思路:
首页,只有当当前页$this->now_page ==1 的时候才高亮显示
尾页,只有当当前页是最后一页的时候,$this->now_page = $page_count才
当我们点击哪一页的时候,该页才是当前页面,通过给每个超链接增加一个参数,表示是第几页
(5) 将商品列表 和 分页类结合起来应用