【php详细笔记】上传文件到服务器(下)

简介: 【php详细笔记】上传文件到服务器

第三步,判断文件的mime类型是否正确。


更多的时候,我们的文件上传功能,都需要判断用户上传的文件,是否符合要求,不可用的文件上传以后,对于线上应用的整体展示效果,会造成恶劣的影响。所以我们需要通过mime类型和后缀名,来判断用户上传的文件是否符合要求。


下面的示例代码中,我们假设当前的项目需求为指定上传图片,要求上传后缀名为GIF或者jpg的文件,当用户上传不符合要求的文件时,返回错误提示。

<?php
/*判断后缀名和MIME类型是否符合指定需求
例如:
当前项目指定上传后缀为.jpg或.gif的图片,则$allowSuffix = array('jpg','gif');
*/
//定义允许的后缀名数组
$myImg = explode('.', $_FILES['file']['name']);
/*
explode() 将一个字符串用指定的字符切割,并返回一个数组,这里我们将文件名用'.''切割,结果存在$myImg中,文件的后缀名即为数组的最后一个值
*/
$myImgSuffix = array_pop($myImg);
/*
根据上传文件名获取文件的后缀名
使用in_array()函数,判断上传文件是否符合要求
当文件后缀名不在我们允许的范围内时退出上传并返回错误信息
*/
if(!in_array($myImgSuffix, $allowSuffix)){
    exit("文件后缀名不符");
}
/*
mime类型和文件后缀名的对应关系,我们可以通过很多途径查询到,为了避免用户自主修改文件后缀名造成文件无法使用。
mime类型也必须做出限制检查mime类型,是为了防止上传者直接修改文件后缀名
导致文件不可用或上传的文件不符合要求。
*/
//数组内容为允许上传的mime类型
$allowMime = array(
    "image/jpg",
    "image/jpeg",
    "image/pjpeg",
    "image/gif"
);
if(!in_array($_FILES['file']['type'], $allowMime)){                      //判断上传文件的mime类型是否在允许的范围内
    exit('文件格式不正确,请检查');
    //如果不在允许范围内,退出上传并返回错误信息
}
?>


第四步,生成指定的路径和文件名。


按照项目的文件安排,生成文件存储路径,为了避免文件名重复而产生的错误,按照一定的格式,生成一个随机文件名。


<?php
//指定上传文件夹
$path = "upload/images/";
/*
根据当前时间生成随机文件名,本行代码是使用当前时间 + 随机一个0-9的数字组合成文件名,后缀即为前面取到的文件后缀名
*/
$name = date('Y').date('m').date("d").date('H').date('i').date('s').rand(0,9).'.'.$myImgSuffix;
?>


第五步,判断是否是上传文件。


is_uploaded_file()函数是专用的函数,来判断目标文件是否是上传文件。


<?php
//使用is_uploaded_file()判断是否是上传文件,函数介绍见上文
if(is_uploaded_file($_FILEs['file']['tmp_name'])){    
}
?>


第六步,移动文件到指定位置。


使用move_uploaded_file()函数,将文件移动到指定的位置,并命名。需要注意的是,Linux系统中对目标目录是否有权限及磁盘空间是否足够,否则会导致上传操作失败。

<?php
/*
使用move_uploaded_file()移动上传文件至指定位置,第一个参数为上传文件,第二个参数为我们在前面指定的上传路径和名称。
*/
if(move_uploaded_file($_FILEs['file']['tmp_name'], $path.$name)){
           //提示文件上传成功
           echo "上传成功";                                
       }else{
/*
文件移动失败,检查磁盘是否有足够的空间,或者linux类系统中文件夹是否有足够的操作权限
*/
           echo '上传失败';                                                
       }
   }else{
       echo '不是上传文件';
   }
}
?>


上传文件到服务器完整项目代码


我们将这个文件片段整理成一整个文件:


