HTTP的header请求首部讲解顿号,php从mysql导出数据到excel表格实现

  • 内容
  • 评论
  • 相关

作为一个web开发者,每天都在使用网路请求,那么我们的网路请求是如何发出和接收的呢,我们来简单的探讨下。鉴于HTTP的复杂性(如果详细透彻的讲解,需要写一本厚厚的书哦),我们只讲请求首部,也是就是所谓的HEAD。

HTTP是一套可靠的数据传输的协议,我们平时访问的web内容大都是存储在web服务器上,这些服务器使用的是HTTP协议,因此我们也称这些服务器为HTTP服务器。当我们在客户端发起HTTP请求的时候,服务器会响应这个请求并返回数据。

那么很多同学要问了,你要讲的HEAD和我的请求有什么直接关系吗?我平时既没有看到过这个HEAD,也没有接到过这个HEAD啦。okay,确实,我们平时在编码程式的时候很少会注意或者直接注意到这个HEAD,那么这个HEAD到底是何须东西呢。那我们先举个例子,当你写信给一个好盆友的时候,你会在信封上写清楚收信地址,收信人,邮政编码等等。这个我们称之为报头(取自电子邮件的发送),那么根据这个信封,邮递员才能快速准确的将你的信件投递给你的好友,同时你的好友收到了邮件才能回复邮件给你哦。那么我们在HTTP中的这个首部也是这个意思,你在发起请求的时候会告诉服务器一些内容,比如我发送的是什么类型的东西,日期和时间,我要接受什么类型的数据等等哦。

使用firefox的小虫子,也就是firebug可以查看到一些首部的信息,例如一个请求头

GET /archives/135 HTTP/1.1
Host: www.xiaotiejiang.com
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:16.0) Gecko/20100101 Firefox/16.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-cn,zh;q=0.8,en-us;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
Connection: keep-alive
Referer: http://www.xiaotiejiang.com/

一个响应头部:

HTTP/1.1 200 OK
Date: Thu, 22 Nov 2012 04:35:56 GMT
Server: Apache/2.2.3 (CentOS)
X-Powered-By: PHP/5.2.17
X-Pingback: http://www.xiaotiejiang.com/xmlrpc.php
Link: <http://www.xiaotiejiang.com/?p=135>; rel=shortlink
Connection: close
Transfer-Encoding: chunked
Content-Type: text/html; charset=UTF-8

在不主动修改的情况下,这些首部都是默认发送和接受的,举例来讲一下这个首部,其中Referer表示来路页面,Host表示接受请求的服务器的主机名和端口号,HTTP/1.1 200 OK表示请求协议版本和请求状态。其余的不多说,我们之前有一篇关于XFF的文章,讲到了伪造客户端的IP,其中就是用到了这个header,我们会现在请求的时候发送一个原始头部告诉服务器,我的IP是多少。我们可以这样来发送一个原始的头:

header('Client-IP: 123.45.67.89');

header('Host: http://wwww.xiaotiejiang.om');

header('Referer: http://www.ifromwhere.com');

很多网站会过滤ip 的功能,国内外有很多的online的IP库,效率蛮好,在此使用国内的新浪的,如下:

$ch = curl_init();
ob_start();
curl_setopt($ch, CURLOPT_URL, 'http://int.dpool.sina.com.cn/iplookup/iplookup.php');
//curl_setopt($ch, CURLOPT_HEADER, 0);
//curl_setopt($ch, CURLOPT_NOBODY, 1);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$ipInfo = curl_exec($ch);

这样子你就会在发送HTTP请求的时候告诉了服务器这些信息,至于伪造IP访问,涉及到的东西还是很多的,在次不细说

那讲到这里,很多同学会说,咦,我用过一个东西:

header('Location: http://gotowhere');

那这个Location的首部选项是告诉客户端实体资源实际上位于何处,也就是平时说的重定向。如果头部有多个选项,需要使用分号隔开。

header('refresh:3;url=http://www.xiaotiejiang.com');

以上表示3秒钟后自动跳转到指定的url。说到此还要说明的是,除了php和其他语言程序中,在html的base标签中也可以实现同样的效果。例如

<base href='http://www.baidu.com' />;

上面这个标签会实现改变当前web的host,这个标签告诉浏览器,我不再使用当前wen文档的url,而是使用此标签定义的主机名。这个标签在一些转移网站文件等处会用到。同时header中的其他一些选项也可以在html中的标签中得到实现,例如禁止网页从缓存中读取文件:


