MutiThread Download Single File in Swift

多线程下载单个文件,有人会问,为什么要用多线程下载一个文件?

因为有时候单个线程不能使带宽的充分利用,因为服务器限速了,这个时候用多线程就可以了,基本都能达到带宽的最大速度.

如果有问题,希望可以留言.源代码在Github上,可以自己下载.

核心代码:

/// Download file
public class Download: NSObject, NSURLSessionDownloadDelegate {
    
    private var url:String
    private weak var delegate:downloadDelegate?
    private var task:NSURLSessionDownloadTask?
    private var thread:Int
    private var filelocation:String
    private var fbegin:Int64
    private var fend:Int64
    
    init(url:String, filelocation:String, delegate:downloadDelegate?, _ thread:Int, _ fbegin:Int64, _ fend:Int64) {
        self.url = url
        self.delegate = delegate
        self.thread = thread
        self.filelocation = filelocation
        self.fbegin = fbegin
        self.fend = fend
    }
    
    public func xdownload() {
        let requst = NSMutableURLRequest(URL: NSURL(string: url)!)
        assert(fbegin < fend)
        requst.setValue("bytes=\(fbegin)-\(fend)", forHTTPHeaderField: "Range")/* Set rang to get different data */
        let session = NSURLSession(configuration: NSURLSessionConfiguration.defaultSessionConfiguration(), delegate: self, delegateQueue: nil)
        task = session.downloadTaskWithRequest(requst)/*, completionHandler: { (url, response, error) -> Void in
            session.finishTasksAndInvalidate()/* Invalidates the object */
            print("Close")
        })*/
        assert(task != nil, "task is nil")
        task?.resume()
    }
    
    public func xSuspend() { task?.suspend() }
    
    public func xResume() { task?.resume() }
    
    public func xStop() { task?.cancel() }
    
    public func URLSession(session: NSURLSession, downloadTask: NSURLSessionDownloadTask, didWriteData bytesWritten: Int64, totalBytesWritten: Int64, totalBytesExpectedToWrite: Int64) {
        let progressPercent = Double(totalBytesWritten) / Double(totalBytesExpectedToWrite)
        if let progressdelegate = delegate {
            progressdelegate.refresh(thread, threadprogress: progressPercent)
        }
        //print("\(thread):\(totalBytesWritten)bytes \(totalBytesExpectedToWrite)bytes")
    }
    public func URLSession(session: NSURLSession, downloadTask: NSURLSessionDownloadTask, didFinishDownloadingToURL location: NSURL) {
        session.finishTasksAndInvalidate()/* Invalidates the object */
        guard let filepath = location.path else { assert(false, "Get file location error"); return }
        guard let readhandle = NSFileHandle(forReadingAtPath: filepath) else { assert(false, "Read file error"); return }
        guard let writehandle = NSFileHandle(forWritingAtPath: filelocation) else { assert(false, "Write file error"); return }
        
        print(filepath)
        writehandle.seekToFileOffset(UInt64(fbegin))
        
        /// move file to destination
        var currentLength:UInt64 = 0
        while true {
            let data = readhandle.readDataOfLength(1024)
            writehandle.writeData(data)
            if data.length < 1024 { break }
            currentLength += 1024
            readhandle.seekToFileOffset(currentLength)
            writehandle.seekToFileOffset(currentLength)
        }
        writehandle.closeFile()
        readhandle.closeFile()
        
        /// delete temp file
        let filemanager = NSFileManager.defaultManager()
        do {
            try filemanager.removeItemAtPath(filepath)
        }catch { assert(false, "Remove file error") }
    }
}

多线程下载(创建4个线程分别下载同一个文件的不同部分):

每当线程下载完成,就将读取下载的临时文件,然后写到Downloads位置

最终拼接成一个文件.

线程采用的是异步,所以线程之间是抢占资源.

public class MutiDownload {
    
    private var url:String
    private var fileinfo:fileInfo?
    private weak var delegate:downloadDelegate?
    
    private var download = [Download]()
    
    public var name:String? { return  fileinfo == nil ? nil : fileinfo!.name }
    public var size:Int64? { return fileinfo == nil ? nil : fileinfo!.size }
    
