Laravel 与 PHP ZipArchive - 添加文件两次

Laravel with PHP ZipArchive - adding file twice

我正在尝试使用 Laravel 创建一个 KMZ 文件。这只是一个 ZIP 文件,在根目录下带有 doc.kml,以及存档中包含的所有引用文件。

下面是我比较简单的代码:

        // Defined previously in the file:
        // $location -- a model containing a geographic location
        // $kml -- an XML document for the location
        // $kml is a valid KML file (tested and working)
        $kmz = new ZipArchive;
        $kmz->open("/tmp/{$location->name}.kmz", ZipArchive::CREATE);
        $kmz->addFromString("doc.kml", $kml);
        $icons = $location->markers->pluck("icon")->unique();
        // dd( $icons );
        foreach( $icons as $icon )
        {
            $icon = trim($icon, "/");
            $kmz->addFile(public_path($icon), $icon);
        }
        $kmz->close();
        return response()->download("/tmp/{$location->name}.kmz");

这将下载 KMZ 文件并按预期工作(我可以在 Google 地球中打开 KMZ 并查看文件、显示标记等)

但是每个图标都添加了两次。这是我在包含单个图标时看到的示例:/img/maps/pins/fa-home.png

如您所见,有一个名为空白的文件夹。当我深入这个文件夹时,它会显示以下内容:

图像fa-home.png同时存在于/img/maps/pins/目录这个奇怪的空白目录中。更糟糕的是,空白目录正在转储我服务器的目录结构(一个小安全漏洞)

可能是什么原因造成的?

几天后我弄清楚了这里的问题,有点尴尬。

这一行有一个微妙的问题提示:

$kmz->open("/tmp/{$location->name}.kmz", ZipArchive::CREATE);

使用ZipArchive::CREATE时,如果文件已经存在,会打开修改。这不是 well-documented。充其量我们有以下行:

ZipArchive::CREATE (integer)

Create the archive if it does not exist.

来源:http://php.net/manual/en/zip.constants.php#ziparchive.constants.create

这里没有指定行为如果确实存在。显然行为是直接修改现有文件。

事实证明,//home/ubuntu/workspace/public//img/maps/pins 目录已添加到我之前编写的测试中的 zip,此后已更正并删除。但是,由于我一直在更新现有文件而不是创建新文件,因此该文件从未从我的 ZIP 文件中删除。

作为快速修复,可以使用以下内容代替此行:

$kmz->open("/tmp/{$location->name}.kmz", ZipArchive::CREATE | ZipArchive::OVERWRITE);

但是,如果两个人试图访问同一个文件,那么第二个可能会收到服务器错误(因为第一个用户有一个打开的 ZIP 文件句柄,并且不能因覆盖而被销毁)。我发现一个更好的解决方案是将一个唯一的字符串附加到文件名。我选择了以下内容:

$utime = utime();
$rand = rand();
$kmz->open("/tmp/{$location->name}-{$utime}-{$rand}.kmz", ZipArchive::CREATE | ZipArchive::OVERWRITE);

我对我的解决方案不是 100% 满意,因为它 non-deterministic。有可能,尽管非常非常非常不太可能,但两个人同时(相同的微秒)为 rand() 函数访问相同的文件 发生 到 return 它们每个的相同值。这种情况在未来 100,000 年内发生一次的几率可能是万亿分之一,但如果有更好的解决方案,我很想弄清楚(利用锁定文件、散列请求的某些属性等)。