You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

iOS Swift多文件本地下载问题:文件存在却提示不存在

问题排查与修复方案

1. 强制解包clientUrl引发的URL失效问题

你在循环中直接用clientUrl!强制解包,若传入的"File location url"格式无效(比如缺少http/https前缀、字符串格式错误),URL(string:)会返回nil,强制解包不仅会触发崩溃,还会传入无效URL执行下载,直接导致“文件不存在”的错误。

修复代码:

if let screens = ServerDrivenUIComponent.sharedInstance.appGenericUIModel?.screens {
    for aScreen in screens {
        // 替换硬编码字符串,从aScreen中获取真实文件URL
        guard let clientUrl = URL(string: aScreen.fileUrlString) else {
            print("无效文件URL: \(aScreen.fileUrlString)")
            continue
        }
        FileDownloader.loadFileAsync(folder: self.selectedClient, url: clientUrl) { destinationPath, error in
            if let path = destinationPath {
                print("文件下载完成: \(path)")
            } else if let err = error {
                print("下载失败: \(err.localizedDescription)")
            }
        }
    }
}

2. 未使用folder参数,文件存储路径不符合预期

loadFileAsync方法接收了folder参数,但完全没用到,所有文件都直接下载到documentDirectory根目录,而非你预期的selectedClient子文件夹。如果业务逻辑要求文件存放在对应folder下,自然会出现找不到文件的情况。

修复代码:

static func loadFileAsync(folder: String, url: URL, completion: @escaping (String?, Error?) -> Void) {
    guard let documentsUrl = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first else {
        completion(nil, NSError(domain: "FileDownloadError", code: -1, userInfo: [NSLocalizedDescriptionKey: "无法获取文档目录"]))
        return
    }
    // 构建带目标文件夹的路径
    let folderUrl = documentsUrl.appendingPathComponent(folder)
    // 确保文件夹存在,不存在则创建
    do {
        try FileManager.default.createDirectory(at: folderUrl, withIntermediateDirectories: true)
    } catch {
        completion(nil, error)
        return
    }
    let destinationUrl = folderUrl.appendingPathComponent(url.lastPathComponent)
    
    // 后续原有逻辑保持不变...
}

3. HTTP状态码判断逻辑错误

当状态码不是200时,你直接传递error,但此时error是nil(因为前面判断了error == nil),导致上层无法获取真实错误原因,且打印的"File is not present"表述不准确(比如500服务器错误、403权限错误都不是文件不存在)。

修复代码:

if let statusCode = (response as? HTTPURLResponse)?.statusCode {
    if statusCode == 200 {
        do {
            try FileManager.default.copyItem(at: tempLocalUrl, to: destinationUrl)
            completion(destinationUrl.path, nil)
        } catch let writeError {
            print("文件保存失败: \(writeError)")
            completion(nil, writeError)
        }
    } else {
        let error = NSError(domain: "HTTPError", code: statusCode, userInfo: [NSLocalizedDescriptionKey: "请求失败,状态码: \(statusCode)"])
        print("请求失败: \(url.absoluteString),状态码: \(statusCode)")
        completion(nil, error)
    }
}

4. 关键错误处理缺失

  • 删除已有文件的catch块为空,无法感知删除失败的问题;
  • 下载任务的else块为空,未将下载错误传递给completion,导致上层无法知晓失败原因。

修复代码:

// 处理文件删除错误
if FileManager.default.fileExists(atPath: destinationUrl.path) {
    print("文件已存在,删除旧文件: \(destinationUrl.path)")
    do {
        try FileManager.default.removeItem(at: destinationUrl)
    } catch let deleteError {
        print("删除旧文件失败: \(deleteError)")
        completion(nil, deleteError)
        return
    }
}

// 处理下载任务错误
else {
    print("下载失败: \(error?.localizedDescription ?? "未知错误")")
    completion(nil, error)
}

5. 重复创建URLSession浪费资源

每次调用loadFileAsync都新建URLSession,会造成资源浪费,并发下载时可能出现异常,应复用URLSession.shared单例。

修复代码:

// 替换原session创建代码,使用共享单例
let task = URLSession.shared.downloadTask(with: request) { (tempLocalUrl, response, error) in
    // 后续逻辑不变...
}

内容的提问来源于stack exchange,提问作者Juno

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.19 20:05:44