You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Cocoa(Swift):如何修改文件下载目标路径至下载文件夹?

修改Swift Cocoa应用的文件下载路径至系统下载文件夹

你说得没错,问题确实出在路径获取的部分!在Cocoa应用的沙盒环境中,.documentDirectory指向的是应用私有库目录下的Documents文件夹(也就是~/Library/Containers/[你的应用Bundle ID]/Data/Documents),这就是为什么你觉得文件存在Library里的原因。

要将文件保存到系统默认的下载文件夹,你需要做两个关键修改:

1. 使用正确的文件夹类型和域

替换原来的.documentDirectory.downloadsDirectory,并且确保使用.userDomainMask(指定当前用户的域,而不是.allDomainsMask,后者可能返回不符合预期的系统级路径)。

另外,不要用强制解包!,改用guard安全获取路径,避免找不到下载文件夹时崩溃。

2. 添加沙盒访问权限

在沙盒环境下,Cocoa应用访问用户的下载文件夹需要申请权限。你需要在Info.plist中添加NSDownloadsFolderUsageDescription键,填写一段说明文字(比如"需要访问下载文件夹以保存您下载的文件"),否则应用会被拒绝访问该目录。

修改后的完整代码

下面是更新后的FileDownloader类,我已经替换了路径获取逻辑,并完善了错误处理:

import Foundation
class FileDownloader {
    static func loadFileSync(url: URL, completion: @escaping (String?, Error?) -> Void) {
        // 获取用户的下载文件夹路径
        guard let downloadsUrl = FileManager.default.urls(for: .downloadsDirectory, in: .userDomainMask).first else {
            let error = NSError(domain:"FileDownloadError", code:1003, userInfo:[NSLocalizedDescriptionKey: "无法找到系统下载文件夹"])
            completion(nil, error)
            return
        }
        let destinationUrl = downloadsUrl.appendingPathComponent(url.lastPathComponent)
        
        if FileManager().fileExists(atPath: destinationUrl.path) {
            print("文件已存在 [\(destinationUrl.path)]")
            completion(destinationUrl.path, nil)
        } else if let dataFromURL = NSData(contentsOf: url) {
            if dataFromURL.write(to: destinationUrl, atomically: true) {
                print("文件已保存 [\(destinationUrl.path)]")
                completion(destinationUrl.path, nil)
            } else {
                print("保存文件失败")
                let error = NSError(domain:"FileDownloadError", code:1001, userInfo:[NSLocalizedDescriptionKey: "保存文件失败"])
                completion(destinationUrl.path, error)
            }
        } else {
            let error = NSError(domain:"FileDownloadError", code:1002, userInfo:[NSLocalizedDescriptionKey: "下载文件失败"])
            completion(destinationUrl.path, error)
        }
    }
    
    static func loadFileAsync(url: URL, completion: @escaping (String?, Error?) -> Void) {
        // 获取用户的下载文件夹路径
        guard let downloadsUrl = FileManager.default.urls(for: .downloadsDirectory, in: .userDomainMask).first else {
            let error = NSError(domain:"FileDownloadError", code:1003, userInfo:[NSLocalizedDescriptionKey: "无法找到系统下载文件夹"])
            completion(nil, error)
            return
        }
        let destinationUrl = downloadsUrl.appendingPathComponent(url.lastPathComponent)
        
        if FileManager().fileExists(atPath: destinationUrl.path) {
            print("文件已存在 [\(destinationUrl.path)]")
            completion(destinationUrl.path, nil)
        } else {
            let session = URLSession(configuration: URLSessionConfiguration.default, delegate: nil, delegateQueue: nil)
            var request = URLRequest(url: url)
            request.httpMethod = "GET"
            let task = session.dataTask(with: request, completionHandler: { data, response, error in
                if let error = error {
                    completion(destinationUrl.path, error)
                    return
                }
                
                guard let response = response as? HTTPURLResponse, response.statusCode == 200 else {
                    let statusError = NSError(domain:"FileDownloadError", code:1004, userInfo:[NSLocalizedDescriptionKey: "服务器响应错误"])
                    completion(destinationUrl.path, statusError)
                    return
                }
                
                guard let data = data else {
                    let dataError = NSError(domain:"FileDownloadError", code:1005, userInfo:[NSLocalizedDescriptionKey: "未获取到下载数据"])
                    completion(destinationUrl.path, dataError)
                    return
                }
                
                do {
                    try data.write(to: destinationUrl, options: .atomic)
                    completion(destinationUrl.path, nil)
                } catch {
                    completion(destinationUrl.path, error)
                }
            })
            task.resume()
        }
    }
}

补充说明

你之前尝试修改为.desktopDirectory没生效,大概率也是因为权限问题——访问桌面文件夹同样需要在Info.plist中添加NSDesktopFolderUsageDescription权限说明,不过既然你的需求是下载文件夹,用上面的方案就可以了。

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

火山引擎 最新活动