关于网页的首部选项Cache-Contrl、Pragma等,可以自己在页面中实际的发送一下试试。

在这里我们简单的将一个小例子,例如你的网站数据量很大,服务器承受着很大的访问压力,这个时候你就想减少http请求,那么在适当的情况下,我们可以使用这个header请求来告诉浏览器我们的行为,我们这里着重使用三个参数
Cache-Control,Pargma,Expires
从字面意思来看,我们主要是设置这个三个参数的本地缓存,也就是你的浏览器的缓存文件夹中的文件保存的时间,在php中我们可以这样实现:

<?php
    header('Cache-Control: public,max-age=86400');
    header('Pargma: cache');
    $offset = 24*60*60;
    $expires = "Expires: ".  gmdate('D, d M Y H:i:s',time()+$offset)." GMT";
    header($expires);
?>

这里我们设置了过期时间为24小时。这个时候我们查看我们的请求头信息如下:

HTTP/1.1 200 OK
Date: Wed, 16 Jan 2013 09:16:55 GMT
Server: Apache/2.2.22 (Win64) PHP/5.3.13
X-Powered-By: PHP/5.3.13
Cache-Control: public,max-age=86400
Pargma: cache
Expires: Thu, 17 Jan 2013 09:16:55 GMT
Content-Length: 127
Keep-Alive: timeout=5, max=100
Connection: Keep-Alive
Content-Type: text/html

也就是说如果用户访问我们的网页,在24小时之内再次访问都会直接访问本地,这个时侯如果你去查看你的图片信息,你其实会看到,图片还是没有缓存的,那么我们如何来将图片做一下本地缓存呢,这个时候我们使用apache的模块来实现,将以下代码写入.htaccess文件:

<IfModule mod_headers.c>
<FilesMatch ".(gif|jpg|jpeg|png|ico)$">
Header set Cache-Control "max-age=604800"
</FilesMatch>

当然你要开启headers模块的支持。
那我们继续我们的header,在我们的程序中,不光要展示文件,很多时候还需要下载或者保存为特定格式的文件,例如我需要将数据保存为excel文件并且导入到本地,这个时候我们在发送请求的时候就需要告诉服务器这个文件的展示和存储方式喽,例如:

header('Content-type:application/vdn.ms-excel');
header('Content-Disposition:attachment;filename=myexcel.xls');
echo "11\t";
echo "12\t\n";
echo "21\t";
echo "22\t\n";

下面给出一个完从数据库查询数据完成导出到excel的代码,这个类库的最新版本可以通过svn获取:https://export-data-to-excel.googlecode.com/svn/trunk,文件地址:https://code.google.com/p/export-data-to-excel/source/browse/trunk/excel.php

<?php
/*
 * class ImporExcel
 * author gaoxueping 
 * version 2.0
 * 支持单表自动查询和自定义查询
 */
    class ImporExcel{
        private $host;
        private $username;
        private $password;
        private $dbname;
        protected $conn;

        function __construct($dbinfo) {
            $this->dbinfo = func_get_arg(0);
            $this->host= $this->dbinfo['host'];
            $this->username = $this->dbinfo['user'];
            $this->password = $this->dbinfo['pass'];
            $this->dbname = $this->dbinfo['db'];
            $this->conn = new mysqli($this->host,$this->username,$this->password,$this->dbname);
            $this->conn->connect_errno?die($mysqli->error):"";
        }

        public function import($table,array $condition,array $fileds,array $title,$query = ''){
            if(trim($table)== '' || trim($table) == ''){
                die('table or query param cant empty all');
            }
            header('Content-type:application/vdn.ms-excel');
            header('Content-Disposition:attachment;filename=myexcel.xls');
            if(trim($query) == ''){
                $filed = join(",", $fileds);
                if(count($condition)>0){
                    foreach($condition as $key => $value){
                        $where[]= $key."=".$value;
                    }
                    $where = " WHERE ".join(' AND ', $where);
                }else{
                    $where = '';
                }
                $sql = trim("SELECT ".$filed." FROM ".$table.$where);
            }else{
                $sql = $query;
            }
            $result = $this->conn->query($sql);
            for($i=0;$i<count($title);$i++){
                $colum = ($i==(count($title)-1))?($title[$i]."\t\n"):($title[$i]."\t");
                echo iconv('utf-8', 'gb2312', $colum);
            }
            while($rows = $result->fetch_array()){
                for($i=0;$i<count($fileds);$i++){
                    $param = ($i==(count($fileds)-1))?($rows[$fileds[$i]]."\t\n"):($rows[$fileds[$i]]."\t");
                    echo $param;
                }
            }
    }
}
    $import = new ImporExcel(array('host'=>'localhost','user'=>'root','pass'=>'pass','db'=>'databse'));
    $sql = "SELECT p.products_model,b.customers_basket_quantity,d.products_name,p.products_price,p.products_price*b.customers_basket_quantity AS sum_price,b.customers_basket_date_added FROM `zen_customers_basket` b JOIN `zen_products` p ON b.products_id = p.products_id JOIN zen_products_description d ON b.products_id = d.products_id WHERE b.customers_id = 28500 GROUP BY b.products_id";
    $import->import('zen_admin',array(),array('products_model','customers_basket_quantity','products_name','products_price','sum_price','customers_basket_date_added'),array('产品类型','数量','产品名称','单价','总价','添加日期'),$sql);
