首次应用更新,用户数据丢失(存储在Documents目录中)

时间:2011-04-09 20:23:19

标签: ios file-io filesystems

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

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

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

当我意外地使用无效的文件名(其中包含不正确的字符)时,它的行为与调试时完全相同 - 它从未保存过。但是通过更新,这些文件已在旧版本中正确保存,但在新版本中以某种方式失败。

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

我确实将原始应用程序保存在自己的项目中,我可以再次返回并继续工作。当我开始处理更新时,我复制了整个目录,我想知道这是否会出现问题?我确实更改了包含所有XCode文件的目录的名称,并使用了Project> Rename函数。这会有什么影响吗?

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

解决方案:

我相信我已经找到了这个问题。就像我在问题开头附近说的那样,我在用户密钥文件中保存了用户文件的全部路径。显然,更新后的完整路径不保证是相同的(我确定这是在某处记录的,但我没有碰到它)。

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

通过在应用程序的didFinishLaunching中放置一个循环以解决文件路径的违规文件(我总是在本地查找并且它仍在工作)并将它们切换到只有fileNAMES来修复。无论何时执行文件操作,都必须以编程方式找到并添加文档路径。

对于它的价值,对我来说实际上有好处,因为重新安装原始版本无法修复用户的问题,但我无法恢复到商店中的先前二进制文件,但我相信它有,至少最初。我确实希望有一种方法可以禁止更新,但仍允许新购买(因为问题不影响新购买)。但是,可能不是一个足够普遍的情况来保证它。

6 个答案:

答案 0 :(得分:57)

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

在您通过导航到

部署到设备之前,您可以在自己的计算机上看到此信息

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

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

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

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

文档 图书馆 TMP YourApp.app

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

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

但是,当您更新应用时,应用将尝试使用的新网址为:

〜/ 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,应用程序就可以找到数据了。

答案 1 :(得分:1)

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

因此,基于他们的观察,我开始意识到(在看起来是这样的)路径中的文件夹的重命名导致了Documents文件夹。这导致我找到一个解决方案,我只保存文件的fileNames并创建一个实用程序类,用于获取已保存的fileName的当前 fullPath字符串,现在存储在当前应用更新后的Documents文件夹:

#import "StringUtils.h"

@implementation StringUtils

+ (NSString *)getFullDocumentUrl:(NSString *)fileName
{
    return [NSString stringWithFormat:@"%@/%@",[self applicationDocumentsDirectory],fileName];
}

+ (NSString *)applicationDocumentsDirectory
{
    return [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
}

@end

答案 2 :(得分:0)

我遇到了同样的问题。下面是一些示例代码,用于修复文件名,方法是遍历文档目录中的文件,并在保留文件名的同时替换文件路径信息。在执行最后一行来更改文件名之前,我建议使用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];
    }

答案 3 :(得分:0)

获取当前的文档目录路径

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

答案 4 :(得分:0)

您可以使用此功能来查找文档的新缺陷

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

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

重建应用程序后,直接文档直接转移到

文件:/// 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
}

答案 5 :(得分:0)

  

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

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