文件管理器有时在文档目录中找不到子文件夹

Iva*_*ino 10 ios swift wkwebview

我以前曾问过这个问题,但不得不重新制定一切,以便更容易理解.

在我的项目中,我使用以下代码在被documents directory调用内部创建了一个子文件夹HTML:

fileprivate func createFolderOnDocumentsDirectoryIfNotExists() {
    let folderName = "HTML"
    let fileManager = FileManager.default
    if let tDocumentDirectory = fileManager.urls(for: .documentDirectory, in: .userDomainMask).first {
        let filePath =  tDocumentDirectory.appendingPathComponent("\(folderName)")
        if !fileManager.fileExists(atPath: filePath.path) {
            do {
                try fileManager.createDirectory(atPath: filePath.path, withIntermediateDirectories: true, attributes: nil)
            } catch {
                print("Couldn't create document directory")
            }
        }
        print("Document directory is \(filePath)")
    }
}
Run Code Online (Sandbox Code Playgroud)

print语句打印以下内容:

文件目录是file:/// var/mobile/Containers/Data/Application/103869C9-9D46-4595-A370-A93BCD75D495/Documents/HTML

在我的应用程序里面,我Bundle有一个.css文件供我在一个HTML字符串中使用WKWebView.

由于.css文件需要与文件夹在同一个文件夹中HTML baseURL,我将该.css文件从Bundle使用上述函数创建的目录复制到下面,代码如下:

