使用URLSessionDownloadTask下载PDF后无法打开,求排查方案
问题分析与解决方案
首先,你的这段下载代码确实存在几个可能导致PDF损坏的问题,我来逐一拆解并给出修复方案:
代码中的核心问题
- 强制解包
fileURL!:如果传入的urlString格式无效(比如拼写错误、缺少http/https前缀),这行代码会直接崩溃,而且你没有任何错误处理逻辑。更关键的是,如果URL无效,下载任务本身就会失败,但强制解包会提前掩盖真实问题。 - 未验证HTTP响应状态:下载完成后你只检查了
error == nil,但没确认服务器返回的是成功状态码(200-299区间)。如果服务器返回404、500等错误,你得到的会是错误页面的HTML内容而非正常PDF,自然无法打开。 - 冗余的目录遍历:下载完成后遍历Documents目录找目标文件完全没必要——你已经明确知道
destinationFileUrl的路径,直接用这个URL就行,遍历操作不仅多余,还可能因目录结构变化导致找不到文件。 - 静默忽略删除失败:
try? FileManager.default.removeItem(at: destinationFileUrl)会忽略删除操作的错误(比如文件不存在),如果目标文件已存在且被占用,删除失败后复制会报错,但你没有对应的反馈逻辑。
修复后的代码
static func downloadFileFromUrl(urlString: String, fileName: String, viewController: UIViewController) { // 1. 安全解析URL,避免强制解包 guard let fileURL = URL(string: urlString) else { DispatchQueue.main.async { UIApplication.shared.keyWindow?.makeToast(message: "无效的文件URL") } return } // 2. 安全获取Documents目录路径 guard let documentsUrl = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first else { DispatchQueue.main.async { UIApplication.shared.keyWindow?.makeToast(message: "无法访问Documents目录") } return } let destinationFileUrl = documentsUrl.appendingPathComponent(fileName) let sessionConfig = URLSessionConfiguration.default let session = URLSession(configuration: sessionConfig) let request = URLRequest(url: fileURL) let task = session.downloadTask(with: request) { (tempLocalUrl, response, error) in // 3. 处理下载错误,给出具体提示 if let error = error { DispatchQueue.main.async { UIApplication.shared.keyWindow?.makeToast(message: "下载失败:\(error.localizedDescription)") } return } // 4. 验证HTTP响应状态码,确保服务器返回有效文件 guard let httpResponse = response as? HTTPURLResponse, (200...299).contains(httpResponse.statusCode) else { DispatchQueue.main.async { UIApplication.shared.keyWindow?.makeToast(message: "服务器返回错误,无法下载文件") } return } // 5. 确保临时下载文件存在 guard let tempLocalUrl = tempLocalUrl else { DispatchQueue.main.async { UIApplication.shared.keyWindow?.makeToast(message: "下载的文件无效") } return } // 6. 处理目标文件存在的情况,明确捕获删除错误 do { if FileManager.default.fileExists(atPath: destinationFileUrl.path) { try FileManager.default.removeItem(at: destinationFileUrl) } try FileManager.default.copyItem(at: tempLocalUrl, to: destinationFileUrl) // 7. 直接使用目标URL打开分享界面,无需遍历目录 DispatchQueue.main.async { let activityViewController = UIActivityViewController(activityItems: [destinationFileUrl], applicationActivities: nil) viewController.present(activityViewController, animated: true, completion: nil) } } catch let writeError { DispatchQueue.main.async { UIApplication.shared.keyWindow?.makeToast(message: "保存文件失败:\(writeError.localizedDescription)") } } } task.resume() }
额外排查步骤
除了修复代码,你还可以做这些事情定位问题:
- 直接在浏览器中打开传入的
urlString,确认能下载到正常PDF,且文件大小符合预期。 - 下载完成后,通过Xcode的
Devices and Simulators查看Documents目录下的PDF,导出到电脑尝试打开,确认是文件本身损坏还是打开方式问题。 - 对比下载后文件和服务器原文件的大小,如果差异很大,说明下载过程中数据丢失或截断。
内容的提问来源于stack exchange,提问作者Rakesh
相关产品推荐
相关产品推荐

