यदि यह किसी की सहायता करता है, तो डाउनलोड को तेज़ करने के लिए मेरा कोड यहां है। यह फाइल डाउनलोड को कई फाइल पार्ट्स डाउनलोड में विभाजित करता है, जो उपलब्ध बैंडविड्थ का अधिक कुशलतापूर्वक उपयोग करता है। यह अभी भी गलत लगता है कि क्या करना है करने के लिए ...
अंतिम उपयोग की तरह है:
// task.pause is not implemented yet
let task = FileDownloadManager.shared.download(from:someUrl)
task.delegate = self
task.resume()
और यहाँ कोड है: अगर मैं एक ही बार में डाउनलोड की एक संख्या शुरू
/// Holds a weak reverence
class Weak<T: AnyObject> {
weak var value : T?
init (value: T) {
self.value = value
}
}
enum DownloadError: Error {
case missingData
}
/// Represents the download of one part of the file
fileprivate class DownloadTask {
/// The position (included) of the first byte
let startOffset: Int64
/// The position (not included) of the last byte
let endOffset: Int64
/// The byte length of the part
var size: Int64 { return endOffset - startOffset }
/// The number of bytes currently written
var bytesWritten: Int64 = 0
/// The URL task corresponding to the download
let request: URLSessionDownloadTask
/// The disk location of the saved file
var didWriteTo: URL?
init(for url: URL, from start: Int64, to end: Int64, in session: URLSession) {
startOffset = start
endOffset = end
var request = URLRequest(url: url)
request.httpMethod = "GET"
request.allHTTPHeaderFields?["Range"] = "bytes=\(start)-\(end - 1)"
self.request = session.downloadTask(with: request)
}
}
/// Represents the download of a file (that is done in multi parts)
class MultiPartsDownloadTask {
weak var delegate: MultiPartDownloadTaskDelegate?
/// the current progress, from 0 to 1
var progress: CGFloat {
var total: Int64 = 0
var written: Int64 = 0
parts.forEach({ part in
total += part.size
written += part.bytesWritten
})
guard total > 0 else { return 0 }
return CGFloat(written)/CGFloat(total)
}
fileprivate var parts = [DownloadTask]()
fileprivate var contentLength: Int64?
fileprivate let url: URL
private var session: URLSession
private var isStoped = false
private var isResumed = false
/// When the download started
private var startedAt: Date
/// An estimate on how long left before the download is over
var remainingTimeEstimate: CGFloat {
let progress = self.progress
guard progress > 0 else { return CGFloat.greatestFiniteMagnitude }
return CGFloat(Date().timeIntervalSince(startedAt))/progress * (1 - progress)
}
fileprivate init(from url: URL, in session: URLSession) {
self.url = url
self.session = session
startedAt = Date()
getRemoteResourceSize().then { [weak self] size -> Void in
guard let wself = self else { return }
wself.contentLength = size
wself.createDownloadParts()
if wself.isResumed {
wself.resume()
}
}.catch { [weak self] error in
guard let wself = self else { return }
wself.isStoped = true
}
}
/// Start the download
func resume() {
guard !isStoped else { return }
startedAt = Date()
isResumed = true
parts.forEach({ $0.request.resume() })
}
/// Cancels the download
func cancel() {
guard !isStoped else { return }
parts.forEach({ $0.request.cancel() })
}
/// Fetch the file size of a remote resource
private func getRemoteResourceSize(completion: @escaping (Int64?, Error?) -> Void) {
var headRequest = URLRequest(url: url)
headRequest.httpMethod = "HEAD"
session.dataTask(with: headRequest, completionHandler: { (data, response, error) in
if let error = error {
completion(nil, error)
return
}
guard let expectedContentLength = response?.expectedContentLength else {
completion(nil, FileCacheError.sizeNotAvailableForRemoteResource)
return
}
completion(expectedContentLength, nil)
}).resume()
}
/// Split the download request into multiple request to use more bandwidth
private func createDownloadParts() {
guard let size = contentLength else { return }
let numberOfRequests = 20
for i in 0..<numberOfRequests {
let start = Int64(ceil(CGFloat(Int64(i) * size)/CGFloat(numberOfRequests)))
let end = Int64(ceil(CGFloat(Int64(i + 1) * size)/CGFloat(numberOfRequests)))
parts.append(DownloadTask(for: url, from: start, to: end, in: session))
}
}
fileprivate func didFail(_ error: Error) {
cancel()
delegate?.didFail(self, error: error)
}
fileprivate func didFinishOnePart() {
if parts.filter({ $0.didWriteTo != nil }).count == parts.count {
mergeFiles()
}
}
/// Put together the download files
private func mergeFiles() {
let ext = self.url.pathExtension
let destination = Constants.tempDirectory
.appendingPathComponent("\(String.random(ofLength: 5))")
.appendingPathExtension(ext)
do {
let partLocations = parts.flatMap({ $0.didWriteTo })
try FileManager.default.merge(files: partLocations, to: destination)
delegate?.didFinish(self, didFinishDownloadingTo: destination)
for partLocation in partLocations {
do {
try FileManager.default.removeItem(at: partLocation)
} catch {
report(error)
}
}
} catch {
delegate?.didFail(self, error: error)
}
}
deinit {
FileDownloadManager.shared.tasks = FileDownloadManager.shared.tasks.filter({
$0.value !== self
})
}
}
protocol MultiPartDownloadTaskDelegate: class {
/// Called when the download progress changed
func didProgress(
_ downloadTask: MultiPartsDownloadTask
)
/// Called when the download finished succesfully
func didFinish(
_ downloadTask: MultiPartsDownloadTask,
didFinishDownloadingTo location: URL
)
/// Called when the download failed
func didFail(_ downloadTask: MultiPartsDownloadTask, error: Error)
}
/// Manage files downloads
class FileDownloadManager: NSObject {
static let shared = FileDownloadManager()
private var session: URLSession!
fileprivate var tasks = [Weak<MultiPartsDownloadTask>]()
private override init() {
super.init()
let config = URLSessionConfiguration.default
config.httpMaximumConnectionsPerHost = 50
session = URLSession(configuration: config, delegate: self, delegateQueue: nil)
}
/// Create a task to download a file
func download(from url: URL) -> MultiPartsDownloadTask {
let task = MultiPartsDownloadTask(from: url, in: session)
tasks.append(Weak(value: task))
return task
}
/// Returns the download task that correspond to the URL task
fileprivate func match(request: URLSessionTask) -> (MultiPartsDownloadTask, DownloadTask)? {
for wtask in tasks {
if let task = wtask.value {
for part in task.parts {
if part.request == request {
return (task, part)
}
}
}
}
return nil
}
}
extension FileDownloadManager: URLSessionDownloadDelegate {
public func urlSession(
_ session: URLSession,
downloadTask: URLSessionDownloadTask,
didWriteData bytesWritten: Int64,
totalBytesWritten: Int64,
totalBytesExpectedToWrite: Int64
) {
guard let x = match(request: downloadTask) else { return }
let multiPart = x.0
let part = x.1
part.bytesWritten = totalBytesWritten
multiPart.delegate?.didProgress(multiPart)
}
func urlSession(
_ session: URLSession,
downloadTask: URLSessionDownloadTask,
didFinishDownloadingTo location: URL
) {
guard let x = match(request: downloadTask) else { return }
let multiPart = x.0
let part = x.1
let ext = multiPart.url.pathExtension
let destination = Constants.tempDirectory
.appendingPathComponent("\(String.random(ofLength: 5))")
.appendingPathExtension(ext)
do {
try FileManager.default.moveItem(at: location, to: destination)
} catch {
multiPart.didFail(error)
return
}
part.didWriteTo = destination
multiPart.didFinishOnePart()
}
func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
guard let error = error, let multipart = match(request: task)?.0 else { return }
multipart.didFail(error)
}
}
extension FileManager {
/// Merge the files into one (without deleting the files)
func merge(files: [URL], to destination: URL, chunkSize: Int = 1000000) throws {
FileManager.default.createFile(atPath: destination.path, contents: nil, attributes: nil)
let writer = try FileHandle(forWritingTo: destination)
try files.forEach({ partLocation in
let reader = try FileHandle(forReadingFrom: partLocation)
var data = reader.readData(ofLength: chunkSize)
while data.count > 0 {
writer.write(data)
data = reader.readData(ofLength: chunkSize)
}
reader.closeFile()
})
writer.closeFile()
}
}
, नेटवर्क उपयोग ~ 800kb/s तक चला जाता है, जो मुझे – Guig
की आवश्यकता के माध्यम से आधा रास्ता है, क्या आप अपना नमूना यूआरएल प्रदान कर सकते हैं? – shallowThought
निश्चित रूप से। मैं वीडियो डाउनलोड कर रहा हूँ। एक उदाहरण हो सकता है: https://s3.amazonaws.com/mettavr/videos/patrice.sabran.z7uxwprzpvn7ltw2k/m6aXS2hqLG84HCsEg/original/IGuYW.r2zpzCh2ze.mp4 (8.5 एमबी) – Guig