【问题标题】:First App Update, User Data Lost (was stored in Documents directory)首次应用更新,用户数据丢失(存储在 Documents 目录中)
【发布时间】:2011-08-02 05:37:28
【问题描述】:

我的第一个应用更新昨晚刚刚上线,我收到了一份投诉,称更新导致用户创建的数据(其中一些)消失了。我已经能够重现该问题,但不知道为什么。

在 Documents 目录中,我保存了一个密钥文件,它告诉我所有用户文件的“标题”及其文件名(完整路径)。那么用户的所有文件也都在 Documents 目录下。

当更新发生时,密钥文件仍然存在(至少,我认为这是因为数据显示在应用程序的第一个屏幕中 - 应用程序在更新后会完全退出并重新启动,对吧?),但是当用户尝试导航到实际文件中,没有数据,并且用户输入的任何新数据都不会保存。

当我不小心使用了无效的文件名(其中包含不正确的字符)时,它的行为与调试时完全相同 - 它从未保存过。但是随着更新,这些文件在旧版本中已经正确保存,但在新版本中却不知何故失败了。

这是我的第一个应用程序和我的第一次更新,我在这里很茫然。我暂时停止销售该应用程序(不知道您不能简单地恢复到旧版本!哎呀!)并且非常感谢关于在哪里/如何查找问题以及如何编写的任何想法不会丢失数据的良好更新。 (到目前为止,我发现的只是“将数据保存在 Documents 目录中”,这是我已经在做的。)

我确实将原始应用程序保存在自己的项目中,我可以返回并重新开始工作。当我开始进行更新时,我复制了整个目录,我想知道这是否可能是问题所在?我确实更改了包含所有 XCode 文件的目录的名称,并使用了 Project>Rename 函数。这会以某种方式产生这种效果吗?

应用程序(原始和更新)在 4.2 及更高版本上运行,如果这很重要的话。

解决方案:

我相信我已经解决了这个问题。就像我在问题开始时所说的那样,我将用户文件的完整路径保存在我的密钥文件中。显然,更新后的完整路径不能保证是相同的(我确信这在某处有记录,但我没有遇到过)。

因此,用户文件已被传输到更新的新文档目录,但我在旧的绝对路径中查找它们,即不在我的沙箱中。

通过在应用程序的 didFinishLaunching 中放置一个循环来提取文件路径的违规文件(我一直在本地寻找并且它仍然有效)并将它们切割成文件名,从而解决了这个问题。每当执行文件操作时,都必须以编程方式找到并添加文档路径。

对于它的价值,实际上对我有好处,因为重新安装原始版本并不能解决用户的问题,所以无法恢复到商店中以前的二进制文件,但我相信它有,至少最初是这样。我确实希望有一种方法可以禁止更新,但仍然允许新购买(因为问题不影响新购买)。不过,这种情况可能并不常见。

【问题讨论】:

  • 天哪,确实如此。 :/ 我必须说,对此的任何远程帮助充其量只能是在黑暗中拍摄 - 祝你好运。
  • 我花了一段时间才弄清楚这一点,但您的问题和回答刚刚证实了我的问题。谢谢。
  • 当 iOS 从 7 升级到 8 时,我在一个应用中遇到了同样的问题。这是否意味着 iOS 会在升级时为所有具有不同十六进制名称的应用创建一个新目录?
  • @ohDear 谢谢你解释清楚的回答

标签: ios file-io filesystems


【解决方案1】:

只是为了让互联网路人对此有所了解。是的,OP 在最后回答了他自己的问题。您不应该在您的文档目录中存储文件的绝对 URL,这样做可能会导致数据丢失。这是因为当您更新应用程序时,通过在 .plist 文件中更改其版本号,iOS 会为该应用程序创建一个具有不同十六进制名称的新目录。现在您的绝对 URL 引用了错误的位置,并且不会返回正确的文件。

在部署到设备之前,您可以在自己的计算机上查看此内容,方法是导航到

~/Library/Application Support/iPhone Simulator/*ios_version*/Applications/

在那里您会看到名称如下的文件夹:

6AA4B05C-8A38-4469-B7BE-5EA7E9712510 CB43C5F3-D720-49C3-87D4-EBE93FFD428B

这些文件夹内将是 iOS 应用程序的标准文件系统布局。即

文件 图书馆 时间 YourApp.app

通过引用整个 URL,您会看到类似的内容,

~/Library/Application Support/iPhone Simulator/*ios_version*/Applications/6AA4B05C-8A38-4469-B7BE-5EA7E9712510/Documents/MyVeryImportantUserData.txt

但是,当您更新应用程序时,应用程序将尝试的新 URL 将是:

