目录
PHP之文件的锁定、上传与下载
首页 php教程 php手册 PHP之文件的锁定、上传与下载

PHP之文件的锁定、上传与下载

Jun 13, 2016 am 09:20 AM
文件

PHP之文件的锁定、上传与下载

 小结文件的锁定机制、上传和下载

 

     1.文件锁定

 

     现在都在讲究什么分布式、并发等,实际上文件的操作也是并发的,在网络环境下,多个用户在同一时刻访问页面,对同一服务器上的同一文件进行着读取,如果,这个用户刚好读到一半,另一个用户就写入了消息,那么前一个用户读到的就是错误数据,在数据库里面好像是称为脏数据,而如果某用户写到一半时,另一用户也对该文件进行写操作,那么就造成了写入数据的混乱和错误,因此才php有一个锁机制,类似于数据库的锁,当某用户在对文件操作时就加上某种锁,使得在同一时间其他用户不能对该文件进行操作或只能进行有限的操作,来保证在这些情况下的文件数据的正确性。

 

     主要使用flock函数,原型:bool flock(resource $handle , int $operation [, int &$wouldblock ]),第一个参数是指向文件的句柄变量,第二个是加锁的方式,分别为

 

     LOCK_SH:共享锁(share),在读取文件时加的锁,加锁后就其他用户不能再对该文件进行写,但可以读取该文件内容;

 

     LOCK_EX:排他锁(exclude),或者叫独占锁,在写文件时使用,加了该锁后,只能是当前用户进行写操作,其他的用户不能读取和写入;

 

     LOCK_NB:附加锁,在文件锁定短时间大量用户的访问操作可能会造成flock在锁定时堵塞,如果再加上该锁后可避免该情况(是不是这么一弄就能解决大量读写操作的问题,怕不行...);

 

     LOCK_UN:释放锁,对前面的各种锁进行一次性释放,解锁。

 

     如果容易堵塞,还可使用第三个参数wouldblock,如果把它设置为1,在锁定后就会阻挡其他进程来进行一些操作,但是windows上不支持,另外附加锁LOCK_NB,windows也是不支持的。

 

     另外,关闭句柄变量的fclose操作也可以释放这些锁。

 

     废话少说,看代码    

 

复制代码

     function readFileData($filename){

         if(true == ($handle = fopen($filename, 'r'))){

             if(flock($handle, LOCK_SH+LOCK_NB)){  // 加共享锁和附加锁,附加锁防止阻塞

                 $str = '';

                 while(!feof($handle)){

                     $str .= fread($handle, 128);

                  }

                  flock($handle, LOCK_UN); // 释放该锁

                      fclose($handle);

                      return $str;

              }

              else{

                 echo 'add a share lock failed';

                 return '';

              }

          }

          else{

              return '';

          }

       }

复制代码

      注意使用多重锁的方式,是LOCK_SH+LOCK_NB,,在排他锁时也可这么用,它们都是枚举变量。对于写操作类似

 

复制代码

    function writeInFile($filename, $data){

        if(true == ($handle = fopen($filename, 'a'))){

            if(flock($handle, LOCK_EX)){   //加上排他锁

                fwrite($handle, $data);

                flock($handle, LOCK_UN);  //释放锁

                fclose($handle);

            }

            else{

                echo 'add exclusive lock failed';

                return;

            }

       }

    }

