织梦dedecms文件上传功能分析教程

2023/05/05 08:19    5G收录网    已浏览132次

织梦dedecms系统里面的上传功能比较多,例如有友情连接里的logo上传、模块管理里面的上传模块和模块打包、发布新图集里面的上传图片集、发布新软件里的上传本地文件、附件管理里面的上传新文件、文件式管理器里面的文件上传、织梦会员中心里面的软件上传和缩略图等等,在织梦里面的文件上传功能到处都有应用。

  我们分析三个有代表性的,一个是logo上传,这个是一般的应用,但相对复杂一点;另一个是附件管理时面的上传新文件,这二个一个共同特点是上传的文件都要保存到数据库里面,第三个是文件式管理器里面的文件上传功能,这个与我们之前的教程“php文件(单文件和多文件)上传详解教程”讲的一样,都是简单的上传文件,没有用到数据库知识,我们就以这三个为例子,其它的上传跟这三个可以说大同小异。

  要分析这三个文件让传功能,需要对织梦dedecms文件上传处理方式,织梦与我们在php文件(单文件和多文件)上传详解教程里看到的文件上传是很不一样的,毕竟,织梦系统是一比较成熟的cms系统,织梦处理文件上传,不像我们看的教程一样写一个文件就可以了,因为,织梦系统里面用到的文件上传很多,不光是织梦系统,其它系统一定会用到文件上传,只要是网站就会用到文件上传功能。

  织梦是如何处理文件上传的呢?要了解这个我们就要分析一下文件上传的共同点,然后,把这公共部分放在一个文件,然后,其它上传文件就直接引用这个文件就可以了,这样其它每个上传功能如logo上传,图片集上传,软件上传等各自写适合自己的代码,然后,再引用公共部分即可。这种思想正是函数产生的思想,什么是函数简单而言之,就是把公共功能写成一段代码,其它,需要这个功能时直接调用,而本篇讲的文件调用也正是如此。

  织梦dedecms文件上传公共部分在根目录(http://www.dedebase.com这就是根目录,如果你在本地的话根目录就是http://localhost)/include/uploadsafe.inc.php,对就在uploadsafe.inc.php文件里面,这个引用文件功能是:“转换上传的文件相关的变量及安全处理、并引用前台通用的上传函数。”。

  织梦是如何把这个文件配置到要引用它的文件里的呢?首页,要在/include/common.inc.php里面,写一句如下代码:

if($_FILES)
{
    require_once(DEDEINC.'/uploadsafe.inc.php');
}

  这段代码的意思就是,当要上传文件时,就引用文件uploadsafe.inc.php,如果我们打开例如logo上传文件friendlink.add.php你会发现,没有这个uploadsafe.inc.php文件,是的,织梦还配置了一个在根目录/dede/里面的config.php文件,这个是后台根目录/dede/所有php文件共有的,如果你随便打开个文件,你会发现都引用了这个文件,这个config.php不仅仅包括了上传文件,还包括了数据库连接等很多织梦程序所必备的内容。为什么这样包涵来包涵去?还是为了开发方便,试想如果我们把这些文件里的东西都写进friendlink.add.php里面,那文件得多大啊,更重要的是重复写相同的内容,不利于开发。

  我们就拿logo上传为例子这些文件是这样相互包括的:friendlink.add.php 引入config.php,config.php引入common.inc.php,common.inc.php引入uploadsafe.inc.php。这样无论friendlink.add.php如何改变都不影响后面的文件,有利于开发。

  现在我们就要分析一uploadsafe.inc.php是如何转换上传的文件变量即$_FILES及安全设置的。

  打开uploadsafe.inc.php文件,找到foreach($_FILES as $_key=>$_value)这句,这句前面是设置强制哪些文件类型可以上传,并设置了一个数组$keyarr = array('name', 'type', 'tmp_name', 'size');数组$keyarr值正是$_FILES的键,这是为后面进行数组遍历作准备。还对是不是通过编辑器上传作了判断。

  在foreach($_FILES as $_key=>$_value)后面是重点了,通过foreach($_FILES as $_key=>$_value)我们可以得到$_key,这个$_key正是我们表单里面的类型为file的name属性值,这一点非常重要。这句foreach($_FILES as $_key=>$_value)代码就是为了得到这个$_key。为了进一步说明这个$_key我们把$_FILES数组存储的内容分析一下,这里以单个文件上传为例子,多个文件一样。$_FILES存储的内容如下。

  Array
(
    [upmyfile] => Array
        (
            [name] => 1440x900wolf.jpg
            [type] => image/jpeg
            [tmp_name] => D:\APMServ5.2.6\tmp\uploadtemp\php6B1.tmp
            [error] => 0
            [size] => 160666
        )

)

  这句代码foreach($_FILES as $_key=>$_value),正是为了获得这个$_key就是$_FILES里面的upmyfile。接下来我们就要得到name,type,tmp_name,error,size了,我们完全可以再遍历一下$_value,因为$_value又是一个数组,包括这些内容,但是大家想一下,这只是单文件上传,如果是一个多文件上传,是不是比较重杂?是的,织梦想到了一好办法就是先定义一个$Keyarr数组,直接从这个数组里面获取name,type,tmp_name,error,size,既简单,又方快速。

  代码foreach($keyarr as $k)这句正是为了获取name,type,tmp_name,error,size内容。

  接下来这句代码$$_key = $_FILES[$_key]['tmp_name'];是这个uploadsafe.inc.php里面的重中之重,这个代码的等价于$upmyfile = D:\APMServ5.2.6\tmp\uploadtemp\php6B1.tmp;以后,我们在引用uploadsafe.inc.php这个文件的文件里看到类似$upmyfile就等于是$_FILES[$_key]['tmp_name']即上传文件临时文件名,同理

  ${$_key.'_name'} = $_FILES[$_key]['name'] 等价于 $upmyfile_name = $_FILES[$_key]['name'] = 1440x900wolf.jpg。

  ${$_key.'_type'} = $_FILES[$_key]['type'] 等价于 $upmyfile_type = $_FILES[$_key]['type'] = image/jpeg。

  ${$_key.'_size'} = $_FILES[$_key]['size'] 等价于 $upmyfile_size = $_FILES[$_key]['size'] = 160666。

  之所以通过以上方式处理,就是为了在引用uploadsafe.inc.php文件的文件里面,不出现类似$_FILES[$_key]['size']这样长的代码。

  后面的代码就是对文件上传的文件名是不是空,是不是我们允许的文件后缀,文件名是不是少个点进行判断;对文件大小是不是0,如果是我们通过系统函数filesize()获取,如果更加详细的判断还要判断一下,用户上传的内容是不是超出了我们设置的大小,是不是超出了服务器设置的大小,但织梦没有判断在上一个教程“php文件(单文件和多文件)上传详解教程”我们已经作了详细判断。

  最后,对上传的文件类型进行判断,强制使用我们设置的文件类型。

  总结:uploadsafe.inc.php用到的系统函数有是否存在函数etmpty(),通过正则来判断是否匹配preg_match(),检查数组中是否存在某个值in_array(),将字符串转化为小写strtolower(),去除字符串首尾处的空白字符(或者其他字符)trim(),preg_replace — 执行一个正则表达式的搜索和替换等函数,现在我们对uploadsafe.inc.php这个文件已经分析完了。

  一、logo文件上传如下图所示。

织梦dedecms文件上传功能分析教程



  现在我们分析以上传logo文件,logo上传处理文件是friendlink.add.php,对应的模板文件是friendlink.add.htm模板文件我们就不用多说了,都是一些html代码,我们只要找到模板中这二句(简化后):

  网站Logo: <input name="logo" type="text" id="logo"/>
   
  上传Logo: <input name="logoimg" type="file" id="logoimg" />

  我们打开friendlink.add.php文件,程序首先用if(is_uploaded_file($logoimg))判断一下上传的文件是不是通过http post上传的?如果是说明是通过本地上传的logo,也就是上面的<input name="logoimg" type="file" id="logoimg" />这个选择框上传的,如果不是则是通过<input name="logo" type="text" id="logo"/>,直接在这个文件框写上的logo地址。

  若if(is_uploaded_file($logoimg))成立则程序向下执行,注意这句代码里面的$logoimg指什么?正常的文件框的话,当然是通过$_POLST获得的,例如上面二行中的网站Logo:就是通过$_POST获取值,而上传logo即$logoimg则不是通过$_POST获得,而是通过$_FILES获得的,也就是我们在uploadsafe.inc.php里面分析的$upmyfile = D:\APMServ5.2.6\tmp\uploadtemp\php6B1.tmp,只不过现在的$upmyfile变成了$logoimg即,$logoimg = D:\APMServ5.2.6\tmp\uploadtemp\php6B1.tmp。

  如果你看明白了这点,那么,下面的$logoimg_name、$logoimg_type、$logoimg_size分别对应上传文件的文件名、上传文件的类型、上传文件的大小了。那下面的代码就比较容易了,这是难点也是重点。

  代码$names = split("\.", $logoimg_name);意思是把上传的文件名通过正则匹配,模式是通过点分成二部分,存放在数组$name中。

  我给织梦挑一个不算错误:函数preg_split()比split()快,而explode()在这三个中运行速度最快,前二个需要正则表达式,最后这个explode()则使用字符串分割。他们三个都返回数组,所以,这里应当换上explode()最好了,我们做个测试一下,看看返回结果。

   $name = "1440x900wolf.jpg";
 $a = split("\.", $name);
 $b = split("\.", $name);
 $c = explode(".",$name);

 print_r($a);

 echo "<br />";

 print_r($b);

 echo "<br />";

 print_r($c);

  这段代码返回的结果是:

Array ( [0] => 1440x900wolf [1] => jpg )
Array ( [0] => 1440x900wolf [1] => jpg )
Array ( [0] => 1440x900wolf [1] => jpg )

  这充分说明了返回的结果是一样的,所以,建议大家能用explode()就用这个。

  回到friendlink.add.php文件里,$shortname = ".".$names[count($names)-1];这句里面的$names[count($names)-1]获取文件类型名,例如上面的例子jpg。整句就得到了.jpg。

  下面这个判断语句用来判断,我们上传的文件名是不是我们允许的,是不是jpg,gif,png,若不是则直接改后缀名为.gif。
    if(!preg_match("#(jpg|gif|png)$#", $shortname))
        {
            $shortname = '.gif';
        }

  下面这句,是对文件名进行重新命名,这样是为了防止,不同的用户上传相同的名子后,前面的会被后面的覆盖掉。

  $filename = MyDate("ymdHis", time()).mt_rand(1000,9999).$shortname;

  下面这句是设置文件上传路径,其中$cfg_medias_dir附件上传路径,在common.inc.php已经定义。

  $imgurl = $cfg_medias_dir."/flink";

  下面这句判断是不是目录,不是则建立,其中$cfg_basedir是根目录,$cfg_dir_purview是权限,在common.inc.php已经定义。

          if(!is_dir($cfg_basedir.$imgurl))
        {
            MkdirAll($cfg_basedir.$imgurl, $cfg_dir_purview);
            CloseFtp();
        }

  下面这句是要把临时文件转移到对应路径和文件名。

  $imgurl = $imgurl."/".$filename;

  移动文件:下面这个函数是重点,我们上面所做的工作完全就是为这个文件转移函数服务的。

   move_uploaded_file($logoimg,$cfg_basedir.$imgurl)
 
  删除临时文件@unlink($logoimg);@这个抑制符意思是不显示错误。

      //强制检测用户友情链接分类是否数据结构不符
    if(empty($typeid) || preg_match("#[^0-9]#", $typeid))
    {
        $typeid = 0;
        $dsql->ExecuteNoneQuery("ALTER TABLE `dede_flinktype` CHANGE `ID` `id` MEDIUMINT( 8 ) UNSIGNED DEFAULT NULL AUTO_INCREMENT; ");
    }
 
  这句看似不重要,实际上这句非常重要,如果这个$typeid错误,直接导致,logo出问题。

  现在就要把友情链接添加到数据库了,请注意我们上传的logo是把路径保存到数据库里面的,而不是把图片保存到数据库里面,有没有把图片保存到数据库里面的?有,以前听老师讲课时,讲到过,但是极少,一般都是保存图片路径的,这样程序只需要查询一下数据库就知道图片在哪里了,目前,本人从没有见过图片保存到数据库里的。

  目前为止,我们已经把logo上传分析完了。


  二、附件管理——>上传文件如下图所示。


织梦dedecms文件上传功能分析教程


  与logo文件上传功能相比,这个文件上传功能需要标题,附加参数,还有一个最大的不同就是,这个功能可以批量上传文件,而且不仅仅是图片,还有flash,音频/视频等。多文件上传,与单文件上传没有多大区别,只不过在单文件上

传的基础上曾加个循环而已。

  重点看这三句代码

  $filesize = ${"upfile".$i."_size"};
  $upfile_type = ${"upfile".$i."_type"};
  $upfile_name = ${"upfile".$i."_name"};

  这不正是我们在upoadsafe.inc.php里面重点分析的吗,在上传logo功能里我们也作了分析,这里不再作过多分析。从代码for($i=0; $i<=40; $i++)开里,当条件if(isset(${"upfile".$i}) && is_uploaded_file(${"upfile".$i}))成立,即已经上传了文件并且是通过http post上传的,那么,程序就判断上传文件类型在不在我们前面定义的二个数组$sparr_image,$sparr_flash里面,如果在则设置存储路径$savePath,若不存在就建立路径,若是非flash类型则打上水印,接下来没有问题,就把数据插入到数据库表dede_uploads,在这个处理程序中用到了取得图像大小的函数getImagesize(),这个函数,不仅可以获得图像的大小,还有高、宽、字节大小、mime类型,为什么要用这个函数?
因为,我们后面的保存数据到数据库表时,要保存高和宽。

  三、文件式管理器 ——>上传文件分析如下图所示。


织梦dedecms文件上传功能分析教程



  文件file_manage_control.php就是对上传的文件进行处理的程序,这个更简单,既不用把上传的文件路径插入到数据库,也不用作过多的判断,代码非常简单。它与logo相比多了多个文件上传,与附件管理文上传,少了不需要保存路径,连上传的文件名都不用改,直接用if(!file_exists($cfg_basedir.$activepath."/".$upfile_name))来判断是不是上传的文件名与已经有的文件名重名,结束。

  总结:我们总结一下,如果让我们自己开发一个上传文件功能,如何开发?

  开发上传文件程序步骤:

   1.设置限制文件类型,一般用数组来设定。
 
   2.判断文件大小,判断错误友好提示信息。

   3.处理$_FILES文件里面的内容,像织梦那样专门做个程序文件upoadsafe.inc.php。

   4.用函数is_uploaded_file()判断上传的临时文件是不是存在,存放则移动文件,用到的函数是move_uploaded_file()。

   5.若要保存到数据库,则插入数据库表。

   6.多个文件处理,这个要用到js,或jquery。使上传效果更酷。