//    $sql = "SELECT * FROM `zen_customers_basket` b ,`zen_products` p ,`zen_products_description` d WHERE b.products_id = p.products_id AND b.products_id = d.products_id AND b.customers_id = 3745";
?>

这两个首部我想大家根据字面意思已经知道了,那么那么多的首部选项大家不一定全部自己记住,用到的时候可以去查询哦。
当然,从excel中读取也是很简单的,和平时读取文件一样,给出一个简单的例子,有这样一个excel文件:

<?php
    $handle = fopen('file.csv','r');
    $j = 0;
    while($data = fgetcsv($handle, 1000, ',')){
        $size = count($data);
        for($i = 0;$i < $size; $i++){
            if($i%2 == 0){
                $numbers[$j][] = $data[$i];
            }
        }
        $j++;
    }
    array_shift($numbers);
    foreach($numbers as $key => $value){
        if($numbers[$key][0] != ''){
            $string = join(',', $numbers[$key]);
            echo $string.'<br />';
        }
    }
?>

根据以上的图片中显示的excel格式,如果我们需要这样的格式输出的话"工号-姓名",则可以这样实现:

<?php
    $handle = fopen('file.csv','r');
    $j = 0;
    while($data = fgetcsv($handle, 1000, ',')){
        $size = count($data);
        //循环遍历的间隔为2
        for($i = 0;$i < $size-1;$i += 2){
            $numbers[$j][] = $data[$i].'-'.$data[$i+1];
        }
        $j++;
    }

    array_shift($numbers);
    foreach($numbers as $key => $value){
        //去掉空白的行
        if($numbers[$key][0] != '-'){
            $string = join(',', $numbers[$key]);
            echo $string.'<br />';
        }
    }
?>

再顺手举一个小例子,就是再给图片打水印的时候:

header('Content-type:image/png');
$im = imagecreatefrompng('/var/www/test/image.png');
$srcIm = imagecreatefrompng('/var/www/test/name.png');
imagecopymerge($im, $srcIm, 20, 20, 0, 0, imagesx($srcIm), imagesy($srcIm), 50);
imagepng($im);

这个首部的意思我想大家也都知道了吧。

总而言之,这个http的首部是一个很灵活的东西,服务器会根据我们发送的header请求以不同方式和形式来展示和存储web文件。

那么最后我们再补充几个http的请求方法和状态码,http的请求大体有:GET,POST,HEADER,TRACE,PUT,OPTIONS,DELETE等。

状态码我们答题说些一下

100-199   信息提示码

200-299   成功状态码

300-399   重定向状态码

400-499   客户端状态码

500-599  服务器端错误状态码

那么请求过程中如果出现时间漫长的时候,我们可以简单写一个加载进度条,至于功能实现,还要看具体问题喽;

<script src="jquery.js"></script>
<?php
//echo '<label id="loading" style="border:1px solid red;display:block;width:100px;height:15px"><span id="bar" style="display:block;background:red;wheight:15px;"></span></label>';exit;
    echo str_pad(' ',5000);
    echo '<label id="loading" style="border:1px solid red;display:block;width:90px;height:15px;"><span id="bar" style="display:block;background:red;height:15px;"></span></label>';
    for($i=1;$i<10;$i++){
//        echo $i;
?>
<script>
    var percentage = <?php echo $i; ?>;
    $('#bar').width(percentage*10);
//    $('#loading').append(percentage);
</script>
<?php
        ob_flush();
        flush();
        sleep(1);
    }
?>

评论

0条评论

发表评论

您的电子邮箱地址不会被公开。 必填项已用*标注