复制代码

      实际上这也是解决php来实现多进程/线程读取文件的方式,php没有多线程这么一说,但加锁机制可以来模拟这种方式,实现对文件某些操作的队列处理,面试时别说你不知道-_-

 

      2.文件上传

 

      文件上传就是将本地的文件上传到服务器上,我们在用度场的云、鹅场的云上传文件时均是如此,当然实际情况肯定比这简单程序复杂。HTTP协议实现了文件的上传机制,首先要在本地选择上传的文件,上传到服务器后,服务端又要做一些处理,为此客户端和服务端均要做一些设置。

 

      1、客户端

 

      文件上传最基本的方法是通过form表单进行POST传递文件,实际上通过PUT方法也可以上传文件,只不过这种方法不安全,需要配置一些安全验证机制,这里只写最常用的方式。form表单的input标签可以设置成文件上传按钮type="file",直接解决了如何选择文件的问题,接下来需要设置form的两个属性:enctype和method

 

      enctype:设置成multipart/form-data

 

      method:设置成post

 

      关于enctype属性设置可参考W3School的解释

 

     

 

      第一条是默认的值,在我们使用HTTP协议传递一般的表单数据时,实际上默认对数据进行了分块编码,比如默认的urlencode方式(空格转为+,其他非字母字符转为%加两个十六进制大写数);当enctype设置为第二个时,不会进行字符编码,使用上传控件(input标签type设为file时即是)上传文件,必须设定为这个值;第三个则是值对空格编码为+,但不对非字母字符进行编码。

 

     我们知道GET方法一般用于获取数据,且传递数据大小有限,而且POST方法可以传递比GET大得多的数据。

 

     form的属性设置完成后,还要传递一个值过去,使用隐藏域(),它的name属性设为MAX_FILE_SIZE,之所以要设定这个值,是先大概定一个文件尺寸值,避免在用户传一个大文件传了半天再告诉他:sorry,你的文件太大了-_-它的value属性值就是文件的size,以字节为单位。当然某些书上说,这个值只是作为参考,可轻易进行欺骗,这里只是象征性的表示,很可惜我这只菜鸟对安全了解甚少,只知道普通注入、XSS等,暂且用着吧。

 

     那么就可以写一个简单得不能再简单的页面了,作为客户端用:

 

   

    选择文件:

             

   

     2、服务端

 

     文件上传到了服务器上还要经过一些处理过程,就像网购派送快递,到了目的地也还得分个类,确认下目的地对错吧。到了目的地的后续处理需要php脚本,上面在提交表单时的action属性就指定了提交的处理脚本。我们知道在php中,$_POST保存的是post传递的数据,而上传文件的相关信息保存在$_FILES里边,假设服务端脚本是这样的:

 

    echo '_FILES:

