我今天重新审视了这个问题,因为在完成我们的用户界面(文件上传器/管理器)时,如果用户删除了由雪球导入创建的文件(其中“未”创建代表任何目录的空对象)以键名分隔),如果它是代表给定目录结构的最后一个对象,则该目录结构基本上不复存在!
我使用 SDK 进行了一些测试并证实了我的怀疑。如果我创建一个空对象来表示目录,然后在其中创建一个对象,则删除该对象不会删除由带有尾随分隔符的空对象表示的目录。但是,如果我创建一个对象而不首先创建一个空的“目录”对象,删除该对象会删除任何“目录路径”曾经存在过的证据。
我还确认您可以创建“空”目录表示,因为它不会影响在其键名中包含 [pseudo] 目录的任何对象。但是删除包含该“路径”的所有对象不会删除该目录的存在。
看来,要么我必须在任何和所有“删除”函数中创建一个解决方法,该函数将检查它是否包含“目录”表示,并创建一个空对象表示它的文件夹(如果它之前不存在)删除该对象,否则我将不得不遍历整个系统并为由于其键名而出现在“文件夹”中的任何和所有对象创建空对象。
呃,如果 AWS 在某个地方记录了这一点,那就太好了。我没有立即在雪球文档中看到任何明确说明这种情况的内容。
其他一些后果:
可以在 [pseudo] 目录中创建表示文件的对象,例如:
myfolder/path/myfile.txt
然后创建另一个与文件夹同名的对象:
myfolder/path/myfile.txt/anotherfile.txt
甚至可以创建一个[伪]目录占位符"
myfolder/path/myfile.txt/
还应注意,由于目录实际上并不存在(除非您创建一个占位符来表示它们的存在),因此如果您在对象键中表示长路径,例如:
some/really/long/path/myfile.txt
如果您不隐式创建以下“目录”,它们实际上并不存在:
some/
some/really/
some/really/long/
some/really/long/path/
这意味着如果您删除该对象,那些没有占位符的“目录”(或表示相同伪路径的其他对象)的任何痕迹都将不复存在!
还应注意,如果您想删除表示为该伪树的子路径的任何内容,例如删除“some/really/”下的所有内容,则必须使用实用程序 SDK 功能:带有前缀“some/really” 或“some/really/”或等效正则表达式的 deleteMatchingObjects。只为“some/really/”执行“deleteObject”只会删除任何带有“some/really”键的现有占位符,并且不会跟踪和删除上面示例中的文件。
注意:不带斜杠的“some/really”可能会匹配其他文件,例如“some/reallybig.jpg”
SW
附录:
与其遍历整个存储桶,为键名中表示的每个伪目录创建占位符,我意识到唯一的潜在问题是删除键名中包含路径表示的对象时。如果我随后测试并创建一个“父”目录,只要一个对象的键代表子目录中的文件,我就可以保留这些文件夹的模拟,因为尝试删除在删除对象时创建的“父”将然后测试并创建任何其他父目录,直到键“路径”中不再表示父目录。
以下函数允许您传递原始密钥,然后使用正则表达式查找任何子目录(假设使用 unix '正斜杠'分隔符):
/**
* run prior to a delete to preserve any directories represented in the
* pathname due to the 'flat' nature of the S3 filesystem.
* Checks if the 'key' represents a file in a 'pseudo' directory.
* If yes, it checks if an empty placeholder exists for the directory.
* If no, it creates one to preserve the directory in the bucket.
* @param string $target
* @param null|string $bucket
* @return \Aws\Result|S3Exception|\Exception
*/
private function _preDeletePersistSubdir($target, $bucket=null) {
if(preg_match('#^([^/]+.*/)[^/]+/?$#', $target, $m)) {
$subDir = $m[1];
try {
if((!$this->getS3ClientCached()->doesObjectExist($bucket,$subDir))) {
$params = array(
"Bucket" => $bucket,
"Key" => $subDir,
"Body" => ""
);
return $putDirResult = $this->getS3ClientCached()->putObject( $params );
}
} catch(S3Exception $e) { return $e; }
}
}
同样,您可以使用类似的函数递归构建整个树,将 preg_match 上的“if”更改为“while”,确保在每个循环结束时分配 $target = $subDir:
/**
* @param string $target
* @param null|string $bucket
* @return \Aws\Result|S3Exception|\Exception
*/
private function createSubdirPlaceholders($target, $bucket=null) {
while(preg_match('#^([^/]+.*/)[^/]+/?$#', $target, $m)) {
$subDir = $m[1];
try {
if((!$this->getS3ClientCached()->doesObjectExist($bucket,$subDir))) {
$params = array(
"Bucket" => $bucket,
"Key" => $subDir,
"Body" => ""
);
return $putDirResult = $this->getS3ClientCached()->putObject( $params );
}
} catch(S3Exception $e) { return $e; }
$target = $subDir;
}
}