~/Library/Application Support/iPhone Simulator/*ios_version*/Applications/CB43C5F3-D720-49C3-87D4-EBE93FFD428B/Documents/MyVeryImportantUserData.txt

这是空的。

相反,您应该始终通过仅在获得文档目录文件路径前缀后保存文件路径来进行动态引用。

/**
 Returns the path to the application's Documents directory.
 */
- (NSString *)applicationDocumentsDirectory {
    return [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
}

对于原始发布者,您可以保存您的用户丢失数据,方法是发布一个新的更新,在该更新中您修改您的代码以获取绝对 url 并通过字符串的文档部分修剪所有内容。然后,您可以在文件路径前加上正确的文档目录 url,应用程序就会找到数据。

【讨论】:

    【解决方案2】:

    问题海报和第一个答案都有助于澄清到底发生了什么。

    因此,根据他们的观察,我开始意识到(似乎是)在通向 Documents 文件夹的路径中对文件夹进行了重命名。这使我找到了一个解决方案,我只保存文件的文件名并创建一个实用程序类来获取已保存的文件名的 current fullPath 字符串,现在存储在 当前应用更新后的文档文件夹:

    #import "StringUtils.h"
    
    @implementation StringUtils
    
    + (NSString *)getFullDocumentUrl:(NSString *)fileName
    {
        return [NSString stringWithFormat:@"%@/%@",[self applicationDocumentsDirectory],fileName];
    }
    
    + (NSString *)applicationDocumentsDirectory
    {
        return [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
    }
    
    @end
    

    【讨论】:

      【解决方案3】:

      我遇到了同样的问题。下面是一些示例代码,通过遍历文档目录中的文件并在保留文件名的同时替换文件路径信息来修复文件名。在执行最后一行更改文件名之前,我建议使用 NSLog 以确保这正是您想要的 updatedName 内容。

          NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
          NSString *documentsPath = [paths objectAtIndex:0]; //Get the docs directory
          NSFileManager *fm = [NSFileManager defaultManager];
          NSArray *filenames = [fm contentsOfDirectoryAtPath:documentsPath error:nil];
      
          //Match on the filepath leading up to docs directory
          NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:@"^/\\S*Documents/"
                                                                                 options:NSRegularExpressionCaseInsensitive
                                                                                   error:nil];
          for (NSString *fileName in filenames)
          {
              NSRange fullLength = NSMakeRange(0, [fileName length]);
              //swap out the prepended directory structure with an empty string
              NSString *updatedFileName = [regex stringByReplacingMatchesInString:fileName
                                                                          options:0
                                                                            range:fullLength
                                                                     withTemplate:@""];
              NSString *currentName = [NSString stringWithFormat:@"%@/%@",documentsPath,fileName];
              NSString *updatedName = [NSString stringWithFormat:@"%@/%@",documentsPath,updatedFileName];
              [fm moveItemAtPath:currentName toPath:updatedName error:nil];
          }
      

      【讨论】:

        【解决方案4】:

        获取当前文档目录路径

        NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] as String
        

        【讨论】:

          【解决方案5】:

          您可以使用此功能查找新的文档目录

          例如:这是您第一次构建应用并将照片保存到此 url

          “file:///var/mobile/Containers/Data/Application/D79E1373-883C-412B-B84E-5C1E47BD0BEC/Documents/Temp/Photo_65650205603482916.jpg”

          在您重建应用程序之后,文档直接被更改为

          file:///var/mobile/Containers/Data/Application/E387590D-B279-4C38-A601-65C6F7BC917D/Documents/

          所以使用旧网址时不能不上传

          上传时可以这样做:

          // you have photoURl 
          
          func getURLtoUpload(photoUrl:URL) {
              let urlStr = photoUrl.lastPathComponent
          
              let fileURL = self.getDocumentsDirectory().appendingPathComponent("Temp").appendingPathComponent("\(urlStr)")
              // your url to upload here 
              let url = URL(string:fileURL)
          }
          
          
          func getDocumentsDirectory() -> URL {
          
              let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
              let documentsDirectory = paths[0]
              return documentsDirectory
          }
          

          【讨论】:

            【解决方案6】:

            如果您想永久保存文件的位置,请使用 NSURL 的书签功能。书签是一种不透明的数据结构,包含在 NSData 对象中,用于描述文件的位置。虽然路径和文件引用 URL 在您的应用启动之间可能很脆弱,但书签通常可用于重新创建文件的 URL,即使在文件被移动或重命名的情况下也是如此。

            https://stackoverflow.com/a/53274341/945906

            【讨论】:

              猜你喜欢
              • 2023-03-24
              • 1970-01-01
              • 1970-01-01
              • 2016-09-18
              • 2015-01-05
              • 2020-02-20
              • 2012-01-02
              • 1970-01-01
              相关资源
              最近更新 更多