English article : http://translate.google.com/translate?sl=zh-CN&tl=en&js=n&prev=_t&hl=zh-CN&ie=UTF-8&u=http://www.cnblogs.com/xiaosan/p/3467953.html
更新的地方:
一. 修复对大数据表导出错误。ps : 理论上依着PHP不出问题的话,可以导出无限量数据, 但不包括表的设计逻辑上的完善考虑。
二. 优化对大数据表的导出速度。ps : 具体参考下面提供的资料
三. 增加个性化输出风格.
四. 多个表导出(详见程序注释)
说明:
一. 导出是单个csv 数据文件, 此类型文件结构简单,非常容易进行转换。但由于是单个文件,在数据庞大的时候,就会有另外的体验问题,如果有网友提的话,那我就修改一下!:)
二. 历史数据文件,在有必要的情况请删除或重命名,那是因为程序会通过此判断跳过已经导出的表。
三. 海量数据导出,应调整相应参数来改善效率,详细请见下面解释.
设计简要:
一)增加了浏览器模式,也就是说,该脚本之前是适合在命令终端运行,但有些情况可能在webshell中无法执行命令,所以就增加了浏览器支持。浏览器导出模式是将导出的数据分批导出,采用了javascript 进行前端持续维持导出状态,所以使用的时候javascript 支持也要打开。
二)浏览器模式中“Restart” 链接可以让程序重新加载,再动态对本脚本参数修改时,因此可以更加方便的响应你的修改。
三)在程序代码中“$dump_max_limit” 变量是控制浏览器模式分批导出最大行数,本身这个值是一个猜想值,默认是50000, 你可以修改成500000. 这都是是没问题的, 导出行数越多就越快,只要web server 接受!适当修改影响到导出整体时间短长。此分批设计是为了迎合无限延伸的海量数据,MySQL创建的MyISAM表默认最大尺寸为4G, 所以在这个层次上再庞大的数据,有了这个设计, 应该也是没问题的。
使用预览:
终端模式
浏览器模式
几个虚设的问题(是关键的问题):
- 问 1. 网上很多程序,为什么你还要写这个程序?
- 答 :像很多提供的导出程序, 根本上出现了很多BUG, 导出也不完整,不经意间漏掉了很多宝贵的数据。包括更新了很多个版本的adminer, 数据遗漏也没有对应提示。
- 问 2. 有支持多种数据库导出的脚本吗?
- 答:这个确实有必要写一个,但我不是专业的程序员,写的也不是很专业,但我喜欢分享,我期望自己以后抽出时间写出来,但不是明天早上!之所以这样,是为了出现BUG,制造出了一些不会写程序朋友而言的麻烦,我非常不愿意这样,所以就要等到有大量时间来进行。(声明:时间不是借口!)
- 问 3. 你是谁?
- 答:和谐小组(H3xIe Security Team)成员. 另外你不管我是谁,你能够在茫茫人海中看到我的博文,说明我的标题很符合你的搜索规范!记住,这是幽默句,听了后,心里请不要浮躁----我们讨论技术好不好!
- 问 4. 我的webshell 不能执行命令,那这个脚本不能用在浏览器上访问导出吗?
- 答:我就想到这个鸡肋的情况,也被这个扯伤了。请在脚本中修改参数“browser_mode”为true , 就可以支持浏览器访问了。
- 问 5. 我该采用哪种模式进行数据导出?可以说一下数据导出经验吗?
- 答:在默认情况最好采用终端命令行导出, 因为速度更快, 更不会被web server 配置所限制。如果碰到无法执行命令的情况,那你就用浏览器模式,同时你碰到数据导出不完整的时候,欢迎在我博客反馈。
附件:
http://files.cnblogs.com/xiaosan/output.zip
预览:
<?php
/**/{}/**//**/{}/**//**/{}/**//**/{}/**//**/{}/**//**/{}/**//**/{}/**//**/{}/**//**/{}/**//**/{}/**/
#### ##### ######## ####### ### ###### ##### #### #### ######### ####### ##### ##### #####
#### ## #### ######### ## ######## ######## ####### ###### ## ######## ######## ###### #####
####### ######### ### #### HeXI3 ####### Security ## #### #### TeAm ## ## ##### ### ## #######
##### ###### ### ##### #### ######### ####(2005 - 2014) #### ###### ####### ###### ##### #####
########## #### ####### ############# ###### ###### ##### ####### ##### ###### ######## ######
/**/{}/**//**/{}/**//**/{}/**//**/{}/**/ {}/**//**/{}/**//**/{}/**//**/{}/**//**/{}/**//**/{}
define('browser_mode', false); /* 注意:如果无法执行命令, 请使用浏览器模式。(改为true 即可) */
define('output_csv_filename', 'data'); /* 便于记忆, 为生成文件名附加以下字符 默认为: data */
define('db_charset', 'gbk'); /* 数据库编码 */
$db_host = 'localhost'; /* 主机名/IP - host */
$db_user = 'test'; /* 数据库用户名 - mysql user */
$db_pass = 'test'; /* 数据库密码 - mysql password */
$db_database = 'test'; /* 数据库名称 - database name */
$table_names = 'test'; /* 可导出多个表,用逗号分隔 */
#
//
/* 提醒 :程序在“浏览器模式”执行中途,不要修改参数,这样会影响到程序正确取值。*/
// 使用介绍(海量数据导出须知,经验浅谈,注意事项,使用说明) : http://www.cnblogs.com/xiaosan/p/3467953.html
#
# 浏览器模式参数
{
/* 浏览器模式 导出支持 参数 */
$limit = 0;
/* 此参数为浏览器模式提供 */
$dump_max_limit = 50000;
$index_table = 0;
$time_start = 0;
$Br_str = '<br />';
}
$msg_arr = array('dump_succ' => 'Operation has completed!',
'exist_table' => "Skip the table \"%s\", this directory already exists \"%s\".",
'not_browser_mode' => "program setting not browser mode",
'exist_dbfile' => "This directory already exists \"%s\"",
'dump_wait' => "Dump Table : \"%s\" waiting...",
'dump_status' => " - Dump Index : %s",
'not_terminal_mode' => "program setting not terminal mode",
'export_table_succ' => "Export %s complete, csvfile = %s. (%f sec)",
'browser_dump_succ' => 'Operation has completed! (%f sec)'
);
/* 系统参数 提示:特殊情况修改,否则默认即可 */
/* ********************************************************** */
ini_set('max_execution_time', 0);
ignore_user_abort();
ini_set('memory_limit','1024M'); // 内存最大空间。
@header("Expires: Mon, 26 Jul 1997 05:00:00 GMT");
@header("Cache-Control: no-cache, must-revalidate");
@header("Pragma: no-cache");
/* ********************************************************* */
if (browser_mode)
{
if (!isset($_SERVER["HTTP_HOST"])) {msg('no_wrap_exit', $msg_arr['not_terminal_mode']);Exit;}
ob_flush();
flush();
if (isset($_GET['limit']))
{
if (is_numeric($_GET['limit']))
{
$int_get_limit = $_GET['limit'];
$limit = $int_get_limit; # limit 限制值累计增加
}
}
echo '<hr />';
echo 'Menu : <a href="'.basename(__FILE__).'"><h1>Restart</h1></a>';
echo '<hr />';
}
else
{
$Br_str = '';
if (isset($_SERVER["HTTP_HOST"])) {echo '<h1>Error : '.$msg_arr['not_browser_mode'].'</h1>';Exit;}
}
$out_csvfile = '';
tables_output(); # run
function tables_output()
{
GLOBAL $table_names, $out_csvfile, $msg_arr;
GLOBAL $index_table, $time_start;
GLOBAL $limit, $dump_max_limit;
$tables_arr = explode(',', $table_names);
if (browser_mode) # 浏览器模式
{
if (isset($_GET['b_time']))
{
$time_start = $_GET['b_time'];
}else $time_start = microtime_float();;
$name = $tables_arr[0]; # 初始化
if (isset($_GET['tb_index']))
{
$index_table = $_GET['tb_index'];
if (isset($tables_arr[$index_table]))
{
$name = $tables_arr[$index_table];
}else
{
$time_end = microtime_float();
$time = $time_end - $time_start;
msg('msg', $msg_arr['browser_dump_succ'], $time);
ls_show();
Exit;
}
}
$out_csvfile = $name.'_'.output_csv_filename.'_'.@date('Y-m-d').'.csv';
if ( ($limit == 0) && is_file($out_csvfile))
{
msg('msg', $msg_arr['exist_table'], $name, $out_csvfile);
$index_table++;
Browser_session_continue($limit, $index_table, '', 4);
}
msg('msg', 'Mode : browser.');
msg('msg', 'Max Dump Limit ('.$dump_max_limit.')');
msg('msg', 'Dump Limit -> '.$limit);
msg('msg', '<font color="red">Please wait...</font>');
export_table($name);
Exit;
}
# 常规模式
foreach($tables_arr as $name)
{
$name = Trim($name);
$out_csvfile = $name.'_'.output_csv_filename.'_'.@date('Y-m-d').'.csv';
if (is_file($out_csvfile))
{
msg('msg', $msg_arr['exist_table'], $name, $out_csvfile);
Continue;
}
msg('msg', $msg_arr['dump_wait'], $name);
$time_start = microtime_float();
export_table($name);
$time_end = microtime_float();
$time = $time_end - $time_start;
msg('err', $msg_arr['export_table_succ'], $name, $out_csvfile, $time);
}
ls_show();
if (!browser_mode) msg('msg', $msg_arr['dump_succ']);
}
function microtime_float()
{
list($usec, $sec) = explode(" ", microtime());
return ((float)$usec + (float)$sec);
}
function msg()
{
GLOBAL $Br_str;
$args = func_get_args();
if ($args[0] == 'no_wrap_exit')
{
$args[0] = 'exit';
$Br_str = '';
}
$paramter_s = '';
if ((!isset($args[2])) && $args[0] == 'msg') {echo '[m] '.$args[1]."\n".$Br_str;return;}
if ((!isset($args[2])) && $args[0] == 'err') {echo "[e] $args[1].\n".$Br_str;}
if ((!isset($args[2])) && $args[0] == 'exit') { die("[e+] $args[1].\n".$Br_str);}
foreach($args as $value)
{
if (($args[0] != $value) && ($args[1] != $value)) $paramter_s .= '\''.$value.'\',';
}
$paramter_s = substr($paramter_s, 0, -1);
if ($args[0] == 'msg')
eval("echo '[m] '.sprintf('$args[1]', ".$paramter_s.").\"\\n\".'".$Br_str."';");
elseif ($args[0] == 'exit')
eval("echo '[e+] '.sprintf('$args[1]', ".$paramter_s.").\"\\n\".'".$Br_str."';Exit;");
elseif ($args[0] == 'err')
eval("echo '[e] '.sprintf('$args[1]', ".$paramter_s.").\"\\n\".'".$Br_str."';");
elseif ($args[0] == 'status')
eval("echo ' '.sprintf('$args[1]', ".$paramter_s.").\"\\n\".'".$Br_str."';");
}
function Browser_session_continue($limit, $table_index = 0, $status = '', $sleep = 1)
{
GLOBAL $time_start;
$query_str = '';
if (isset($limit) && $table_index != 0)
{
$query_str = 'limit='.$limit.'&tb_index='.$table_index;
}
elseif (isset($limit) && $table_index == 0)
{
$query_str = 'limit='.$limit;
}
if (!isset($_GET['b_time'])) $query_str .= '&b_time='.$time_start; else $query_str .= '&b_time='.$_GET['b_time'];
echo '<meta http-equiv="refresh" content="'.$sleep.'; url=?'.$query_str.'" />';
Exit;
}
function ls_show()
{
GLOBAL $Br_str;
$self_dir = @scandir('.');
$ls = '';
if (!is_array($self_dir)) return;
if (count($self_dir) == 0) return;
foreach($self_dir as $now)
{
if (substr($now, -4) == '.csv') $ls .= ' -'.' '.$now.' Size(MB) : '.(filesize($now) / 1024 / 1024)."\n".$Br_str;
}
if ($ls != '')
{
echo '[s] ls *.csv'."\n".$Br_str;
echo $ls.$Br_str;
echo "\n".$Br_str;
}
}
function export_table($table_name)
{
GLOBAL $db_host, $db_user, $db_pass, $db_database, $out_csvfile;
GLOBAL $limit, $index_table;
GLOBAL $Br_str;
GLOBAL $msg_arr;
$sql = @mysql_connect($db_host, $db_user, $db_pass) or msg('exit', mysql_error());
@mysql_select_db($db_database, $sql) or msg('exit', mysql_error());
mysql_query('SET NAMES '.db_charset);
# 常规模式变量
{
$Dump_limit = 500000; // 分批查询
$limit_i = 0;
}
if (browser_mode) # 让程序再脚本限制时间内, 尽量把导出任务做好
{ # 浏览器模式
GLOBAL $dump_max_limit;
$query = "SELECT * FROM `$table_name` ORDER BY 1 DESC LIMIT $limit,$dump_max_limit";
}else # 常规模式
{
msg('Mode : default.');
$query = "SELECT * FROM $table_name ORDER BY 1 DESC LIMIT $limit_i,$Dump_limit";
}
while($export = mysql_query($query))
{
if (!$export)
{
echo("Sql error : ".mysql_error()."\n".$Br_str);return;
}
if (browser_mode)
{
if (!mysql_num_rows($export))
{
$index_table++;
$limit = 0;
Browser_session_continue($limit, $index_table);
}
}
else
{
$limit_i = $limit_i + $Dump_limit;
msg('status', $msg_arr['dump_status'], $limit_i);
$query = "SELECT * FROM $table_name ORDER BY 1 DESC LIMIT $limit_i,$Dump_limit";
if (!mysql_num_rows($export))
{
msg('status', $msg_arr['dump_status'], $limit_i.' (Next)');
return;
}
}
$header = '';
$data = '';
if ( (!isset($_GET['limit'])) || ($limit_i == $Dump_limit)) # 浏览器模式持续导出操作,不需要此过程
{
$fields = mysql_num_fields ($export);
for ( $i = 0; $i < $fields; $i++) { $header .= mysql_field_name($export, $i).','; }
}
$fh = fopen($out_csvfile, 'a+');
if ($header != '')
{
$header = substr($header, 0, -1);
fwrite($fh, $header."\n");
}
while ($row = mysql_fetch_array($export, MYSQL_NUM))
{
$line = '';
foreach($row as $value)
{
if ((!isset($value)) || ($value == "")) { $value = '"",'; }
else
{
$value = str_replace('"', '""', $value );
$value = '"'.$value.'"'.',';
}
$line .= $value;
}
fwrite($fh, substr(Trim($line), 0, -1)."\n");
}
fclose($fh);
if (browser_mode) Break;
}
mysql_close($sql);
if (browser_mode) # 生成持续导出会话。(由浏览器自主完成)
{
$limit = $limit + $dump_max_limit;
Browser_session_continue($limit, $index_table);
}
}
?>