';
<p>    print_r($_FILES);</p>
<p>      </p>
<p>    echo '_POST: </p><pre class="brush:php;toolbar:false">';
<p>    print_r($_POST);</p>
<p>     不管服务端如何处理的,先看看这两个数组里面有什么:</p>
<p> </p>
<p>        </p>
<p> </p>
<p>      看FILES数组的选项就猜得到,这些就是上传文件的名字、类型、尺寸、错误信息等等,还有这个FILES是二维数组。在弄清楚这些选项之前有必要了解几个php配置选项,打开php.ini文件,找到下面四项(其实看注释也明白了):</p>
<p> </p>
<p>      file_uploads:是否允许通过HTTP传递文件,默认是On允许;</p>
<p> </p>
<p>      upload_max_filesize:允许传递文件的最大大小,以M为单位,这是服务端配置文件设定的选项;</p>
<p> </p>
<p>      max_file_uploads:一次请求所允许传递的做多文件个数;</p>
<p> </p>
<p>      post_max_size:通过POST传递数据的最大大小,因为文件传递也是post方式,也算post传递,需要特别注意的是,它必须要大于upload_max_filesize选项,因为在一次post传递过程中不仅会上传文件,还会传递其他数值,比如上面的POST数组中的数据,必须考虑到,比如upload_max_filesize设为150M,这个就可以设为200M;</p>
<p> </p>
<p>      upload_tmp_dir:上传文件的临时目录,配置文件里面默认为空,会使用操作系统默认的临时目录,因此上面的FILES数组中的tmp_name中的眼熟的路径就可以解释了,使用windows默认的存放临时文件的目录,而且服务器默认对文件名作了修改。</p>
<p> </p>
<p>      那么FILES数组中的uploadFile哪里来的,为什么要用它做键名,这是因为在上传控件的name属性就是uploadFile,它标记的是这个控件的上传文件信息,因此我们可以放多个上传控件,设置不同的name,当然设置一样的name也可以,完全可以把它们全放在一个数组里边,如<input type="file" name="upload[]">。</p>
<p> </p>
<p>      现在回过头看FILE数组的键名代表的信息,type是MIME类型,以/分隔,前面是主要类型,后面是具体文件类型,error肯定表示错误,有这么几种情况,0:没有错误,上传成功; 1:文件超过了PHP配置指令中的upload_max_filesize规定的大小; 2:文件超过HTML表单中MAX_FILE_SIZE规定的大小,3:文件只有部分上传; 4:没有文件上传。现在关于FILES数组的问题全部明白了。</p>
<p> </p>
<p>      问题是,是不是上传成功就不做任何处理了,当然不是,总不能全堆在一个临时目录里面,上传多了必然就要将文件移到别的地方,而php提供了专门而安全的函数。is_uploaded_file函数,判断是否通过HTTP POST上传,可以确保恶意的用户去欺骗脚本而管理这些文件,例如/etc/pass(又是linux...),至于具体怎样,我还不清楚。move_uploaded_file函数,将上传文件移动到新位置,同时还可判断文件是否为合法上传,即通过HTTP POST方式,他们运行成功均返回布尔类型true。</p>
<p> </p>
<p>     扯了半天,上传文件大概要经过这样几个步骤:</p>
<p> </p>
<p>     1、客户端写好上传控件脚本,并传递一个限制文件大小的隐藏值;</p>
<p> </p>
<p>     2、服务端首先判断FILES数组error值,看是否出错;</p>
<p> </p>
<p>     3、判断是否为允许上传的类型(可以不判断);</p>
<p> </p>
<p>     4、判断在服务端脚本里边是否超过指定的文件大小;</p>
<p> </p>
<p>     5、上传到临时位置,生成新文件名(防止把已有同名文件覆盖掉),检查并移动到新目录下。</p>
<p> </p>
<p>     客户端准备工作刚已做,看服务端处理代码:</p>
<p> </p>
<p>复制代码</p>
<p><?php </p>
</p><p>    $typeWhiteList = array('txt', 'doc', 'php', 'zip', 'exe');   // 类型白名单,过滤不允许上传的文件类型</p>
<p>    $max_size = 1000000;  // 大小限制 为1M</p>
<p>    $upload_path = 'D:/WAMP/upload/';    // 指定移至的目录</p>
<p>     </p>
<p>    // 1、判断是否成功上传到服务器 </p>
<p>    $error = $_FILES['uploadFile']['error'];</p>
<p>    if($error > 0){</p>
<p>         switch($error){</p>
<p>             case 1: exit('超过php配置的最大文件上传限制');</p>
<p>             case 2: exit('超过HTML表单的最大文件上传限制');</p>
<p>             case 3: exit('文件只有部分被上传');</p>
<p>             case 4: exit('没有上传任何文件');</p>
<p>             default: exit('未知类型错误');</p>
<p>         }</p>
<p>    }</p>
<p>     </p>
<p>    // 2、判断是否为允许上传的类型</p>
<p>    $extension = pathinfo($_FILES['uploadFile']['name'], PATHINFO_EXTENSION); // 获取扩展名</p>
<p>    if(!in_array($extension, $typeWhiteList)){</p>
<p>        if($extension == '')</p>
<p>           exit('不允许上传空类型文件');</p>
<p>         else </p>
<p>           exit('不允许上传'.$extension.'类型文件');</p>
<p>    } </p>
<p>     </p>
<p>    // 3、判断是否为允许大小</p>
<p>    if($_FILES['uploadFile']['size'] > $max_size){</p>
<p>        exit('超过了允许上传到的'.$max_size.'字节');</p>
<p>    }</p>
<p>     </p>
<p>    // 4、已到指定位置</p>
<p>    $filename = date('Ymd').rand(1000, 9999);   // 生成一个新文件名,防止覆盖</p>
<p>    if(is_uploaded_file($_FILES['uploadFile']['tmp_name'])){   // 判断是否通过HTTP POST上传</p>
<p>        if(!move_uploaded_file($_FILES['uploadFile']['tmp_name'], $upload_path.$filename.'.'.$extension)){</p>
<p>            exit('无法移动到指定位置');</p>
<p>         }</p>
<p>         else{</p>
<p>            echo '文件上传成功<br>';</p>
<p>            echo '文件名: '.$upload_path.$filename.'.'.$extension.'<br>';</p>
<p>         }</p>
<p>    }</p>
<p>     else{</p>
<p>         exit('文件未通过合法途径上传');</p>
<p>     }</p>
<p>复制代码</p>
<p>      本想迅速体验一把,结果报了个Warning,说时间设置依赖系统...bug总是这么不期而遇,设置好时间后,再试,perfect!</p>
<p> </p>
<p>         </p>
<p> </p>
<p>      3.文件下载</p>
<p> </p>
<p>      文件下载就比较简单了,简单的文件下载只需要用一个HTML链接就够了,使用<a>标签,href属性指定资源位置,一点就可。但这种方式只能处理浏览器默认无法识别的MIME类型,比如rar、7z等压缩的数据。</a></p>
<p> </p>
<p>复制代码</p>
<p></p>
<p>    </p>
<p>             </p><title>donwload file</title>
<p>             <meta http-equiv="Content-Type" content="text/html" charset="utf-8"></p>
<p>    </p>
<p>    </p>
<p>             <a href="resource/header.txt">header.txt</a><br></p>
<p>             <a href="resource/php.zip">php.zip</a><br></p>
<p>             <a href="resource/pic.ico">pic.ico</a></p>
<p>           </p>
<p>    </p>
<p></p>
<p>复制代码</p>
<p>      对于这些浏览器不认识的类型文件,点链接,它直接弹框让你下载,有的浏览器甚至直接就下了,那么对于文本txt、jpg等浏览器默认识别的类型的文件,一点击则会直接展现在页面上,比如上面header.txt、pic.ico。如何不展示在页面上而去下载它们呢,使用header函数。</p>
<p> </p>
<p>      header函数会通过发送头信息告知,请把该文件当成一个附件,这样点击的时候,就也会下载了。</p>
<p> </p>
<p>复制代码</p>
<p><?php </p>
</p><p>    $filename = 'header.txt';</p>
<p>    header('Content-Type: text/plain');   // 类型为普通文本</p>
<p>    header('Content-Disposition:attachment; filename="$filename"');  // Content-Disposition:attachment,告诉它这是附件</p>
<p>    header('Content-Length:'.filesize($filename));   // 告知文件大小</p>
<p> </p>
<p>    readfile($filename);  // 读取文件直接输出,便于下载</p>
<p>复制代码</p>
						
