2
在我的应用程序,我需要有以下要求下载文件:iOS版雨燕下载大量的背景小文件
- 下载大量的(比如3000)的小PNG文件(比如说5KB)
- 一个由一个
- 如果应用程序在后台中继续下载
- 如果图像下载失败(通常是因为互联网连接已丢失),请等待X秒后再重试。如果Y次失败,则认为下载失败。
- 可以设置每次下载之间的延迟,以减少服务器负载
是iOS版能够做到这一点?我试图使用NSURLSession和NSURLSessionDownloadTask,但没有成功(我想避免同时启动3000个下载任务)。
编辑:
的ViewController:通过MwcsMac要求一些代码
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
}
}
有什么不对的代码:
- 我不确定背景部分是否正常工作。我遵循这个教程:https://www.raywenderlich.com/110458/nsurlsession-tutorial-getting-started。它说,如果我按回家,然后双击家中显示应用程序切换器,应用程序屏幕截图应该更新。似乎没有可靠的工作。当我重新打开应用程序时它会被更新。自从昨天开始使用iPhone之后,我不知道这是否是正常行为?
- 在startDownload方法中启动了3000次下载。队列的maxConcurrentOperationCount似乎并未受到尊重:下载正在并发运行
- downloadsSession.downloadTask(with:url);通话需要30ms。乘以3000,需要1mn30,这是一个大问题:/。等待几秒钟(2-3)就可以了。
- 我不能同时设置两个下载之间的延迟(这不是一个大问题。会很好,虽然,但如果我不能马上就好)
理想情况下,我会跑的startDownload方法异步,并在for循环中同步下载文件。但我想我不能在iOS的背景下做到这一点?
显示不工作的代码。 – MwcsMac
@MwcsMac,看我的编辑 –