<?php
if ($_FILES['file']['error'] > 0) {
   switch ($_FILES['file']['error']) {
       //错误码不为0,即文件上传过程中出现了错误
       case '1':
           echo '文件过大';
           break;
       case '2':
           echo '文件超出指定大小';
           break;
       case '3':
           echo '只有部分文件被上传';
           break;
       case '4':
           echo '文件没有被上传';
           break;
       case '6':
           echo '找不到指定文件夹';
           break;
       case '7':
           echo '文件写入失败';
           break;
       default:
           echo "上传出错<br/>";
   }
} else {
   $MAX_FILE_SIZE = 100000;
   if ($_FILES['file']['size'] > $MAX_FILE_SIZE) {
       exit("文件超出指定大小");
   }
   $allowSuffix = array(
       'jpg',
       'png',
       'gif',
   );
   $myImg = explode('.', $_FILES['file']['name']);
   $myImgSuffix = array_pop($myImg);
   if (!in_array($myImgSuffix, $allowSuffix)) {
       exit("文件后缀名不符");
   }
   $allowMime = array(
       "image/jpg",
       "image/jpeg",
       "image/pjpeg",
       "image/gif",
       "image/png",
   );
   if (!in_array($_FILES['file']['type'], $allowMime)) {
       exit('文件格式不正确,请检查');
   }
   $path = "up/";
   $name = date('Y') . date('m') . date("d") . date('H') . date('i') . date('s') . rand(0, 9) . '.' . $myImgSuffix;
   echo "$name";
   echo "woshi";
   echo $_FILES['file']['tmp_name'];
   if (is_uploaded_file($_FILES['file']['tmp_name'])) {
       if (move_uploaded_file($_FILES['file']['tmp_name'], $path . $name)) {
           echo "上传成功";
       } else {
           echo '上传失败';
       }
   } else {
       echo '不是上传文件';
   }
}
?>


多文件上传


介绍了PHP上传单个文件的过程。但是有些时候,为了使用方便,我们需要满足同时上传多个文件的需求。多文件上传原理相同,不过在处理数据时,需要对上传数据进行特殊处理。

<html> 
   <head> 
       <meta charset="utf-8" /> 
       <title>单文件上传</title> 
   </head> 
   <body> 
       <form action="morefile.php" method="post" enctype="multipart/form-data"> 
        <input type="file" name="file[]"> 
        <input type="file" name="file[]"> 
        <input type="submit" value="上传"> 
    </form> 
   </body> 
</html>


这里是一个简易的上传页面,form表单同时提交了两个文件。我们 可以通过这个页面提交内容。


注意:


1.input type=“file” name="file[]"与之前相比file后多加了一个中括号


2.写了2个或者多个input type=“file” name=“file[]”


我们使用 $_FILES 来接收文件信息,打印并查看数组:


<?php 
var_dump($_FILES); //打印$_FILES查看数组结构 
?>


数组结构如下

array (size=1)  
    'file' =>  
        array (size=5) 
    'name' =>  
        array (size=2) 
        //文件名 
        0 => string 'psu.jpg' (length=7) 
        1 => string 'qwe.jpg' (length=7) 
    //文件mime类型 
    'type' => array (size=2) 
            0 => string 'image/jpeg' (length=10) 
            1 => string 'image/jpeg' (length=10) 
    //缓存文件 
    'tmp_name' =>  
        array (size=2) 
            0 => string 'E:\wamp\tmp\phpF6D5.tmp' (length=23) 
            1 => string 'E:\wamp\tmp\phpF6F5.tmp' (length=23) 
    //文件错误信息 
    'error' =>  
        array (size=2) 
            0 => int 0 
            1 => int 0 
    //文件大小 
    'size' =>  
        array (size=2) 
        0 => int 225824     
        1 => int 151651


我们可以看到,两个文件被存储在一个数组中,键名和上传单文件是相同。所以,需要我们用for()循环,来分别取出两个文件的需要用到的数据。


在$_FILES中同时保存了两个文件的数据,我们需要使用一个简单的循环,来读取单个文件的信息,并将文件移动到我们想要放的位置。


<?php
for ($i=0; $i < count($_FILE['file']['name']); $i++) {  
/* 
用is_uploaded_file()函数判断是上传文件 
并且没有出现错 
*/ 
   if(is_uploaded_file($_FILEs['file']['tmp_name'][$i]) && $_FILEs['file']['error'][$i] == 0){     
       if(move_uploaded_file($_FILEs['file']['tmp_name'][$i],'upload/'.$_FILE['file']['name'][$i])){
   //用move_uploaded_file()函数移动文件到指定的位置并使用文件原名 
   echo "上传成功"; 
       }else{ 
           echo '上传失败'; 
       } 
   }else{ 
       echo '上传失败'; 
   } 
} 
?>