登录后复制
本站声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

热AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover

AI Clothes Remover

用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool

Undress AI Tool

免费脱衣服图片

Clothoff.io

Clothoff.io

AI脱衣机

AI Hentai Generator

AI Hentai Generator

免费生成ai无尽的。

热门文章

R.E.P.O.能量晶体解释及其做什么(黄色晶体)
3 周前 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.最佳图形设置
3 周前 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.如果您听不到任何人,如何修复音频
3 周前 By 尊渡假赌尊渡假赌尊渡假赌

热工具

记事本++7.3.1

记事本++7.3.1

好用且免费的代码编辑器

SublimeText3汉化版

SublimeText3汉化版

中文版,非常好用

禅工作室 13.0.1

禅工作室 13.0.1

功能强大的PHP集成开发环境

Dreamweaver CS6

Dreamweaver CS6

视觉化网页开发工具

SublimeText3 Mac版

SublimeText3 Mac版

神级代码编辑软件(SublimeText3)

微信文件过期怎么恢复 微信的过期文件能恢复吗 微信文件过期怎么恢复 微信的过期文件能恢复吗 Feb 22, 2024 pm 02:46 PM

打开微信,在我中选择设置,选择通用后选择存储空间,在存储空间选择管理,选择要恢复文件的对话选择感叹号图标。教程适用型号:iPhone13系统:iOS15.3版本:微信8.0.24解析1首先打开微信,在我的页面中点击设置选项。2接着在设置页面中找到并点击通用选项。3然后在通用页面中点击存储空间。4接下来在存储空间页面中点击管理。5最后选择要恢复文件的对话,点击右侧的感叹号图标。补充:微信文件一般几天过期1要是微信接收的文件并没有点开过的情况下,那在七十二钟头之后微信系统会清除掉,要是己经查看了微信

照片无法打开此文件,因为格式不受支持或文件已损坏 照片无法打开此文件,因为格式不受支持或文件已损坏 Feb 22, 2024 am 09:49 AM

在Windows系统中,照片应用是一个便捷的方式来查看和管理照片和视频。通过这个应用程序,用户可以轻松访问他们的多媒体文件,而无需安装额外的软件。然而,有时候用户可能会碰到一些问题,比如在使用照片应用时遇到“无法打开此文件,因为不支持该格式”的错误提示,或者在尝试打开照片或视频时出现文件损坏的问题。这种情况可能会让用户感到困惑和不便,需要进行一些调查和修复来解决这些问题。当用户尝试在Photos应用程序上打开照片或视频时,会看到以下错误。抱歉,照片无法打开此文件,因为当前不支持该格式,或者该文件

