iOS Swift应用中WKWebView无法触发Google Drive文件下载的问题
WKWebView加载Google Drive文件无法触发下载的解决方案
问题分析
Google Drive的网页下载按钮并非直接指向文件资源链接,而是通过JavaScript触发跳转,且会进行权限验证,导致WKWebView默认的下载代理回调无法捕获下载请求。你的现有代码已经实现了WKDownloadDelegate,但缺少关键的代理配置和链接转换处理。
修复方案(WKWebView方式)
1. 完善WKWebView代理设置
iOS 14及以上版本,WKWebView新增了downloadDelegate属性,需要直接设置该代理才能正确捕获下载请求,补充代码如下:
override func viewDidLoad() { super.viewDidLoad() let configuration = WKWebViewConfiguration() webView = WKWebView(frame: self.view.bounds, configuration: configuration) webView.navigationDelegate = self // 设置downloadDelegate(iOS14+) if #available(iOS 14.0, *) { webView.downloadDelegate = self } webView.translatesAutoresizingMaskIntoConstraints = false view.addSubview(webView) // ... 原有约束代码 // 转换为直接下载链接后加载 if let driveURL = URL(string: "GOOGLEDRIVE_FILE_URL"), let directDownloadURL = convertToDirectDownloadURL(from: driveURL) { let request = URLRequest(url: directDownloadURL) webView.load(request) } }
2. 转换Google Drive链接为直接下载格式
默认的Google Drive文件查看链接(如https://drive.google.com/file/d/FILE_ID/view?usp=sharing)不会触发自动下载,需要转换为导出下载的专用链接:
func convertToDirectDownloadURL(from driveURL: URL) -> URL? { // 提取链接中的文件ID guard let dIndex = driveURL.pathComponents.firstIndex(of: "d"), dIndex + 1 < driveURL.pathComponents.count else { return nil } let fileID = driveURL.pathComponents[dIndex + 1] // 构造直接下载链接 return URL(string: "https://drive.google.com/uc?export=download&id=\(fileID)") }
备选方案:使用URLSession实现下载
如果WKWebView方案仍无法生效,直接使用URLSession发起下载请求更可靠,不受网页JS逻辑限制:
1. 发起下载任务
// 在需要触发下载的地方调用 if let driveURL = URL(string: "GOOGLEDRIVE_FILE_URL"), let directDownloadURL = convertToDirectDownloadURL(from: driveURL) { downloadFileWithURLSession(from: directDownloadURL) } func downloadFileWithURLSession(from url: URL) { let session = URLSession(configuration: .default, delegate: self, delegateQueue: nil) let downloadTask = session.downloadTask(with: url) downloadTask.resume() }
2. 实现URLSession下载代理
// 扩展ViewController实现URLSessionDownloadDelegate extension ViewController: URLSessionDownloadDelegate, URLSessionTaskDelegate { func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL) { // 保存文件到Documents目录 guard let documentsDir = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first else { return } let suggestedFilename = downloadTask.response?.suggestedFilename ?? "unknown_file" let destinationURL = documentsDir.appendingPathComponent(suggestedFilename) do { // 覆盖已存在的文件 if FileManager.default.fileExists(atPath: destinationURL.path) { try FileManager.default.removeItem(at: destinationURL) } try FileManager.default.moveItem(at: location, to: destinationURL) print("文件已保存至: \(destinationURL)") } catch { print("保存文件失败: \(error.localizedDescription)") } } func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) { if let error = error { print("下载失败: \(error.localizedDescription)") } } // 处理大文件的确认重定向 func urlSession(_ session: URLSession, task: URLSessionTask, willPerformHTTPRedirection response: HTTPURLResponse, newRequest request: URLRequest, completionHandler: @escaping (URLRequest?) -> Void) { guard response.statusCode == 302, let redirectURL = request.url else { completionHandler(request) return } // 检测是否是Google Drive的确认页面 if redirectURL.path.contains("/confirm"), let confirmParam = redirectURL.queryItems?.first(where: { $0.name == "confirm" })?.value { // 构造POST请求确认下载 var postRequest = URLRequest(url: redirectURL) postRequest.httpMethod = "POST" postRequest.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type") postRequest.httpBody = "confirm=\(confirmParam)".data(using: .utf8) completionHandler(postRequest) return } completionHandler(request) } }
关于直接URL下载的疑问
可以通过直接URL下载,但必须将Google Drive的共享链接转换为uc?export=download格式的专用下载链接。对于超过一定大小的文件,Google Drive会返回重定向到确认页面,此时需要在代码中处理该重定向(如上述URLSession代理中的逻辑),才能获取真实的文件资源链接完成下载。
内容的提问来源于stack exchange,提问作者Alise
相关产品推荐
相关产品推荐