详细的判断过程,参见单文件上传,这里只做了基本的判断,并没有对文件的大小及格式提醒。

请按照业务,自行判断文件大小及格式进行错误提醒。


文件上传进度处理


当文件过大,或者用户网络状态一般,通常上传过程需要一段时间,如果这时候让用户白屏等待的话,相信大部分用户都会直接关掉应用,所以一个监控上传进度,并实时向用户报告的需求就被产品汪摆在了桌面上。一个高逼格的上传进度提示,会让你的应用瞬间变成被仰望的存在。


PHP在5.4之前,总是需要安装额外的扩展才能监控到文件上传进度。而从5.4开始,引入session.upload_progress的新特性,我们只需要在php.ini中开启配置,即可通过session监控文件上传进度。在php.ini中。


注意:本章学习需要有session基础和javascript和ajax基础。


Jquery和JS


1668338568045.jpg


php.ini修改


我们需要配置,注意查看和修改php.ini文件:


配置项 说明

session.upload_progress.enabled 是否启用上传进度报告(默认开启) 1为开启,0为关闭
session.upload_progress.cleanup 是否在上传完成后及时删除进度数据(默认开启, 推荐开启)
session.upload_progress.prefix[=upload_progress_] 进度数据将存储在_SESSION[session.upload_progress.prefix . _POST[session.upload_progress.name]]
session.upload_progress.name[=PHP_SESSION_UPLOAD_PROGRESS]  如果_POST[session.upload_progress.name]没有被设置, 则不会报告进度.
session.upload_progress.freq[=1%] 更新进度的频率(已经处理的字节数), 也支持百分比表示’%’.
session.upload_progress.min_freq[=1.0]  更新进度的时间间隔(秒级)


开启了配置,我们可以通过session,来记录一个完整的文件上传进度。在session中,会出现一个如下结果的数组:

$_SESSION["upload_progress_test"] = array(
    //请求时间
     "start_time" => 1234567890,
     // 上传文件总大小
     "content_length" => 57343257,
     //已经处理的大小
     "bytes_processed" => 453489,
     //当所有上传处理完成后为TRUE,未完成为false
     "done" => false,
     "files" => array(
      0 => array(
        //表单中上传框的名字
       "field_name" => "file1",
       //上传文件的名称
       "name" => "test1.avi",
       //缓存文件,上传的文件即保存在这里
       "tmp_name" => "/tmp/phpxxxxxx",
       //文件上传的错误信息
       "error" => 0,
       //是否上传完成,当这个文件处理完成后会变成TRUE
       "done" => true, 
       //这个文件开始处理时间
       "start_time" => 1234567890,
       //这个文件已经处理的大小
       "bytes_processed" => 57343250,     
      ),
      1 => array(
       "field_name" => "file2",
       "name" => "test2.avi",
       "tmp_name" => NULL,
       "error" => 0,
       "done" => false,                    
       "start_time" => 1234567899,
       "bytes_processed" => 54554,
      ),
     )
    );


AJAX来获取进度


这个数组详细记录了文件上传的进度,已经处理完的文件状态为true。下面,我们通过一个jQuery的AJAX实例,来学习一下文件上传进度的流程。


首先,在表单中,需要添加一个type=hidden 的 input 标签,标签 value 为自定义(建议使用有一定意义的值,因为这个值将要在后台用到)

<form id="upload-form" action="upload.php" method="POST" enctype="multipart/form-data" style="margin:15px 0" target="hidden_iframe">
    <input type="hidden" name="<?php echo ini_get("session.upload_progress.name"); ?>" value="test" />
    <p><input type="file" name="file1" /></p>
    <p><input type="submit" value="Upload" /></p>
</form>
<div id="progress" class="progress" style="margin-bottom:15px;display:none;">
    <div class="label">0%</div>
</div>


这里,添加了一个ID为progress的div,作为展示上传进度的容器。我们通过js的setTimeout(),定时执行ajax来获取文件上传进度,后台文件返回文件上传的进度百分比。

