Web 技术研究所

我一直坚信着,Web 将会成为未来应用程序的主流

让度娘为咱做图片网络备份

  一个网站的图片数量总是会随着时间的推移而增加,对网站程序备份很容易,因为程序通常都很小,1M就已经是大程序了。而图片备份就是备份中最麻烦的一个环节。大公司使用CDN技术实现网络负载平衡的同时,实际上就在网络上做了许多次备份,而小公司或个人自己搞CDN显然不现实,也没必要。那么我们为何不利用大公司的设备为自己做备份呢?这听上去像是去大公司抢劫一样有点惊悚,但是这完全是可以实现的。现在就拿度娘开刀来实现一下。不过毕竟是基于百度程序的接口,如果哪天它的程序改版了我们的程序也要跟着改,但是这个方法肯定可以利用起来。
  首先是思路:在自己网站上传图片的时候同时传一份到大公司的服务器上,具体方法很多,大公司的很多业务都是需要外部图片上传接口的。比如百度贴吧发帖就可以传图片,我们只要找到这个接口就可以把图片传到百度的服务器上了。不同的公司提供的接口都不同,我最初的想法是直接让客户端向百度传图片,再把返回的图片地址传到我的服务器上。但是由于百度传图片的接口没有提供JSONP,也就没有跨域访问权限,所以客户端无法获取到图片上传后的返回地址,因此放弃了这个方法。我又琢磨着其它方法,如果大公司的服务器储存图片的URL是使用图片的MD5,我们就可以在客户端把图片上传到大公司的服务器上,并且自己在客户端计算图片的MD5以得出URL。但是度娘这傲娇货没有使用MD5作为文件名,在百度上传图片时,即使是相同的图片每次的URL也会不同。我不明白百度的工程师这样做是什么用意,不过这样就不能使用这个方法了。虽然这两个方法对度娘行不通,但是如果是用其它公司的接口也许可以用上,这两个方法是最简单的。
  既然客户端无法直接把图片上传到百度的服务器上,那就只好让服务器端来做了。虽然会占用一些带宽,但是这和每天去备份图片浪费的带宽比起来要划算的多。这个方法是可行的,但是如果按照传统的思路,客户端上传图片的时间就会变成原来的两倍。因为图片需要传两次,一次是客户端到服务器,然后是服务器到百度。解决这个问题也很简单我们有短连接技术,它就可以让客户端在把图片传到自己的服务器并取得图片在自己服务器上的URL后马上断开连接,服务器在客户端连接断开后再把图片传到百度的服务器上。这就可以避免客户端的等待时间变成两倍。我们还需要在数据库中建一张表来存放图片在自己服务器上的URL和在百度服务器上的URL。当服务器把图片上传到百度后,可以在这个表中添加一条记录,这样即使把自己服务器上的图片删除了也可以通过这个表去百度上把图片下载回来。下面是我的代码片段
ob_clean();

//使用短连接需要调用的
ignore_user_abort();
set_time_limit(0);

//验证并保存上传的图片
if($f=$_FILES['file']){
  $p=dirname(__FILE__);
  if(file_exists($n=$f['tmp_name'])){
    //打开文件取文件头
    $f=fopen($n,"rb"); 
    $h=unpack("H*",fread($f,4));
    $h=strtoupper($h[1]);
    fclose($f);
    //判断文件类型生成文件
    $s=array(
      'FFD8FFE0'=>'jpg',
      '47494638'=>'gif',
      '89504E47'=>'png'
    );
    if($extname=$s[$h]){
      $pic=md5_file($n).'.'.$extname;
      copy($n,$p.'\\'.$pic);
    }else $err='图片可能已损坏!';
  }else $err='服务器权限设置错误!';
}else $err='上传失败!';

//返回JSON
echo '{"err":"'.$err.'","pic":"'.$pic.'"}';

//输出数据并断开客户端连接
$data=ob_get_contents();
ob_end_clean();
header('Content-Length:'.strlen($data));
header('Connection:Close');
echo $data;
flush();

//后续过程
//如果图片从客户端上传成功则执行
if(!$err){
  //尝试上传到百度服务器
  try{
    $ci=curl_init('http://tieba.baidu.com/dc/common/imgtbs');
    curl_setopt_array($ci,array(
      CURLOPT_RETURNTRANSFER=>1,
      CURLOPT_FOLLOWLOCATION=>1,
      CURLOPT_COOKIE=>'BAIDUID=BaiduIsAGreatSB'
    ));
    $data=curl_exec($ci);
    curl_close($ci);
    $data=json_decode($data);
    $tbs=$data->data->tbs;
    $ci=curl_init('http://upload.tieba.baidu.com/upload/pic?tbs='.$tbs);
    curl_setopt_array($ci,array(
      CURLOPT_RETURNTRANSFER=>1,CURLOPT_POST=>1,
      CURLOPT_POSTFIELDS=>array('file'=>'@'.realpath($pic))
    ));
    $result=json_decode(curl_exec($ci));
    curl_close($ci);
    $bak=$result->info->pic_id_encode.'.'.$extname;
  }catch(Exception $e){
    $bak='Error';
  };
  //写入数据库
  mysql_query("
    insert into picture(filename,backup)
    values('$pic','$bak')
    on duplicate key update
      backup=values(backup)
  ");
};
  这是上传图片的服务器代码,前面是处理客户端上传的图片,后面是把图片上传到百度。前面的过程就不用多说了,处理上传图片的代码爱怎么写怎么写,没有特别的规定。传到百度的代码就有点死,度娘很傲娇的,必须要先请求一次tbs数据才能上传成功。不过幸好不用登录,要登录的就更麻烦。其它公司的图片上传接口我就懒的研究了,目前这个接口能用就行了。如果以后百度修改了自己的图片上传接口,我也懒的更新这篇文章了,当你看到这篇文章并且这个接口已经失效时,请自己去抓包研究= =。。
  也许很多人会担心,如果哪一天百度把自己备份的图片都删除了呢?我觉得这个概率很小,不过确实有这样的可能性,毕竟我使用的这个图片接口是百度贴吧的公用接口,里面的图片很杂。如果想做安全点,就使用相册服务。相册服务的接口是私有的,一般需要登录,实现的代码也会比这个复杂。但是相册服务的图片就比较安全,因为一些隐私保密方面的原因,百度这样的大公司不会去动个人相册的图片,更没理由去删除。所以如果当心安全性就使用这种模式。
  还记得多态文件吗?百度贴吧上经常都有一些图片下载下来把扩展名改为rar就可以作为压缩包打开。这样我们不仅可以在百度上备份图片,还可以备份其它类型的文件。我不建议这样做,删除图片的概率固然小,但是重置图片的概率可不小。如果哪天因为图片可以储存压缩包这个问题引起一些轰动性的事件,百度就很有可能把所有图片中带的压缩包都处理掉。用百度备份图片算是一种手段,而图片包含压缩包算是一种BUG。BUG随时可能被修复,而手段只要手没断都可以用。总之,好自为之吧。
  另外,新年快乐。。。
网名:
3.80.55.*
电子邮箱:
仅用于接收通知
提交 悄悄的告诉你,Ctrl+Enter 可以提交哦
神奇海螺
[查看全部主题]
各类Web技术问题讨论区
发起新主题
本模块采用即时聊天邮件通知的模式
让钛合金F5成为历史吧!
次碳酸钴的技术博客,文章原创,转载请保留原文链接 ^_^