从临时目录移动时无法重命名文件

Unable to rename the file while moving from temporary directorary

我正在开发一个 zip 提取器应用程序,我遵循了 CRD 解释的算法 @ 但我卡在了第三步,我无法重命名临时目录中的解压缩文件。 这是我的代码

  NSURL *tempDir = [NSURL fileURLWithPath:destinationPath];
        NSError *error;

        NSURL *tmpDirectory = [[NSFileManager defaultManager] URLForDirectory:NSCachesDirectory inDomain:NSUserDomainMask appropriateForURL:tempDir create:YES error:&error];
        if (error) {
            return ;
        }
        tmpDirectory = [tmpDirectory URLByAppendingPathComponent:@"extracts"];
        NSLog(@"temp dir %@",tmpDirectory);
        NSLog(@"temp path %@",tmpDirectory.path);


        [SSZipArchive unzipFileAtPath:zipFilePath toDestination:tmpDirectory.path];

        NSArray *dirFiles = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:tmpDirectory.path error:nil];
        NSLog(@"dir file %@",dirFiles);
        for (NSString *string in dirFiles) {

            NSArray *dirDestinationFiles = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:destinationPath error:nil];
            NSLog(@"dir destination file %@",dirDestinationFiles);
            [dirDestinationFiles enumerateObjectsUsingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
                NSFileManager *fm = [NSFileManager defaultManager];
                NSError *error;


                if ([string isEqualToString:obj]) {


                    NSLog(@"Already present");

                    BOOL isMoved = [fm moveItemAtPath:tmpDirectory.path  toPath:[destinationPath stringByAppendingString:[NSString stringWithFormat:@"/%@-1",string]] error:&error];

                    if (isMoved) {
                        NSLog(@"Moved");
                    }else{
                        NSLog(@"errorL %@", error);
                        NSLog(@"Not moved");
                    }

                    [fm removeItemAtPath:tmpDirectory.path error:&error];

                    [self moveFileToTrash:zipFilePath];
                    [self openExtractedFolderWithZipPath:zipFilePath toDestinationPath:destinationPath];

                }

            }];


        }

任何建议.. 提前致谢!

这是适合我的代码。

 NSURL *tempDir = [NSURL fileURLWithPath:destinationPath];
        NSError *error;

        NSURL *tmpDirectory = [[NSFileManager defaultManager] URLForDirectory:NSCachesDirectory inDomain:NSUserDomainMask appropriateForURL:tempDir create:YES error:&error];
        if (error) {
            return ;
        }
        tmpDirectory = [tmpDirectory URLByAppendingPathComponent:@"extracts"];
        NSLog(@"temp dir %@",tmpDirectory);
        NSLog(@"temp path %@",tmpDirectory.path);


        [SSZipArchive unzipFileAtPath:zipFilePath toDestination:tmpDirectory.path];

        NSArray *dirFiles = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:tmpDirectory.path error:nil];
        NSLog(@"dir file %@",dirFiles);
        for (NSString *string in dirFiles) {

            NSArray *dirDestinationFiles = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:destinationPath error:nil];
            NSLog(@"dir destination file %@",dirDestinationFiles);
            NSMutableArray *folderCount = [[NSMutableArray alloc] init];
            NSMutableArray *folderNumCount = [[NSMutableArray alloc] init];

            [dirDestinationFiles enumerateObjectsUsingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {

               if ([obj containsString:string]){
                    [folderNumCount   addObject:obj];
                }
                if ([string isEqualToString:obj]) {


                    [folderCount addObject:string];

                }

            }];


            NSFileManager *fm = [NSFileManager defaultManager];
            NSError *error;

            if (folderCount.count == 0) {
                NSLog(@"First time extract");


                BOOL isMoved = [fm moveItemAtPath:tmpDirectory.path  toPath:[destinationPath stringByAppendingString:[NSString stringWithFormat:@"/%@",string]] error:&error];

                if (isMoved) {
                    NSLog(@"Moved");
                }else{
                    NSLog(@"errorL %@", error);
                    NSLog(@"Not moved");
                }
                [fm removeItemAtPath:tmpDirectory.path error:&error];

                // [self moveFileToTrash:zipFilePath];
                // [self openExtractedFolderWithZipPath:zipFilePath toDestinationPath:destinationPath];

            }else if (folderCount.count > 0){
                NSLog(@"Already present");

                BOOL isMoved = [fm moveItemAtPath:tmpDirectory.path  toPath:[destinationPath stringByAppendingString:[NSString stringWithFormat:@"/%@-%lu",string,folderNumCount.count-1]] error:&error];

                if (isMoved) {
                    NSLog(@"Moved");
                }else{
                    NSLog(@"errorL %@", error);
                    NSLog(@"Not moved");
                }
                [fm removeItemAtPath:tmpDirectory.path error:&error];

                //  [self moveFileToTrash:zipFilePath];
                //  [self openExtractedFolderWithZipPath:zipFilePath toDestinationPath:destinationPath];


            }


        }

让我们检查一下您的代码,希望对您有所帮助。

这可能看起来很小,但要选择好的变量名:

NSURL *tempDir = [NSURL fileURLWithPath:destinationPath];
NSURL *tmpDirectory = [[NSFileManager defaultManager] URLForDirectory:NSCachesDirectory inDomain:NSUserDomainMask appropriateForURL:tempDir create:YES error:&error];

两个在语义上相似的不同事物的名称,这只是令人困惑。比方说,destinationURL 而不是 tempDir 怎么样?

接下来,当constructing/pullingapart/etc。路径名或 URLs 你最好保持一致。 NSURLNSString 都为这些操作提供了类似的方法,在一个地方使用它们:

tmpDirectory = [tmpDirectory URLByAppendingPathComponent:@"extracts"];

然后恢复使用路径分隔符直接进行字符串操作,该分隔符可能正确,也可能不正确:

[destinationPath stringByAppendingString:[NSString stringWithFormat:@"/%@-1",string]]

NSURLNSString 提供的例程抽象了路径分隔符的细节以及如何在最后一个路径组件上找到扩展名(重命名时您可能会发现它很有用)以避免冲突)。

回到:

tmpDirectory = [tmpDirectory URLByAppendingPathComponent:@"extracts"];

你没有理由这样做。临时目录是为您创建的,您应该在使用后将其删除。因此,无需在其中创建子目录 extracts,通过重新分配给相同的变量,您丢失了 URL,您需要删除临时目录。

现在有些不太明显,在我上面的评论中我写道:

To move each item you must handle name clashes, to do this try the move and if you get an error indicating a name clash modify the destination name however you like and re-try the move, repeating until you succeed or you until reach some limit of tries (determined by you).

我没有解释为什么你应该这样做,你已经用不同的方式解决了这个问题:对于你要移动的每个项目,你检查名称冲突 before 尝试通过遍历目标目录中的名称来移动。

如果您阅读 Apple 关于文件系统的文档,您会发现他们经常建议您尝试一个操作,然后检查返回的任何错误,而不是试图预测错误是否会发生并避免它。这样做的原因是文件系统是动态的,其他进程可以修改它,所以如果你试图避免错误,你仍然可能会遇到错误。在伪代码中你最好做这样的事情:

moveDone = false
attemptCount = 0
while not moveDone and attemptCount < MAX_ATTEMPTS
   move object
   if object exists error
      modify destination URL
      increment attemptCount
   else
      moveDone = true
   end
end
if not moveDone then handle error

遵循此大纲并使用简单计数和 NSString/NSURL 路径例程将为您提供比您现在作为答案发布的解决方案更简单、更可靠的解决方案。

HTH