<script src="../jquery/1.8.2/jquery.min.js"></script>
<script type="text/javascript">
function fetch_progress(){
    $.get('progress.php',{ '<?php echo ini_get("session.upload_progress.name"); ?>' : 'test'}, function(data){
        var progress = parseInt(data);
        $('#progress .label').html(progress + '%');
        if(progress < 100){
            setTimeout('fetch_progress()', 100);    //当上传进度小于100%时,显示上传百分比
        }else{
            $('#progress .label').html('完成!'); //当上传进度等于100%时,显示上传完成
        }
    }, 'html');
}
$('#upload-form').submit(function(){
    $('#progress').show();
    setTimeout('fetch_progress()', 100);//每0.1秒执行一次fetch_progress(),查询文件上传进度
});
</script>

上面这段代码,就是通过JQ的ajax,每0.1秒返回一次文件上传进度。并把进度百分比在div 标签中显示。


后台代码,需要分为两个部分,upload.php处理上传文件。progress.php 获取session中的上传进度,并返回进度百分比。


这里文件上传就不再赘述,详细步骤参见上文,upload.php:


<?php
if(is_uploaded_file($_FILES['file1']['tmp_name'])){                                            //判断是否是上传文件
   //unlink($_FILES['file1']['tmp_name']);    
   move_uploaded_file($_FILES['file1']['tmp_name'], "./{$_FILES['file1']['name']}");     //将缓存文件移动到指定位置
}
?>

主要关注progress.php:

<?php
/*
开启session。请注意在session_start()之前,请不要有想浏览器输出内容的动作,否则可能引起错误。
*/
session_start();
//ini_get()获取php.ini中环境变量的值
$i = ini_get('session.upload_progress.name');
//ajax中我们使用的是get方法,变量名称为ini文件中定义的前缀 拼接 传过来的参数
$key = ini_get("session.upload_progress.prefix") . $_GET[$i];    
//判断 SESSION 中是否有上传文件的信息
if (!empty($_SESSION[$key])) {                                        
   //已上传大小
   $current = $_SESSION[$key]["bytes_processed"];
   //文件总大小
   $total = $_SESSION[$key]["content_length"];
   //向 ajax 返回当前的上传进度百分比。
   echo $current < $total ? ceil($current / $total * 100) : 100;
}else{
   echo 100;                                                       
}
?>

到这里,文件进度的代码就已经完成了,配合前端,我们就可以做一个炫酷的文件上传功能啦!


相关文章
|
2月前
|
缓存 网络协议 数据可视化
WinSCP下载安装并实现远程SSH本地服务器上传文件
WinSCP下载安装并实现远程SSH本地服务器上传文件
|
2天前
|
PHP Android开发
android通过http上传文件,服务器端用php写(原创)
android通过http上传文件,服务器端用php写(原创)
14 4
|
2月前
|
监控 安全 容灾
PHP服务器稳定性保障
确保PHP服务器稳定性,需关注以下几点:配置合适硬件及优化操作系统;使用最新稳定版PHP,及时更新安全补丁;编写高质量代码并优化性能;处理异常,记录日志以便监控;管理资源,使用性能监控工具;加强安全防护,如权限设置、防注入攻击;采用自动化部署和持续集成工具;定期备份数据,建立容灾机制。
20 0
|
2月前
|
存储 网络协议 数据可视化
如何使用Synology Drive作为文件同步服务器实现云同步Obsidian笔记
如何使用Synology Drive作为文件同步服务器实现云同步Obsidian笔记
|
3月前
|
弹性计算 PHP
ECS续费问题之PHP运行环境到期如何解决
ECS续费是指对已创建的阿里云ECS实例执行的续费操作,以延长其服务期限;本合集旨在为用户提供ECS续费的操作步骤、策略选择和注意事项,确保业务的持续性和成本的优化。
ECS续费问题之PHP运行环境到期如何解决
|
3月前
|
JSON JavaScript Java
【PHP】英文博客专栏PHP快速入门个人笔记
【PHP】英文博客专栏PHP快速入门个人笔记
120 1
|
SQL 关系型数据库 MySQL
|
8月前
|
关系型数据库 MySQL PHP
PHP 原生操作 Mysql
PHP 原生操作 Mysql
82 0
|
8月前
|
关系型数据库 MySQL 数据库连接
PHP 原生连接 Mysql
PHP 原生连接 Mysql
108 0
|
8月前
|
关系型数据库 MySQL Unix
PHP MySql 安装与连接
PHP MySql 安装与连接
135 0