Discuz插件处理大型dot源码

上篇,本地sgf棋谱转换成dot源码后,本地可以生成图片。对于这个然而并没有什卵用的东西,我又想在Discuz论坛显示dot源码的棋谱,但是Discuz论坛插件不能正确处理,接下来为了能让DZ插件能处理大型dot源码,通宵折腾,保持一个坐姿,完事了差点没站起来,第二天髋关节疼一天,真是no zuo no die。

API增加POST方式

大型dot源码请求GraphViz API,报错如下

Request-URI Too Large

The requested URL's length exceeds the capacity limit for this server.
Apache/2.2.22 (Debian) Server at server1.tecbbs.com Port 80

很明显,GET方式已经不能处理像棋谱这么大型的dot源码了。之前实现的GraphViz API只接受GET方式的参数,看来需要改改了。

<?php
/**
 * graphviz api
 *
 * $Id api.php  tecbbs@qq.com 2015-6-8 $
 **/
$host = $_SERVER['HTTP_HOST'];
$siteurl = "http://" . $host . "/gv/"; 
$cht = "dot";
$chl = "graph {fontname=\"SimSun\";node[shape=box];a[label=\"nothing to do~ 参数错误\"];}";
$error = "error.png";
$engine = array("dot", "neato", "fdp", "sfdp", "twopi", "circo");
$imgtype = "png"; 
$imgtype_arr = array("png", "gif", "jpeg");

if(isset($_GET['cht']) && isset($_GET['chl']) && isset($_GET['chof'])) {
	$cht = $_GET['cht'];
	$chl = $_GET['chl'];
	$imgtype = $_GET['chof'];
} 
if(isset($_POST['cht']) && isset($_POST['chl']) && isset($_POST['chof'])) {
	$cht = $_POST['cht'];
	$chl = urldecode($_POST['chl']);
	$imgtype = $_POST['chof'];
}
 
$arr = explode(':', $cht);
if(!array_key_exists("1", $arr)) {
	$cht = "dot";
} else {
	$cht = $arr['1'];
}
if(!in_array($cht, $engine)) {
	$cht = "dot";
}

if(!in_array($imgtype, $imgtype_arr)) {
	$imgtype = "png";
}

$gvname = md5($chl) . $cht;
$gvpath = "./gv/" . $gvname . ".gv";
$imgpath = "img/" . $gvname . "." .  $imgtype;


$file = fopen("$gvpath", "w");
$encode = mb_detect_encoding($chl, array("ASCII","UTF-8","GB2312", "GBK", "EUC-CN"));

if($encode != "UTF-8") { 
	$chl = iconv("$encode", "UTF-8", $chl);
}

$chl = str_replace(">", ">", $chl);
$chl = str_replace("<", "<", $chl);
$chl = str_replace("&quot;", "\"", $chl);
fwrite($file, "$chl");
fclose($file);
if(!file_exists($imgpath)) {
	exec("$cht -T$imgtype $gvpath -o $imgpath",$out, $ret);
	if($ret != 0) {
		$imgpath = $error;		
		$imgtype = "png";
	}
}

$imgstrout = "image$imgtype(imagecreatefrom$imgtype('$imgpath'));";
if(isset($_GET['cht']) && isset($_GET['chl'])) {
	header("Content-Type: image/$imgtype; charset=UTF-8");
	eval($imgstrout);
}
?>

插件中应用POST请求

有时候写代码真的需要运气。

curl命令请求一下,确实可以成功,然而,诡异的事情是插件里面的POST请求总是没有任何结果,就像POST函数没被执行一样,在POST函数里加echo,发现函数确实被调用了,API服务器上看日志,却没有收到插件发来的请求,curl命令发送的请求是都可以看到的

192.168.60.1 - - [20/Jun/2015:03:28:12 +0800] "POST /gv/api.php HTTP/1.1" 200 866177 "-" "curl/7.33.0" -
192.168.60.1 - - [20/Jun/2015:03:28:44 +0800] "POST /gv/api.php HTTP/1.1" 200 5655 "-" "curl/7.33.0" -
192.168.60.1 - - [20/Jun/2015:03:28:55 +0800] "POST /gv/api.php HTTP/1.1" 200 880208 "-" "curl/7.33.0" -

然后就迷茫了,运气不好,首先陷入了错误的方向,去排查编码问题。。当然没有任何结果。然后才想看http返回值

$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);

返回值为0,看来curl请求还是有问题,接下来是要找到php curl调试的方法(思而不学则殆啊,不学就用运气怎么能好==),最好能看到类似curl命令-I选项那样的信息。步入正轨,搜到curl_error[1],打印结果:

$data = curl_exec ($ch);
print_r(curl_error($ch));

结果:
Protocol http not supported or disabled in libcurl

这下好办了,直接搜 Protocol http not supported or disabled in libcurl

最后用PHP Curl 模拟访问HTTPS ,总是得到 Protocol https not supported or disabled in libcurl 错误,奇怪了,找了很多资料,有人说没有开启SSL,各种办法都弄过了,最后发现网址前面有一个空格,去掉就正常了,晕菜了[2]

去检查后台登记的api链接,果然是这样

Protocol http not supported or disabled in libcurl

Protocol http not supported or disabled in libcurl

改代码,果断trim url

//GET长度过大时改用post,直接调用服务端md5文件名图片
if(strlen($texcode) > 40000) {
	$md5img = md5(urldecode($texcode));
	$id = $md5img . $engine;
	$img_path = $host . "img/";
	$img_url = $img_path . $id . "." . $gvOutFormat;
	$gv_md5 = $host . "gv/" . $id . ".gv";

	$post_data = array();
	$post_data['cht'] = $cht;
	$post_data['chl'] = $texcode;
	$post_data['chof'] = $gvOutFormat;

	$this->curlPost($post_data, trim($mygvApiUrl));   //trim防止url开头空格
	$img_alt = $img_url;
	$img = $img_url;
}
include template('zxsq_bbstex:vizapi');
return trim($vizapi);

curl请求等待问题

其实一开始设计GraphViz API返回的是图片URL,于是Discuz调用的时候要等待页面所有的dot源码都获得相应图片URL后才加载页面,慢的无法忍受,后来改成在模板里直接进行API调用,即img src为API链接加GET参数方式(类似Google Charts API),速度明显提升。但是现在大型源码必须用POST方式了,模板里img src不好写了,需要设计一种客户端不等待服务端图片生成完成就开始加载页面的方式,img src只能有客户端来生成了,这样就要求服务器端也有相同的图片文件生成方式以便调用:

协商生成img url

协商生成img url

这种方式比最初返回图片URL的方式快,然后仍然不够快,因为curl客户端要等待至少1s,当页面有多个大型dot源码需要渲染时,就有的等了。后续改进可以考虑改成完全异步的,只管给API发画图请求,参考[3]的讨论,可以考虑使用fscokopen()函数。

最后给一个Discuz演示地址: http://www.tecbbs.com/forum.php?mod=viewthread&tid=6739

参考资料

[1]. http://www.php.net/curl_error
[2]. http://www.cnblogs.com/hubj/p/3417944.html
[3]. http://www.dewen.io/q/3970/

 

发表回复

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