fileprivate func copyCSSFileToHTMLFolder() {
    guard let cssURL = Bundle.main.url(forResource: "swiss", withExtension: "css") else { return }
    let fileManager = FileManager.default
    if let tDocumentDirectory = fileManager.urls(for: .documentDirectory, in: .userDomainMask).first {
        let fp = tDocumentDirectory.appendingPathComponent("HTML")
        if fileManager.fileExists(atPath: fp.path) {
            let fp2 =  fp.appendingPathComponent(cssURL.lastPathComponent)
            do {
                try fileManager.copyItem(atPath: cssURL.path, toPath: fp2.path)
            } catch {
                print("\nFailed to copy css to HTML folder with error:", error)
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

我知道我可以同时使用在应用程序的Bundle通过设置HTML baseURLmain.Bundle,但因为我需要出示里面的本地图片HTML,而不是网页链接的图像,我不得不搬到那些到子文件夹,以便我稍后复制所需的图像进入该子文件夹,因为它main.Bundle是只读的而不可写的(我们无法保存那些图像).

现在我有一个ViewController被推动了UINavigationController; 在里面推了ViewController我有一个WKWebView会呈现的HTML.

我还有一个var markdownString: String? {}属性,一旦实例化我HTML使用以下函数将其转换为字符串:

func getHTML(str: String) -> String {
    /*
      the cssImport here points to the previously copied css file as they are on the same folder
    */

    let cssImport = "<head><link rel=\"stylesheet\" type=\"text/css\" href=\"swiss.css\"></head>"
    let htmlString = try? Down(markdownString: str).toHTML()    
    return cssImport + htmlString!
}
Run Code Online (Sandbox Code Playgroud)

WKWebView用以下代码声明:

private lazy var webView: WKWebView = {
    // script to fit the content with the screen
    var scriptContent = "var meta = document.createElement('meta'); meta.setAttribute('name', 'viewport'); meta.setAttribute('content', 'width=device-width'); document.getElementsByTagName('head')[0].appendChild(meta);"

    let wkuscript = WKUserScript(source: scriptContent, injectionTime: WKUserScriptInjectionTime.atDocumentEnd, forMainFrameOnly: true)
    let wkucontroller = WKUserContentController()
    wkucontroller.addUserScript(wkuscript)

    let wkwebconfig = WKWebViewConfiguration()
    wkwebconfig.userContentController = wkucontroller

    let wv = WKWebView(frame: .zero, configuration: wkwebconfig)
    wv.frame.size.height = 1
    return wv
}()
Run Code Online (Sandbox Code Playgroud)

每当我markdown String设置,我将其转换为HTML字符串,然后我实例化,HTML baseURLwebView使用以下代码将其加载到我的:

public var markdownString: String? {
    didSet {
        guard let kPath = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first?.appendingPathComponent("HTML", isDirectory: true) else { return }

        guard let str = markdownString else { return }
        let html = getHTML(str: str)

        print("base url:", kPath)

        let fm = FileManager.default

        do {
            let items = try fm.contentsOfDirectory(atPath: kPath.path)
            print("items:", items)
        } catch {
            print("Error:", error)
        }
        webView.loadHTMLString(html, baseURL: kPath)
    }
}
Run Code Online (Sandbox Code Playgroud)

这是我的问题开始的地方.

swiss.css文件位于以下目录中:

文件:///无功/移动/集装箱/数据/应用/ 103869C9-9D46-4595-A370-A93BCD75D495 /文件/ HTML /

HTML baseURL指向以下路径的点:

文件:///无功/移动/集装箱/数据/应用/ 103869C9-9D46-4595-A370-A93BCD75D495 /文件/ HTML /

如你所见,他们都指向同一条道路.

有时我会HTML找到该swiss.css文件,因为它itemsdo{}catch{}块中打印,但有时它找不到文件夹,try fm.contentsOfDirectory(atPath: kPath.path)会在catch{}块中触发错误,打印以下内容:

错误:错误域= NSCocoaErrorDomain代码= 260"文件夹"HTML"不存在." UserInfo = {NSFilePath =/var/mobile/Containers/Data/Application/103869C9-9D46-4595-A370-A93BCD75D495/Documents/HTML,NSUserStringVariant =(Folder),NSUnderlyingError = 0x1c044e5e0 {Error Domain = NSPOSIXErrorDomain Code = 2"No such文件或目录"}}

这两种情况都发生在模拟器和真实设备上.

是什么导致了这个问题?为什么它有时会找到该文件夹​​,有时却没有找到它,即使没有重新启动应用程序,这可能会重置documents directory路径,即使我没有强制路径,而是我正在提取它,如上所示.

我不明白这种行为.

UPDATE

所以我认为html直接读取字符串可能会导致一些问题所以我改变了我的markdown: String?声明.

在其中,我将转换后的markdown字符串(不是html字符串)写入同一路径中的文件中,称为index.html.

现在有人认为发生了不同的事情 - 当我第一次推送这个视图时,它现在找到我的swiss.css文件并且html文件检测到它并按预期使用它; 到现在为止还挺好.

但是当我关闭这个视图控制器时,弹出到父视图控制器(按后退按钮),然后尝试再次按下它,相同的错误(如前所述)提示:

无法将html写入文件并显示错误:Error Domain = NSCocoaErrorDomain Code = 4"文件夹"index.html"不存在." UserInfo = {NSURL = file:///var/mobile/Containers/Data/Application/C0639153-6BA7-40E7-82CF-25BA4CB4A943/Documents/HTML/index.html,NSUserStringVariant = Folder,NSUnderlyingError = 0x1c045d850 {Error Domain = NSPOSIXErrorDomain代码= 2"没有这样的文件或目录"}}

请注意,这里的一些路径与之前的路径不同,因为它是一个不同的启动并且它会发生变化,但这不是问题,因为我从不存储路径并强制写入它,我总是使用默认的文件管理器获取URL取,

更新后的markdown声明如下:

public var markdownString: String? {
    didSet {
        guard let kPath = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first?.appendingPathComponent("HTML", isDirectory: true) else { return }

        guard let str = markdownString else { return }
        let html = getHTML(str: str)

        do {
            let writePath = kPath.appendingPathComponent("index.html")
            try html.write(to: writePath, atomically: true, encoding: .utf8)
            webView.loadFileURL(writePath, allowingReadAccessTo: kPath)
        } catch {
            print("Failed to write html to file with error:", error)
            return
        }

        let fm = FileManager.default
        do {
            let items = try fm.contentsOfDirectory(atPath: kPath.path)
            print("items:", items)
        } catch {
            print("Error:", error)
        }   
    }
}
Run Code Online (Sandbox Code Playgroud)

不知怎的,这让我更加困惑.

为什么它适用于第一个演示文稿然后它没有?

小智 1

该错误告诉您它无法写入该文件路径,这意味着它不存在。FileManager.default.fileExists(atPath: kPath.path)在尝试将 index.html 写入 writePath 之前,您应该在 markdownString 方法中添加检查来确认这一点。

确认后,您需要跟踪 HTML 文件夹创建的生命周期(只需继续检查,FileManager.default.fileExists(atPath: thePath)直到找到它消失/未创建的时间。

从您发布的代码来看,没有明显的迹象表明为什么会发生这种情况。它必须位于应用程序中的其他位置。