【问题标题】:PHP rename() cannot always find source file (code 2) in Windows environmentPHP rename() 在 Windows 环境下总不能找到源文件(代码 2)
【发布时间】:2014-03-07 14:40:05
【问题描述】:

我的环境是:Windows、MsSQL 和 PHP 5.4。

我的场景: 我正在做一个小的 shell 脚本,它从我想要的数据库创建一个完整的备份到一个临时文件夹,然后将它移动到一个新的位置。

备份正常,文件被创建到我的临时文件夹中。然后我将它重命名为第二个文件夹,有时它会正常,有时它找不到源文件。

当然,此时我知道我可以完全跳过临时位置,但是找不到文件的实际问题困扰着我。为什么它如此随机,并且可能还会影响我在此之前编写的其他文件功能...此外,我需要能够控制文件移动到目标的方式和时间。

基本代码应该很简单(尽管这是我实际代码的简化版本,因为我怀疑有人会对我的错误处理/记录条件感兴趣):

$query = "use test; backup database test to disk '//server01/temp/backups/file.bak', COMPRESSION;";

if($SQLClass->query($query)) { 

    $source="////server01//temp//backups//file.bak";
    $destination="////server02//storage//backups//file.bak";

    if(!rename($source , $destination)) {
        //handleError is just a class function of mine that logs and outputs errors.
        $this->handleError("Moving {$source} to {$destination} failed.");
    }
}
else {
    die('backup failed');
}

我试过的是:

  • 我在它之前添加了一个file_exists,它也找不到源文件,当重命名时找不到。
  • 由于找不到文件,copy() 和 unlink() 也不起作用
  • 试过 clearstatcache()
  • 在 sql 备份完成后尝试 sleep(10)

这些都没有帮助。我和谷歌似乎对下一步做什么或尝试没有想法。当然我可以执行一些 shell_execing,但这并不能消除我对早期产品的担忧。

我只是在尝试连续多次运行该命令时才注意到这个问题。是否有 clearstatcache() 不会触及的文件名的某种缓存?这似乎与某种幽灵文件现象有关,其中php更新文件系统内容迟到了。

如果您有任何关于下一步尝试的想法,我将不胜感激,如果您读到这里,谢谢:)。

【问题讨论】:

  • 您是否为当前用户检查了服务器中文件的权限?
  • 您是否尝试过在调用handleError 之前将重命名调用置于一个休眠一段时间并重试3-5 次的循环中?有时这有助于处理偶尔超时的文件操作。
  • 是的,我有,权利很好。这也是我早期的想法之一,但结果是有时无法找到该文件。我没有尝试循环播放它,但我确实尝试睡了 10 秒。我会试试看它是否有效,也许两次调用相同的函数会重置一些东西。
  • 在下面试试我的解决方案,希望它有效

标签: php sql sql-server file-move


【解决方案1】:

您可以尝试调用系统的copy 命令。 当我不得不在两个 NFS 共享之间复制文件时,我曾经遇到过像你一样的问题(在 Linux 机器上)。它只是不时地失败,没有明显的原因。在我切换到cp(Windows 副本的模拟)之后,问题就消失了。

当然它并不完美,但它对我有用。

【讨论】:

  • 这可能是唯一的解决方案。我一直对php的文件函数有点警惕。但是因为我以前真的从来没有遇到过任何问题,所以我一直在使用它们。
  • 我写了一个小的 file_move($source, $dest) 函数。即使这样有时似乎也太快了,但至少它比迄今为止的php文件功能稳定得多。
【解决方案2】:

可能是缓存相关,或者mysql进程还没有释放文件。 mysql 会将文件转储到 another 临时文件中,首先将其移动到 您的 临时文件夹。 在移动文件时,其他进程可能无法访问它。

首先,当出现错误时,我会尝试 glob() 临时目录中的所有文件。也许你注意到了,它还没有完成。

您是否也尝试过实现 10 次重试迭代,但有一些延迟?

$notMoved = 0;
while($notMoved < 10){
    $source="////server01//temp//backups//file.bak";
    $destination="////server02//storage//backups//file.bak";

    if(!rename($source , $destination)) {
        //handleError is just a class function of mine that logs and outputs errors.
        if ($notMoved++ < 10){
           sleep(20);
        } else {
           $this->handleError("Moving {$source} to {$destination} failed.");
           break;
        }
    }else{
       break;
    }
}

绕过问题:

  • 不要倾倒和移动
  • 移动然后转储:-)

(ofc。你的备份存储会落后一个)

    $source="////server01//temp//backups//file.bak";
    $destination="////server02//storage//backups//file.bak";

    if(!rename($source , $destination)) {
        //handleError is just a class function of mine that logs and outputs errors.
        $this->handleError("Moving {$source} to {$destination} failed.");
    }

$query = "use test; backup database test to disk '//server01/temp/backups/file.bak', COMPRESSION;";

if($SQLClass->query($query)) { 
  //done :-)
}
else {
    die('backup failed');
}

【讨论】:

  • 好的,我尝试了 10 次尝试循环,每个循环睡眠 5 秒。结果很可疑。有时需要几个循环来移动文件。有时它会经历所有 10 次迭代。将这样的东西投入生产是可怕的:)。
  • @MacRedrum 转储有多大?根据服务器负载和大小,可能需要几秒钟才能将临时文件复制到那里。如果第一次移动是异步的,那么你的第二次移动尝试就会遇到问题。
  • 目前大约 8 兆。完成它需要几分之一秒。根据 SQL Server,它处理备份的速度约为 50mb/s。这就是为什么即使在睡眠 50 秒后也找不到它的原因。
【解决方案3】:

试试

$source = "\\server01\temp\backups\file.bak";
$destination = "\\server02\storage\backups\file.bak";
$content = file_get_content($source);
file_put_contents($destination, $content);

【讨论】:

  • 刚试过这个,它随机得到相同的随机错误:“无法打开流:没有这样的文件或目录”。就像 rename() 一样,这有时也有效。
  • 你知道这个问题是从$source读取还是保存到$destination时出现的?
  • 是的,来源。完整错误:“错误:file_get_contents(//server01/temp/backups/file.bak):无法打开流:没有这样的文件或目录”。我还有一些功能可以确保目标文件夹存在(当然在第一次运行之后它也总是存在)
  • 哦,我认为这是因为 windows 服务器需要将 / 更改为 \ 的原因。我也编辑了我的解决方案代码
  • 不,这不是路径,因为它会时不时地找到它:)。
猜你喜欢
  • 1970-01-01
  • 2018-07-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-10-04
  • 1970-01-01
  • 2013-02-10
  • 1970-01-01
相关资源
最近更新 更多