这是我用我的工作我的方式来解决的代码。
public function indexAction()
{
//id3 options
$options = array("version" => 3.0, "encoding" => Zend_Media_Id3_Encoding::ISO88591, "compat" => true);
//path to collection
$path = APPLICATION_PATH . '/../public/Media/Music/';//Currently Approx 2000 files
//inner iterator
$dir = new RecursiveDirectoryIterator($path, RecursiveDirectoryIterator::SKIP_DOTS);
//iterator
$iterator = new RecursiveIteratorIterator($dir, RecursiveIteratorIterator::SELF_FIRST);
foreach ($iterator as $file) {
if (!$file->isDir() && $file->getExtension() === 'mp3') {
//real path to mp3 file
$filePath = $file->getRealPath();
Zend_Debug::dump($filePath);//current results: accepted path no errors
$id3 = new Zend_Media_Id3v2($filePath, $options);
foreach ($id3->getFramesByIdentifier("T*") as $frame) {
$data[$frame->identifier] = $frame->text;
}
Zend_Debug::dump($data);//currently can scan the whole collection without timing out, but APIC data not being processed.
}
}
}
问题:处理的MP3文件的文件系统的多个目录中。 提取ID3标签数据到数据库中(3个表),并从该标签中的封面图像提取到一个单独的文件中。
我能处理的实际提取和数据处理。 我的问题是与输出。
与Zend框架1.x的处理输出缓冲,输出该文件被处理的指示符的方法是困难的。 在一个老式的PHP脚本,而不输出缓冲,你可以打印出一些HTML与循环的每一次迭代,并有一些进展迹象。
我想能够处理每一张专辑的目录,输出结果,然后继续到下一张专辑的目录。 只需要在一定的误差用户干预。
任何帮助,将不胜感激。
JavaScript是不是我要找的解决方案。 我觉得这应该PHP和ZF 1 MVC的结构内是不可能的。
我这样做主要是为我自己的启示,似乎学习一些重要的概念,一个很好的办法。
[编辑]
好了,怎么样就如何打破这种分解成小块的一些想法。 流程一个大块,提交,处理一个块,这样的事情。 转入或转出ZF的。
[编辑]
我开始看到什么,我试图完成的问题。 看来,输出缓冲不仅仅发生在ZF,它是由ZF发生无处不在一路浏览器。 Hmmmmm ...
介绍
这是一个什么样你不应该这样做 ,因为一个典型的例子
您正在试图解析ID3 tag
用PHP这是缓慢的,想有一次多个解析文件肯定会使它更慢
RecursiveDirectoryIterator
将加载从我所看到的是没有限制的文件夹和子文件夹中的所有文件..它可能是2,000
今天100,000
的第二天? 总处理时间是不可预测的,这绝对可以采取一些时间在某些情况下,
在单一文件系统的高度依赖性,与您现有的架构中的文件都存储在本地系统,这将是难以分割的文件,并做适当的负载均衡
如果文件信息之前被提取,这导致你不检查Loop and extraction Duplication
No locking system
。这意味着,这个过程可以同时启动造成的服务器上一般性能下降
解决方法1:使用当前架构
我的建议是不要用loop
或RecursiveDirectoryIterator
来处理大量的文件。
一旦目标文件,因为它们上传或传送到服务器。 这样,你只能用这种方式可以传播的处理时间将一个文件的时间工作 。
解决方案2:作业队列(建议的解决方案)
你的问题是什么作业队列的设计你也不限于与执行解析PHP
..你占便宜C
或C++
性能
优点
- 传输作业到更适合做这项工作的其他机器或流程
- 它允许你做并行工作,以负载均衡处理,
- 通过异步运行耗时的任务,减少在高容量的Web应用程序的页面浏览延迟
- 在多语言的客户端
PHP
在断绝C
例子已经测试
预计流程客户端
- 连接到作业队列例如德国
- 连接到数据库MongoDB的如Redis的或
- 循环使用的文件夹路径
- 检查文件扩展名
- 如果文件为MP3,生成文件哈希如。 sha1_file
- 检查文件已送交处理
- 发送哈希值,文件作业服务器
预期过程服务器
- 连接到作业队列例如德国
- 连接到数据库MongoDB的如Redis的或
- 接收散列/文件
- 提取ID3标签;
- 更新DB与ID3标签信息
最后,这个处理可以并行地对多台服务器进行
一个解决办法是使用作业队列,这样的Gearman。 Gearman的是这类问题的最佳解决方案,并且与Zend框架很容易地集成(http://blog.digitalstruct.com/2010/10/17/integrating-gearman-into-zend-framework/)
它可以让你创建一个工人来处理每个“吸盘”,允许同时处理作业的过程中,继续畅通,对于长时间运行的proceeses如音乐/图像处理等非常方便http://gearman.org/index。 PHP?ID = getting_started
我不熟悉如何Zend框架的工作。 我会给你一个一般建议。 当与过程做这么多的反复并有可能在很长一段时间的工作,一般建议是,漫长的过程被移动到后台进程。 或者,在网络有关,搬进cron作业。
如果你想在使用过程中是单一的网站,就可以实现这样的事情,在你的cronjob(注:粗糙的伪代码):
<?php
$targetdir = "/path/to/mp3";
$logdir = "/path/to/log/";
//check if current state is exists. If it does, then previous cronjob is still running
//we should stop this process so that it doesn't do duplicated process which might have introduced random bugs
if(file_exists($logdir."current-state")){
exit;
}
//start process, write state to logdir
file_put_contents($logdir."current-log", "process started at ".date("Y-m-d H:i:s"));
file_put_contents($logdir."current-state", "started\t".date("Y-m-d H:i:s"));
$dirh = opendir($targetdir);
while($file = readdir($dirh)){
//lets ignore current and parent dir
if(in_array($file, array('.', '..'))) continue;
//do whatever process you want to do here:
//you might want to write another log, too:
file_put_contents($logdir."current-log", "processing file {$file}", FILE_APPEND);
}
closedir($dirh);
file_put_contents($logdir."current-log", "process finished at ".date("Y-m-d H:i:s"));
//process is finished, delete current-state:
unlink($logdir."current-state");
接下来,在你的PHP文件的网络,你可以添加代码段,说管理页面,或页脚,或任何网页你想要的,看到的进展:
<?php
if(file_exists($logdir."current-state")){
echo "<strong>there are background process running.</strong>";
} else {
echo "<strong>no background process running.</strong>";
}
我建议使用插件。
class Postpone extends Zend_Controller_Plugin_Abstract
{
private $tail;
private $callback;
function __construct ($callback = array())
{
$this->callback = $callback;
}
public function setRequest (Zend_Controller_Request_Abstract $request)
{
/*
* We use layout, which essentially contains some html and a placeholder for action output.
* We put the marker into this placeholder in order to figure out "the tail" -- the part of layout that goes after placeholder.
*/
$mark = '---cut-here--';
$layout = $this->getLayout ();
$layout->content = $mark;
/*
* Now we have it.
*/
$this->tail = preg_replace ("/.*$mark/s", '', $layout->render ());
}
public function postDispatch (Zend_Controller_Request_Abstract $request)
{
$response = $this->getResponse ();
$response->sendHeaders ();
/*
* The layout generates its output to the default section of the response.
* This output inludes "the tail".
* We don't need this tail shown right now, because we have callback to do.
* So we remove it here for a while, but we'll show it later.
*/
echo substr ($this->getResponse ()
->getBody ('default'), 0, - strlen ($this->tail));
/*
* Since we have just echoed the result, we don't need it in the response. Do we?
*/
Zend_Controller_Front::getInstance ()->returnResponse(true);
$response->clearBody ();
/*
* Now to business.
* We execute that calculation intensive callback.
*/
if (! empty ($this->callback) && is_callable ($this->callback))
{
call_user_func ($this->callback);
}
/*
* We sure don't want to leave behind the tail.
* Output it so html looks consistent.
*/
echo $this->tail;
}
/**
* Returns layout object
*/
function getLayout ()
{
$layout_plugin = Zend_Controller_Front::getInstance ()->getPlugin ('Zend_Layout_Controller_Plugin_Layout');
return $layout = $layout_plugin->getLayout ();
}
}
class IndexController extends Zend_Controller_Action
{
/*
* This is a calculation intensive action
*/
public function indexAction ()
{
/*
* Zend_Layout in its current implementation accumulates whole action output inside itself.
* This fact hampers out intention to gradually output the result.
* What we do here is we defer execution of our intensive calculation in form of callback into the Postpone plugin.
* The scenario is:
* 1. Application started
* 2. Layout is started
* 3. Action gets executed (except callback) and its output is collected by layout.
* 4. Layout output goes to response.
* 5. Postpone::postDispatch outputs first part of the response (without the tail).
* 6. Postpone::postDispatch calls the callback. Its output goes stright to browser.
* 7. Postpone::postDispatch prints the tail.
*/
$this->getFrontController ()
->registerPlugin (new Postpone (function ()
{
/*
* A calculation immigration
* Put your actual calculations here.
*/
echo str_repeat(" ", 5000);
foreach (range (1, 500) as $x)
{
echo "<p>$x</p><br />\n";
usleep(61500);
flush();
}
}), 1000);
}
}