在我的应用中,我需要下载符合以下要求的文件:
iOS能够做到吗?我试图使用NSURLSession和NSURLSessionDownloadTask,但没有成功(我希望避免同时启动3000个下载任务)。
编辑:MwcsMac要求的一些代码:
的ViewController:
class ViewController: UIViewController, URLSessionDelegate, URLSessionDownloadDelegate {
// --------------------------------------------------------------------------------
// MARK: Attributes
lazy var downloadsSession: URLSession = {
let configuration = URLSessionConfiguration.background(withIdentifier:"bgSessionConfigurationTest");
let session = URLSession(configuration: configuration, delegate: self, delegateQueue:self.queue);
return session;
}()
lazy var queue:OperationQueue = {
let queue = OperationQueue();
queue.name = "download";
queue.maxConcurrentOperationCount = 1;
return queue;
}()
var activeDownloads = [String: Download]();
var downloadedFilesCount:Int64 = 0;
var failedFilesCount:Int64 = 0;
var totalFilesCount:Int64 = 0;
// --------------------------------------------------------------------------------
// --------------------------------------------------------------------------------
// MARK: Lifecycle
override func viewDidLoad() {
super.viewDidLoad()
startButton.addTarget(self, action:#selector(onStartButtonClick), for:UIControlEvents.touchUpInside);
_ = self.downloadsSession
_ = self.queue
}
// --------------------------------------------------------------------------------
// --------------------------------------------------------------------------------
// MARK: User interaction
@objc
private func onStartButtonClick() {
startDownload();
}
// --------------------------------------------------------------------------------
// --------------------------------------------------------------------------------
// MARK: Utils
func startDownload() {
downloadedFilesCount = 0;
totalFilesCount = 0;
for i in 0 ..< 3000 {
let urlString:String = "http://server.url/\(i).png";
let url:URL = URL(string: urlString)!;
let download = Download(url:urlString);
download.downloadTask = downloadsSession.downloadTask(with: url);
download.downloadTask!.resume();
download.isDownloading = true;
activeDownloads[download.url] = download;
totalFilesCount += 1;
}
}
// --------------------------------------------------------------------------------
// --------------------------------------------------------------------------------
// MARK: URLSessionDownloadDelegate
func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
if(error != nil) { print("didCompleteWithError \(error)"); }
failedFilesCount += 1;
}
func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL) {
if let url = downloadTask.originalRequest?.url?.absoluteString {
activeDownloads[url] = nil
}
downloadedFilesCount += 1;
[eventually do something with the file]
DispatchQueue.main.async {
[update UI]
}
if(failedFilesCount + downloadedFilesCount == totalFilesCount) {
[all files have been downloaded]
}
}
// --------------------------------------------------------------------------------
// --------------------------------------------------------------------------------
// MARK: URLSessionDelegate
func urlSessionDidFinishEvents(forBackgroundURLSession session: URLSession) {
if let appDelegate = UIApplication.shared.delegate as? AppDelegate {
if let completionHandler = appDelegate.backgroundSessionCompletionHandler {
appDelegate.backgroundSessionCompletionHandler = nil
DispatchQueue.main.async { completionHandler() }
}
}
}
// --------------------------------------------------------------------------------
}
的AppDelegate:
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
var backgroundSessionCompletionHandler: (() -> Void)?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
return true
}
func applicationWillResignActive(_ application: UIApplication) {
// Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
// Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game.
}
func applicationDidEnterBackground(_ application: UIApplication) {
// Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
// If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
}
func applicationWillEnterForeground(_ application: UIApplication) {
// Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.
}
func applicationDidBecomeActive(_ application: UIApplication) {
// Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
}
func applicationWillTerminate(_ application: UIApplication) {
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
}
func application(_ application: UIApplication, handleEventsForBackgroundURLSession identifier: String, completionHandler: @escaping () -> Void) {
backgroundSessionCompletionHandler = completionHandler
}
}
下载:
class Download: NSObject {
var url: String
var isDownloading = false
var progress: Float = 0.0
var downloadTask: URLSessionDownloadTask?
var resumeData: Data?
init(url: String) {
self.url = url
}
}
此代码有什么问题:
理想情况下,我会异步运行startDownload方法,并在for循环中同步下载文件。但我想我不能在iOS背景下做到这一点?
答案 0 :(得分:3)
所以这就是我最终做的事情:
这样,当用户离开应用程序时,下载会持续几分钟(在iOS 10上为3),并且在这3分钟过去之前暂停。如果用户在后台离开应用程序超过3分钟,他必须回来完成下载。