在Windows 11/10中,准备删除要花费很长时间 在Windows 11/10中,准备删除要花费很长时间 Feb 19, 2024 pm 07:42 PM

在本文中,我们将介绍如何解决在Windows系统中删除文件或文件夹时出现“准备删除”提示的问题。这个提示意味着系统正在进行一些后台操作,如检查文件权限、验证文件是否被其他程序占用、计算要删除项目的大小等。我们将为您提供一些解决方法,以确保您能够顺利删除文件,而无需等待太长时间。为什么Windows要花这么长时间才能删除文件?Windows准备删除文件所需的时间受多种因素影响,包括文件大小、存储设备速度和后台进程。长时间或被卡住的“正在准备删除”提示可能暗示系统资源不足、磁盘错误或文件系统问题。在

可以删除Tmp格式文件吗? 可以删除Tmp格式文件吗? Feb 24, 2024 pm 04:33 PM

Tmp格式文件是一种临时文件格式,通常由计算机系统或程序在执行过程中生成。这些文件的目的是存储临时数据,以帮助程序正常运行或提高性能。一旦程序执行完成或计算机重启,这些tmp文件往往就没有了存在的必要性。所以,对于Tmp格式文件来说,它们本质上是可以删除的。而且,删除这些tmp文件能够释放硬盘空间,确保计算机的正常运行。但是,在删除Tmp格式文件之前,我们需

出现0x80004005错误代码怎么办 小编教你0x80004005错误代码解决方法 出现0x80004005错误代码怎么办 小编教你0x80004005错误代码解决方法 Mar 21, 2024 pm 09:17 PM

在电脑中删除或解压缩文件夹,时有时候会弹出提示对话框“错误0x80004005:未指定错误”,如果遇到这中情况应该怎么解决呢?提示错误代码0x80004005的原因其实有很多,但大部分因为病毒导致,我们可以重新注册dll来解决问题,下面,小编给大伙讲解0x80004005错误代码处理经验。有用户在使用电脑时出现错误代码0X80004005的提示,0x80004005错误主要是由于计算机没有正确注册某些动态链接库文件,或者计算机与Internet之间存在不允许的HTTPS连接防火墙所引起。那么如何

夸克网盘的文件怎么转移到百度网盘? 夸克网盘的文件怎么转移到百度网盘? Mar 14, 2024 pm 02:07 PM

  夸克网盘和百度网盘都是现在最常用的储存文件的网盘软件,如果想要将夸克网盘内的文件保存到百度网盘,要怎么操作呢?本期小编整理了夸克网盘电脑端的文件转移到百度网盘的教程步骤,一起来看看是怎么操作吧。  夸克网盘的文件怎么保存到百度网盘?要将夸克网盘的文件转移到百度网盘,首先需在夸克网盘下载所需文件,然后在百度网盘客户端中选择目标文件夹并打开。接着,将夸克网盘中下载的文件拖放到百度网盘客户端打开的文件夹中,或者使用上传功能将文件添加至百度网盘。确保上传完成后在百度网盘中查看文件是否成功转移。这样就

如何安装GHO文件 如何安装GHO文件 Feb 19, 2024 pm 10:06 PM

gho文件是一种GhostImage影像文件,它通常用于将整个硬盘或分区的数据备份成一个文件。在一些特定的情况下,我们需要将这种gho文件重新安装回硬盘上,以还原硬盘或分区到先前的状态。下面将介绍gho文件的安装方法。首先,在安装之前,我们需要准备以下工具和材料:实体的gho文件:确保你拥有一份完整的gho文件,它通常以.gho为后缀名,并且包含有备份

上载失败,文件不能为空不一致错误 上载失败,文件不能为空不一致错误 Feb 19, 2024 pm 01:21 PM

Discorde是一个流行的文件共享平台,但用户在上传文件时可能会遇到上传失败、文件不能为空等错误。尽管尚无明确解释,这些错误可能会随机出现,导致无法上传某些文件,甚至影响再次上传之前发送过的文件。修复上载失败,文件不能为空不一致错误上传失败时出现文件不能为空错误会让人感到沮丧。以下是一些建议的解决方法,希望能对您有所帮助。适用于iOS和Android用户验证文件在共享文件之前将其压缩更新/重新安装不一致1]适用于iOS和Android用户在iPhone和Android用户中,对于文件上传失败错

See all articles