    init(url:String, delegate:downloadDelegate?) {
        self.url = url
        self.delegate = delegate
        
        fileinfo = xGetFileInfo(url)
        assert(fileinfo != nil, "Can't get file infomation")
        //guard fileinfo != nil else { return nil }
    }
    /**
    To get file information about file size and file name
    
    - returns: fileInfo
    */
    private func xGetFileInfo(url:String) -> fileInfo? {
        let requst = NSMutableURLRequest(URL: NSURL(string: url)!)
        requst.HTTPMethod = Method.HEAD.rawValue
        var response:NSURLResponse?
        do {
            try NSURLConnection.sendSynchronousRequest(requst, returningResponse: &response)
            return fileInfo(size: response!.expectedContentLength, name: response!.suggestedFilename)
        }catch { assert(response != nil, "get file information error") }/* URL error or Network Error */
        return nil
    }
    
    public func xMutiDownload() {
        let home = NSHomeDirectory()
        let filepath = home + "/Downloads/\(name!)"
        
        /// Remove file if it exist
        let fileManager = NSFileManager.defaultManager()
        if fileManager.fileExistsAtPath(filepath) {
            do {
                try fileManager.removeItemAtPath(filepath)
            }catch { assert(false, "Remove file error") }
        }
        
        /// Create an empty file
        fileManager.createFileAtPath(filepath, contents: nil, attributes: nil)
        if let filehandle = NSFileHandle(forWritingAtPath: filepath) {
            filehandle.truncateFileAtOffset(UInt64(size!))
            filehandle.closeFile()
        }
        
        let group = dispatch_group_create()
        let queue = dispatch_queue_create("com.download.xwjack", DISPATCH_QUEUE_SERIAL)
        
        dispatch_group_async(group, queue, {
            self.download.append(Download(url: self.url, filelocation: filepath, delegate: self.delegate, 1, 0, self.fileinfo!.size / 4))
            self.download[0].xdownload()
        })
        dispatch_group_async(group, queue, {
            self.download.append(Download(url: self.url, filelocation: filepath, delegate: self.delegate, 2, self.fileinfo!.size / 4 + 1, self.fileinfo!.size / 4 * 2))
            self.download[1].xdownload()
        })
        dispatch_group_async(group, queue, {
            self.download.append(Download(url: self.url, filelocation: filepath, delegate: self.delegate, 3, self.fileinfo!.size / 4 * 2 + 1, self.fileinfo!.size / 4 * 3))
            self.download[2].xdownload()
        })
        dispatch_group_async(group, queue, {
            self.download.append(Download(url: self.url, filelocation: filepath, delegate: self.delegate, 4, self.fileinfo!.size / 4 * 3 + 1, self.fileinfo!.size))
            self.download[3].xdownload()
        })
        dispatch_group_notify(group, queue, {
            print("All Thread Begin Download")
        })
    }
    
    public func xSuspend() {
        for download in self.download {
            download.xSuspend()
        }
    }
    public func xResume() {
        for download in self.download {
            download.xResume()
        }
    }
    public func xStop() {
        for download in self.download {
            download.xStop()
        }
    }
}

源代码各部分都有注释.

如何获得文件信息?

HTTP协议HEAD中有一个字段为Range,设置Range的值就可以请求对应的数据部分,最后合并成一个文件.

public func xdownload() {
        let requst = NSMutableURLRequest(URL: NSURL(string: url)!)
        assert(fbegin < fend)
        requst.setValue("bytes=\(fbegin)-\(fend)", forHTTPHeaderField: "Range")/* Set rang to get different data */
        let session = NSURLSession(configuration: NSURLSessionConfiguration.defaultSessionConfiguration(), delegate: self, delegateQueue: nil)
        task = session.downloadTaskWithRequest(requst)/*, completionHandler: { (url, response, error) -> Void in
            session.finishTasksAndInvalidate()/* Invalidates the object */
            print("Close")
        })*/
        assert(task != nil, "task is nil")
        task?.resume()
    }

程序使用的方法都是封装好的类,只要适当调用就可以了,之后我会继续用C语言写(深入底层用套接字,不适用封装好的类,并在博客上更新.),使用封装好的类有时候是很方便,但是有时候也不方便.

如果想深入了解实现过程,还是应该用C语言写.当然最后还是C语言和Swift混合编程.

Github项目地址:MutiThreadDownload

创建时间:2016-3-17

程序采用多线程异步下载单个文件.

 

如果转载请注明出处和原文地址(http://www.cnblogs.com/xwjack1554239786/p/5289582.html).谢谢.

你可能感兴趣的:(MutiThread Download Single File in Swift)