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 年内发生一次的几率可能是万亿分之一,但如果有更好的解决方案,我很想弄清楚(利用锁定文件、散列请求的某些属性等)。
我正在尝试使用 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 年内发生一次的几率可能是万亿分之一,但如果有更好的解决方案,我很想弄清楚(利用锁定文件、散列请求的某些属性等)。