如何使用PHP和Ajax实时读取文件

3

我希望实时读取一个文本文件,并在前端更新新添加的行。该文本文件保存了两个用户的日志数据。我已经尝试过一些方法来解决这个问题,但是无论是php脚本还是js都不能满足我的要求。

JS脚本

  function ShowNewMessages(res, curr, end) {
    // build the front end
    var newdiv1 = "<div class=\"message\"> <div class=\"own-avatar-message\" style=\"background-image: url("+res[curr][4].replace(/\\\//g, "/")+");\"></div><div class=\"own-message2\"><div class=\"own-message\"><p>" +  res[curr][3]  +"</p></div><div class=\"message-timestamp\"> "+ res[curr][1] +" • "+res[curr][2] +"</div></div> </div>"; 

    $( newdiv1 ).insertAfter( $( ".message" ).last());
    var height = 0;
    $('.message').each(function(i, value){
        height += parseInt($(this).height());
    });

    height += '';

    $('div').animate({scrollTop: height});

    // check if there is another messsage or checkagain for new messages             
    curr++;
    if (curr <= end) ShowNewMessages(res, curr, end);
    else {setTimeout(function() {GetNewMesssages()}, 10000);}
}



function GetNewMesssages() {
    $.ajax({
        type:'POST',
        url: "../lib/GetMesssage.php",
        data: "friend=" + MsgFriend,
        dataType: 'json',
        async: false, 
        success:function(data){
            // check error state
            if (data[0] != "Error") {
                var start = 0;
                var end = data.length-1; // minus one for the new line
                if (end > 0 ) ShowNewMessages(data, start, end-1);
                else setTimeout(function() {GetNewMesssages()}, 10000);

            }
            else alert('some error message');
            }
        });   
} 

GetMesssage.php

<?php 

    session_start();

    // get the requirements for the file name
    $MyEmail = $_SESSION['login'];
    $FriendEmail = $_POST['friend'];

    if (strcmp($MyEmail, $FriendEmail) < 0) {
        $FileName = $MyEmail.'-'.$FriendEmail.'.txt';
    }
    else {
        $FileName = $FriendEmail.'-'.$MyEmail.'.txt';
    }


    $File = fopen('data/'.$FileName, "r") or die(json_encode(array_push($messages, "Error")));

    // count the lines exist in the file
    $count = 0;
    while(!feof($File)){
        $tmp = fgets($File);
        $count++;
    }

    // if the session var that holds the offset position is not set 
    // or has value more than the lines of text file
    if (!isset($_SESSION['pos']) || $_SESSION['pos'] >= $count) {
       $_SESSION['pos'] = 0; // back to the beginning
    } 

    // the array that holds the logs data
    $messages = array(); 

    // move the pointer to the current position
    fseek($File, $_SESSION['pos']);

    // read the file
    while(!feof($File)){
        $msg = fgets($File);
        $msg1 = explode("<!@#@>", $msg);
        array_push($messages, $msg1);
        $_SESSION['pos']++;
    }

    // get the current offset position
    //$_SESSION['pos'] = ftell($File);

    // return the array
    echo json_encode($messages);

    fclose($File)
?>

解释

假设我们第一次调用GetNewMesssages()函数时,例程开始运行。该函数将触发php并从文件中获取数据。

接下来,如果数据包含内容,则返回数组不为空,我们执行ShowNewMessages()函数并更新前端。

完成后,我们启动另一个GetNewMesssages()回调。

我的需求

我想每N秒检查一次文件是否有新添加的行,并更新我的前端。

问题

我在php和js脚本中都遇到了问题。我不想添加两个问题。

在PHP中

我没有找到如何使用ftell()仅获取新添加的行。因为脚本的工作方式是这样的,在每次更新中我都会不断地将整个文件读入我的数组中。

在JS中

在JS中,我遇到了同步ajax调用的问题。也就是说,在每次调用GetNewMessages()函数时,我会再次调用2-3次该函数。这与setTimeout()有关,但我也找不到如何设置回调。

更新

我要读取的文本文件的示例:

line_id <BreakString> some data <BreakString> blah blah

another_line_id <BreakString> some_other data <BreakString> blah blah 2

注意:这并不代表任何意思,仅是将内容分割成数组。
更新 2
在收到版主的通知后,我将缩小我的问题范围,只是想找到保持结构的实时读取文件的最佳方法。

看起来读取文件并不需要 PHP,但是写入文件需要一些东西。 - Praveen Kumar Purushothaman
@PraveenKumar 你具体是指什么?(我有另一个过程可以更新文件) - user4108694
https://dev59.com/1FrUa4cB1Zd3GeqPoekX - mplungjan
可能应该先说明为什么需要从文本文件中进行操作,而不是使用某种类型的数据库,这将比在服务器上自己开发要简单得多。 - charlietfl
@charlietfl 我之前就考虑过了。我想现在这是最好的解决方案。在下一个更新中,当我的项目完成后,我会开始考虑它。无论如何,这只是为了练习,不会有任何损失。 - user4108694
显示剩余3条评论
1个回答

1

PHP

在 PHP 中,fseek() 可以将文件指针设置到文件中特定的 字节 偏移量。你在代码中所做的假设是 fseek() 会将指针设置到文件中特定的一行。

假设你的消息不包含换行符(这段代码中并没有这样的前提条件),你可以使用 file() 将整个文件加载到 PHP 的数组中,并通过这种方式确定所需的行偏移量。

例如...

$file = file($fileName);
$mssages = array_slice($file, $_SESSION['pos']);

这将为您提供一个数组,其中包含从$_SESSION['pos']行偏移开始的文件中的所有行。

重构您现有的PHP代码

相反地,为了重构您现有的代码,您可以修改以下循环以在达到$_SESSION['pos']时停止读取,并因此继续从该点加载新消息的逻辑...

$count = 0;
while(!feof($File)){
    $tmp = fgets($File);
    $count++;
}

相反,你要做这个...
$count = 0;
while(!feof($File) || $count < $_SESSION['pos']){
    $tmp = fgets($File);
    $count++;
}

现在删除fseek($File, $_SESSION['pos'])这一部分,你的第二个循环应该从你期望的那一行开始。

文件格式

如果存储这些消息的文件意图是采用分行格式,即文件中的每一行都代表着一个消息(这似乎是你在代码中所假设的),那么避免消息内容自身破坏文件的最安全方式是以不允许在消息有效负载中使用换行符的方式对消息内容进行编码。这类编码的例子包括 JSONBase64

后者将会增加大约33%的存储成本,但两种编码方式都适用于分行文件格式。


Javascript

不清楚您的Javascript逻辑为什么使用 currend 来确定是否应该使用 setTimeout()。如果您想要一个全局循环每10秒钟检查新数据,您应该在全局作用域中调用 setInterval(GetNewMesssages, 10000) 一次,而不是有条件地使用 setTimeout()。在您的代码中,如果当前消息是找到的最后一条消息的条件,则您将不再从js调用PHP以检查新消息,这显然不是您想要的。 setTimeoutsetInterval 的区别在于,setTimeout 只在定时器到达时间后触发一次,而 setInterval 会以给定间隔连续触发回调。


PHP的操作代码没有起作用。我再次得到了整个数组。但是为什么? - user4108694
我不知道你所说的“_the manipulate code for php_”是什么意思,也不知道“_it didn't work_”是什么意思。如果你正在寻找有人帮助你调试代码,你必须准备好提供以下三个方面的内容:1)展示出问题的代码;2)你期望这段代码做什么;3)这段代码实际上做了你没有预料到的事情。 - Sherif
它返回给我一个空数组。你能把整段代码发出来吗? - user4108694
我不确定你想让我发布哪些代码。也许你得到一个空数组的原因是$_SESSION['pos']在文件末尾或者文件本身为空。 - Sherif
让我们在聊天中继续这个讨论 - user4108694
显示剩余4条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接