├── .gitignore ├── .swift-version ├── DownloaderDemo.xcodeproj ├── project.pbxproj └── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ └── IDEWorkspaceChecks.plist ├── DownloaderDemo ├── AppDelegate.h ├── AppDelegate.m ├── Base.lproj │ ├── LaunchScreen.xib │ └── Main.storyboard ├── Images.xcassets │ └── AppIcon.appiconset │ │ └── Contents.json ├── Info.plist ├── TaskCell.h ├── TaskCell.m ├── TaskCell.xib ├── TaskModel.h ├── TaskModel.m ├── ViewController.h ├── ViewController.m ├── main.m └── test.jpg ├── DownloaderDemoTests ├── DownloaderDemoTests.m └── Info.plist ├── FGDownloader.podspec ├── FGDownloader ├── FGCommon.h ├── FGHeader.h └── lib │ ├── FGDownloadManager.h │ ├── FGDownloadManager.m │ ├── FGDownloader.h │ ├── FGDownloader.m │ ├── FGTool.h │ ├── FGTool.m │ ├── FGUploadManager.h │ ├── FGUploadManager.m │ ├── FGUploader.h │ └── FGUploader.m ├── LICENSE ├── README.md └── demo.gif /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | build/ 4 | *.pbxuser 5 | *.mode1v3 6 | *.mode2v3 7 | *.perspectivev3 8 | xcuserdata 9 | *.xccheckout 10 | *.moved-aside 11 | DerivedData 12 | *.hmap 13 | *.ipa 14 | *.xcuserstate 15 | *.DS_Store 16 | # CocoaPods 17 | # 18 | # We recommend against adding the Pods directory to your .gitignore. However 19 | # you should judge for yourself, the pros and cons are mentioned at: 20 | # http://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control 21 | # 22 | # Pods/ 23 | -------------------------------------------------------------------------------- /.swift-version: -------------------------------------------------------------------------------- 1 | 2.3 2 | -------------------------------------------------------------------------------- /DownloaderDemo.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 45D99A091CF432560015623E /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 45D99A081CF432560015623E /* ViewController.m */; }; 11 | C37E0D862000793F000E92BB /* FGDownloader.m in Sources */ = {isa = PBXBuildFile; fileRef = C37E0D7F2000793F000E92BB /* FGDownloader.m */; }; 12 | C37E0D872000793F000E92BB /* FGDownloadManager.m in Sources */ = {isa = PBXBuildFile; fileRef = C37E0D812000793F000E92BB /* FGDownloadManager.m */; }; 13 | C37E0D882000793F000E92BB /* FGUploader.m in Sources */ = {isa = PBXBuildFile; fileRef = C37E0D832000793F000E92BB /* FGUploader.m */; }; 14 | C37E0D892000793F000E92BB /* FGUploadManager.m in Sources */ = {isa = PBXBuildFile; fileRef = C37E0D852000793F000E92BB /* FGUploadManager.m */; }; 15 | C37E0D8C20008140000E92BB /* FGTool.m in Sources */ = {isa = PBXBuildFile; fileRef = C37E0D8B20008140000E92BB /* FGTool.m */; }; 16 | C391A49220E48189005A476E /* test.jpg in Resources */ = {isa = PBXBuildFile; fileRef = C391A49120E48189005A476E /* test.jpg */; }; 17 | E85AD0351BAFC0E600588FC9 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = E85AD0341BAFC0E600588FC9 /* main.m */; }; 18 | E85AD0381BAFC0E600588FC9 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = E85AD0371BAFC0E600588FC9 /* AppDelegate.m */; }; 19 | E85AD03E1BAFC0E600588FC9 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = E85AD03C1BAFC0E600588FC9 /* Main.storyboard */; }; 20 | E85AD0401BAFC0E600588FC9 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = E85AD03F1BAFC0E600588FC9 /* Images.xcassets */; }; 21 | E85AD0431BAFC0E600588FC9 /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = E85AD0411BAFC0E600588FC9 /* LaunchScreen.xib */; }; 22 | E85AD04F1BAFC0E600588FC9 /* DownloaderDemoTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E85AD04E1BAFC0E600588FC9 /* DownloaderDemoTests.m */; }; 23 | E8F64B0E1BB1010000899E45 /* TaskCell.m in Sources */ = {isa = PBXBuildFile; fileRef = E8F64B0C1BB1010000899E45 /* TaskCell.m */; }; 24 | E8F64B0F1BB1010000899E45 /* TaskCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = E8F64B0D1BB1010000899E45 /* TaskCell.xib */; }; 25 | E8F64B121BB1033C00899E45 /* TaskModel.m in Sources */ = {isa = PBXBuildFile; fileRef = E8F64B111BB1033C00899E45 /* TaskModel.m */; }; 26 | /* End PBXBuildFile section */ 27 | 28 | /* Begin PBXContainerItemProxy section */ 29 | E85AD0491BAFC0E600588FC9 /* PBXContainerItemProxy */ = { 30 | isa = PBXContainerItemProxy; 31 | containerPortal = E85AD0271BAFC0E600588FC9 /* Project object */; 32 | proxyType = 1; 33 | remoteGlobalIDString = E85AD02E1BAFC0E600588FC9; 34 | remoteInfo = DownloaderDemo; 35 | }; 36 | /* End PBXContainerItemProxy section */ 37 | 38 | /* Begin PBXFileReference section */ 39 | 45D99A071CF432560015623E /* ViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; 40 | 45D99A081CF432560015623E /* ViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; 41 | C37E0D7B20007543000E92BB /* FGCommon.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FGCommon.h; sourceTree = ""; }; 42 | C37E0D7C200075DE000E92BB /* FGHeader.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FGHeader.h; sourceTree = ""; }; 43 | C37E0D7E2000793F000E92BB /* FGDownloader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FGDownloader.h; sourceTree = ""; }; 44 | C37E0D7F2000793F000E92BB /* FGDownloader.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FGDownloader.m; sourceTree = ""; }; 45 | C37E0D802000793F000E92BB /* FGDownloadManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FGDownloadManager.h; sourceTree = ""; }; 46 | C37E0D812000793F000E92BB /* FGDownloadManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FGDownloadManager.m; sourceTree = ""; }; 47 | C37E0D822000793F000E92BB /* FGUploader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FGUploader.h; sourceTree = ""; }; 48 | C37E0D832000793F000E92BB /* FGUploader.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FGUploader.m; sourceTree = ""; }; 49 | C37E0D842000793F000E92BB /* FGUploadManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FGUploadManager.h; sourceTree = ""; }; 50 | C37E0D852000793F000E92BB /* FGUploadManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FGUploadManager.m; sourceTree = ""; }; 51 | C37E0D8A20008140000E92BB /* FGTool.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FGTool.h; sourceTree = ""; }; 52 | C37E0D8B20008140000E92BB /* FGTool.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FGTool.m; sourceTree = ""; }; 53 | C391A49120E48189005A476E /* test.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = test.jpg; sourceTree = ""; }; 54 | E85AD02F1BAFC0E600588FC9 /* DownloaderDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = DownloaderDemo.app; sourceTree = BUILT_PRODUCTS_DIR; }; 55 | E85AD0331BAFC0E600588FC9 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 56 | E85AD0341BAFC0E600588FC9 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 57 | E85AD0361BAFC0E600588FC9 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 58 | E85AD0371BAFC0E600588FC9 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 59 | E85AD03D1BAFC0E600588FC9 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 60 | E85AD03F1BAFC0E600588FC9 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; 61 | E85AD0421BAFC0E600588FC9 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/LaunchScreen.xib; sourceTree = ""; }; 62 | E85AD0481BAFC0E600588FC9 /* DownloaderDemoTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = DownloaderDemoTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 63 | E85AD04D1BAFC0E600588FC9 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 64 | E85AD04E1BAFC0E600588FC9 /* DownloaderDemoTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = DownloaderDemoTests.m; sourceTree = ""; }; 65 | E8F64B0B1BB1010000899E45 /* TaskCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TaskCell.h; sourceTree = ""; }; 66 | E8F64B0C1BB1010000899E45 /* TaskCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TaskCell.m; sourceTree = ""; }; 67 | E8F64B0D1BB1010000899E45 /* TaskCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = TaskCell.xib; sourceTree = ""; }; 68 | E8F64B101BB1033C00899E45 /* TaskModel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TaskModel.h; sourceTree = ""; }; 69 | E8F64B111BB1033C00899E45 /* TaskModel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TaskModel.m; sourceTree = ""; }; 70 | /* End PBXFileReference section */ 71 | 72 | /* Begin PBXFrameworksBuildPhase section */ 73 | E85AD02C1BAFC0E600588FC9 /* Frameworks */ = { 74 | isa = PBXFrameworksBuildPhase; 75 | buildActionMask = 2147483647; 76 | files = ( 77 | ); 78 | runOnlyForDeploymentPostprocessing = 0; 79 | }; 80 | E85AD0451BAFC0E600588FC9 /* Frameworks */ = { 81 | isa = PBXFrameworksBuildPhase; 82 | buildActionMask = 2147483647; 83 | files = ( 84 | ); 85 | runOnlyForDeploymentPostprocessing = 0; 86 | }; 87 | /* End PBXFrameworksBuildPhase section */ 88 | 89 | /* Begin PBXGroup section */ 90 | C37E0D7D2000793F000E92BB /* lib */ = { 91 | isa = PBXGroup; 92 | children = ( 93 | C37E0D7E2000793F000E92BB /* FGDownloader.h */, 94 | C37E0D7F2000793F000E92BB /* FGDownloader.m */, 95 | C37E0D802000793F000E92BB /* FGDownloadManager.h */, 96 | C37E0D812000793F000E92BB /* FGDownloadManager.m */, 97 | C37E0D822000793F000E92BB /* FGUploader.h */, 98 | C37E0D832000793F000E92BB /* FGUploader.m */, 99 | C37E0D842000793F000E92BB /* FGUploadManager.h */, 100 | C37E0D852000793F000E92BB /* FGUploadManager.m */, 101 | C37E0D8A20008140000E92BB /* FGTool.h */, 102 | C37E0D8B20008140000E92BB /* FGTool.m */, 103 | ); 104 | path = lib; 105 | sourceTree = ""; 106 | }; 107 | C3888A2B1F5D47DC009EE0DD /* FGDownloader */ = { 108 | isa = PBXGroup; 109 | children = ( 110 | C37E0D7D2000793F000E92BB /* lib */, 111 | C37E0D7B20007543000E92BB /* FGCommon.h */, 112 | C37E0D7C200075DE000E92BB /* FGHeader.h */, 113 | ); 114 | path = FGDownloader; 115 | sourceTree = ""; 116 | }; 117 | E85AD0261BAFC0E600588FC9 = { 118 | isa = PBXGroup; 119 | children = ( 120 | C3888A2B1F5D47DC009EE0DD /* FGDownloader */, 121 | E85AD0311BAFC0E600588FC9 /* DownloaderDemo */, 122 | E85AD04B1BAFC0E600588FC9 /* DownloaderDemoTests */, 123 | E85AD0301BAFC0E600588FC9 /* Products */, 124 | ); 125 | sourceTree = ""; 126 | }; 127 | E85AD0301BAFC0E600588FC9 /* Products */ = { 128 | isa = PBXGroup; 129 | children = ( 130 | E85AD02F1BAFC0E600588FC9 /* DownloaderDemo.app */, 131 | E85AD0481BAFC0E600588FC9 /* DownloaderDemoTests.xctest */, 132 | ); 133 | name = Products; 134 | sourceTree = ""; 135 | }; 136 | E85AD0311BAFC0E600588FC9 /* DownloaderDemo */ = { 137 | isa = PBXGroup; 138 | children = ( 139 | E85AD0361BAFC0E600588FC9 /* AppDelegate.h */, 140 | E85AD0371BAFC0E600588FC9 /* AppDelegate.m */, 141 | 45D99A071CF432560015623E /* ViewController.h */, 142 | 45D99A081CF432560015623E /* ViewController.m */, 143 | E8F64B0B1BB1010000899E45 /* TaskCell.h */, 144 | E8F64B0C1BB1010000899E45 /* TaskCell.m */, 145 | E8F64B0D1BB1010000899E45 /* TaskCell.xib */, 146 | E8F64B101BB1033C00899E45 /* TaskModel.h */, 147 | E8F64B111BB1033C00899E45 /* TaskModel.m */, 148 | C391A49120E48189005A476E /* test.jpg */, 149 | E85AD03C1BAFC0E600588FC9 /* Main.storyboard */, 150 | E85AD03F1BAFC0E600588FC9 /* Images.xcassets */, 151 | E85AD0411BAFC0E600588FC9 /* LaunchScreen.xib */, 152 | E85AD0321BAFC0E600588FC9 /* Supporting Files */, 153 | ); 154 | path = DownloaderDemo; 155 | sourceTree = ""; 156 | }; 157 | E85AD0321BAFC0E600588FC9 /* Supporting Files */ = { 158 | isa = PBXGroup; 159 | children = ( 160 | E85AD0331BAFC0E600588FC9 /* Info.plist */, 161 | E85AD0341BAFC0E600588FC9 /* main.m */, 162 | ); 163 | name = "Supporting Files"; 164 | sourceTree = ""; 165 | }; 166 | E85AD04B1BAFC0E600588FC9 /* DownloaderDemoTests */ = { 167 | isa = PBXGroup; 168 | children = ( 169 | E85AD04E1BAFC0E600588FC9 /* DownloaderDemoTests.m */, 170 | E85AD04C1BAFC0E600588FC9 /* Supporting Files */, 171 | ); 172 | path = DownloaderDemoTests; 173 | sourceTree = ""; 174 | }; 175 | E85AD04C1BAFC0E600588FC9 /* Supporting Files */ = { 176 | isa = PBXGroup; 177 | children = ( 178 | E85AD04D1BAFC0E600588FC9 /* Info.plist */, 179 | ); 180 | name = "Supporting Files"; 181 | sourceTree = ""; 182 | }; 183 | /* End PBXGroup section */ 184 | 185 | /* Begin PBXNativeTarget section */ 186 | E85AD02E1BAFC0E600588FC9 /* DownloaderDemo */ = { 187 | isa = PBXNativeTarget; 188 | buildConfigurationList = E85AD0521BAFC0E600588FC9 /* Build configuration list for PBXNativeTarget "DownloaderDemo" */; 189 | buildPhases = ( 190 | E85AD02B1BAFC0E600588FC9 /* Sources */, 191 | E85AD02C1BAFC0E600588FC9 /* Frameworks */, 192 | E85AD02D1BAFC0E600588FC9 /* Resources */, 193 | ); 194 | buildRules = ( 195 | ); 196 | dependencies = ( 197 | ); 198 | name = DownloaderDemo; 199 | productName = DownloaderDemo; 200 | productReference = E85AD02F1BAFC0E600588FC9 /* DownloaderDemo.app */; 201 | productType = "com.apple.product-type.application"; 202 | }; 203 | E85AD0471BAFC0E600588FC9 /* DownloaderDemoTests */ = { 204 | isa = PBXNativeTarget; 205 | buildConfigurationList = E85AD0551BAFC0E600588FC9 /* Build configuration list for PBXNativeTarget "DownloaderDemoTests" */; 206 | buildPhases = ( 207 | E85AD0441BAFC0E600588FC9 /* Sources */, 208 | E85AD0451BAFC0E600588FC9 /* Frameworks */, 209 | E85AD0461BAFC0E600588FC9 /* Resources */, 210 | ); 211 | buildRules = ( 212 | ); 213 | dependencies = ( 214 | E85AD04A1BAFC0E600588FC9 /* PBXTargetDependency */, 215 | ); 216 | name = DownloaderDemoTests; 217 | productName = DownloaderDemoTests; 218 | productReference = E85AD0481BAFC0E600588FC9 /* DownloaderDemoTests.xctest */; 219 | productType = "com.apple.product-type.bundle.unit-test"; 220 | }; 221 | /* End PBXNativeTarget section */ 222 | 223 | /* Begin PBXProject section */ 224 | E85AD0271BAFC0E600588FC9 /* Project object */ = { 225 | isa = PBXProject; 226 | attributes = { 227 | LastUpgradeCheck = 0930; 228 | ORGANIZATIONNAME = "夏桂峰"; 229 | TargetAttributes = { 230 | E85AD02E1BAFC0E600588FC9 = { 231 | CreatedOnToolsVersion = 6.4; 232 | DevelopmentTeam = 9P9D2224EQ; 233 | SystemCapabilities = { 234 | com.apple.BackgroundModes = { 235 | enabled = 1; 236 | }; 237 | }; 238 | }; 239 | E85AD0471BAFC0E600588FC9 = { 240 | CreatedOnToolsVersion = 6.4; 241 | TestTargetID = E85AD02E1BAFC0E600588FC9; 242 | }; 243 | }; 244 | }; 245 | buildConfigurationList = E85AD02A1BAFC0E600588FC9 /* Build configuration list for PBXProject "DownloaderDemo" */; 246 | compatibilityVersion = "Xcode 3.2"; 247 | developmentRegion = English; 248 | hasScannedForEncodings = 0; 249 | knownRegions = ( 250 | en, 251 | Base, 252 | ); 253 | mainGroup = E85AD0261BAFC0E600588FC9; 254 | productRefGroup = E85AD0301BAFC0E600588FC9 /* Products */; 255 | projectDirPath = ""; 256 | projectRoot = ""; 257 | targets = ( 258 | E85AD02E1BAFC0E600588FC9 /* DownloaderDemo */, 259 | E85AD0471BAFC0E600588FC9 /* DownloaderDemoTests */, 260 | ); 261 | }; 262 | /* End PBXProject section */ 263 | 264 | /* Begin PBXResourcesBuildPhase section */ 265 | E85AD02D1BAFC0E600588FC9 /* Resources */ = { 266 | isa = PBXResourcesBuildPhase; 267 | buildActionMask = 2147483647; 268 | files = ( 269 | E8F64B0F1BB1010000899E45 /* TaskCell.xib in Resources */, 270 | C391A49220E48189005A476E /* test.jpg in Resources */, 271 | E85AD03E1BAFC0E600588FC9 /* Main.storyboard in Resources */, 272 | E85AD0431BAFC0E600588FC9 /* LaunchScreen.xib in Resources */, 273 | E85AD0401BAFC0E600588FC9 /* Images.xcassets in Resources */, 274 | ); 275 | runOnlyForDeploymentPostprocessing = 0; 276 | }; 277 | E85AD0461BAFC0E600588FC9 /* Resources */ = { 278 | isa = PBXResourcesBuildPhase; 279 | buildActionMask = 2147483647; 280 | files = ( 281 | ); 282 | runOnlyForDeploymentPostprocessing = 0; 283 | }; 284 | /* End PBXResourcesBuildPhase section */ 285 | 286 | /* Begin PBXSourcesBuildPhase section */ 287 | E85AD02B1BAFC0E600588FC9 /* Sources */ = { 288 | isa = PBXSourcesBuildPhase; 289 | buildActionMask = 2147483647; 290 | files = ( 291 | C37E0D882000793F000E92BB /* FGUploader.m in Sources */, 292 | C37E0D872000793F000E92BB /* FGDownloadManager.m in Sources */, 293 | 45D99A091CF432560015623E /* ViewController.m in Sources */, 294 | C37E0D862000793F000E92BB /* FGDownloader.m in Sources */, 295 | E8F64B121BB1033C00899E45 /* TaskModel.m in Sources */, 296 | E8F64B0E1BB1010000899E45 /* TaskCell.m in Sources */, 297 | C37E0D8C20008140000E92BB /* FGTool.m in Sources */, 298 | E85AD0381BAFC0E600588FC9 /* AppDelegate.m in Sources */, 299 | C37E0D892000793F000E92BB /* FGUploadManager.m in Sources */, 300 | E85AD0351BAFC0E600588FC9 /* main.m in Sources */, 301 | ); 302 | runOnlyForDeploymentPostprocessing = 0; 303 | }; 304 | E85AD0441BAFC0E600588FC9 /* Sources */ = { 305 | isa = PBXSourcesBuildPhase; 306 | buildActionMask = 2147483647; 307 | files = ( 308 | E85AD04F1BAFC0E600588FC9 /* DownloaderDemoTests.m in Sources */, 309 | ); 310 | runOnlyForDeploymentPostprocessing = 0; 311 | }; 312 | /* End PBXSourcesBuildPhase section */ 313 | 314 | /* Begin PBXTargetDependency section */ 315 | E85AD04A1BAFC0E600588FC9 /* PBXTargetDependency */ = { 316 | isa = PBXTargetDependency; 317 | target = E85AD02E1BAFC0E600588FC9 /* DownloaderDemo */; 318 | targetProxy = E85AD0491BAFC0E600588FC9 /* PBXContainerItemProxy */; 319 | }; 320 | /* End PBXTargetDependency section */ 321 | 322 | /* Begin PBXVariantGroup section */ 323 | E85AD03C1BAFC0E600588FC9 /* Main.storyboard */ = { 324 | isa = PBXVariantGroup; 325 | children = ( 326 | E85AD03D1BAFC0E600588FC9 /* Base */, 327 | ); 328 | name = Main.storyboard; 329 | sourceTree = ""; 330 | }; 331 | E85AD0411BAFC0E600588FC9 /* LaunchScreen.xib */ = { 332 | isa = PBXVariantGroup; 333 | children = ( 334 | E85AD0421BAFC0E600588FC9 /* Base */, 335 | ); 336 | name = LaunchScreen.xib; 337 | sourceTree = ""; 338 | }; 339 | /* End PBXVariantGroup section */ 340 | 341 | /* Begin XCBuildConfiguration section */ 342 | E85AD0501BAFC0E600588FC9 /* Debug */ = { 343 | isa = XCBuildConfiguration; 344 | buildSettings = { 345 | ALWAYS_SEARCH_USER_PATHS = NO; 346 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 347 | CLANG_CXX_LIBRARY = "libc++"; 348 | CLANG_ENABLE_MODULES = YES; 349 | CLANG_ENABLE_OBJC_ARC = YES; 350 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 351 | CLANG_WARN_BOOL_CONVERSION = YES; 352 | CLANG_WARN_COMMA = YES; 353 | CLANG_WARN_CONSTANT_CONVERSION = YES; 354 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 355 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 356 | CLANG_WARN_EMPTY_BODY = YES; 357 | CLANG_WARN_ENUM_CONVERSION = YES; 358 | CLANG_WARN_INFINITE_RECURSION = YES; 359 | CLANG_WARN_INT_CONVERSION = YES; 360 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 361 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 362 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 363 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 364 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 365 | CLANG_WARN_STRICT_PROTOTYPES = YES; 366 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 367 | CLANG_WARN_UNREACHABLE_CODE = YES; 368 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 369 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 370 | COPY_PHASE_STRIP = NO; 371 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 372 | ENABLE_STRICT_OBJC_MSGSEND = YES; 373 | ENABLE_TESTABILITY = YES; 374 | GCC_C_LANGUAGE_STANDARD = gnu99; 375 | GCC_DYNAMIC_NO_PIC = NO; 376 | GCC_NO_COMMON_BLOCKS = YES; 377 | GCC_OPTIMIZATION_LEVEL = 0; 378 | GCC_PREPROCESSOR_DEFINITIONS = ( 379 | "DEBUG=1", 380 | "$(inherited)", 381 | ); 382 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 383 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 384 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 385 | GCC_WARN_UNDECLARED_SELECTOR = YES; 386 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 387 | GCC_WARN_UNUSED_FUNCTION = YES; 388 | GCC_WARN_UNUSED_VARIABLE = YES; 389 | IPHONEOS_DEPLOYMENT_TARGET = 8.4; 390 | MTL_ENABLE_DEBUG_INFO = YES; 391 | ONLY_ACTIVE_ARCH = YES; 392 | SDKROOT = iphoneos; 393 | }; 394 | name = Debug; 395 | }; 396 | E85AD0511BAFC0E600588FC9 /* Release */ = { 397 | isa = XCBuildConfiguration; 398 | buildSettings = { 399 | ALWAYS_SEARCH_USER_PATHS = NO; 400 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 401 | CLANG_CXX_LIBRARY = "libc++"; 402 | CLANG_ENABLE_MODULES = YES; 403 | CLANG_ENABLE_OBJC_ARC = YES; 404 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 405 | CLANG_WARN_BOOL_CONVERSION = YES; 406 | CLANG_WARN_COMMA = YES; 407 | CLANG_WARN_CONSTANT_CONVERSION = YES; 408 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 409 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 410 | CLANG_WARN_EMPTY_BODY = YES; 411 | CLANG_WARN_ENUM_CONVERSION = YES; 412 | CLANG_WARN_INFINITE_RECURSION = YES; 413 | CLANG_WARN_INT_CONVERSION = YES; 414 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 415 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 416 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 417 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 418 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 419 | CLANG_WARN_STRICT_PROTOTYPES = YES; 420 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 421 | CLANG_WARN_UNREACHABLE_CODE = YES; 422 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 423 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 424 | COPY_PHASE_STRIP = NO; 425 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 426 | ENABLE_NS_ASSERTIONS = NO; 427 | ENABLE_STRICT_OBJC_MSGSEND = YES; 428 | GCC_C_LANGUAGE_STANDARD = gnu99; 429 | GCC_NO_COMMON_BLOCKS = YES; 430 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 431 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 432 | GCC_WARN_UNDECLARED_SELECTOR = YES; 433 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 434 | GCC_WARN_UNUSED_FUNCTION = YES; 435 | GCC_WARN_UNUSED_VARIABLE = YES; 436 | IPHONEOS_DEPLOYMENT_TARGET = 8.4; 437 | MTL_ENABLE_DEBUG_INFO = NO; 438 | SDKROOT = iphoneos; 439 | VALIDATE_PRODUCT = YES; 440 | }; 441 | name = Release; 442 | }; 443 | E85AD0531BAFC0E600588FC9 /* Debug */ = { 444 | isa = XCBuildConfiguration; 445 | buildSettings = { 446 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 447 | DEVELOPMENT_TEAM = 9P9D2224EQ; 448 | ENABLE_BITCODE = NO; 449 | INFOPLIST_FILE = DownloaderDemo/Info.plist; 450 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 451 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 452 | PRODUCT_BUNDLE_IDENTIFIER = "com.xgf.$(PRODUCT_NAME:rfc1034identifier)"; 453 | PRODUCT_NAME = "$(TARGET_NAME)"; 454 | }; 455 | name = Debug; 456 | }; 457 | E85AD0541BAFC0E600588FC9 /* Release */ = { 458 | isa = XCBuildConfiguration; 459 | buildSettings = { 460 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 461 | DEVELOPMENT_TEAM = 9P9D2224EQ; 462 | ENABLE_BITCODE = NO; 463 | INFOPLIST_FILE = DownloaderDemo/Info.plist; 464 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 465 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 466 | PRODUCT_BUNDLE_IDENTIFIER = "com.xgf.$(PRODUCT_NAME:rfc1034identifier)"; 467 | PRODUCT_NAME = "$(TARGET_NAME)"; 468 | }; 469 | name = Release; 470 | }; 471 | E85AD0561BAFC0E600588FC9 /* Debug */ = { 472 | isa = XCBuildConfiguration; 473 | buildSettings = { 474 | BUNDLE_LOADER = "$(TEST_HOST)"; 475 | FRAMEWORK_SEARCH_PATHS = ( 476 | "$(SDKROOT)/Developer/Library/Frameworks", 477 | "$(inherited)", 478 | ); 479 | GCC_PREPROCESSOR_DEFINITIONS = ( 480 | "DEBUG=1", 481 | "$(inherited)", 482 | ); 483 | INFOPLIST_FILE = DownloaderDemoTests/Info.plist; 484 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 485 | PRODUCT_BUNDLE_IDENTIFIER = "com.xgf.$(PRODUCT_NAME:rfc1034identifier)"; 486 | PRODUCT_NAME = "$(TARGET_NAME)"; 487 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/DownloaderDemo.app/DownloaderDemo"; 488 | }; 489 | name = Debug; 490 | }; 491 | E85AD0571BAFC0E600588FC9 /* Release */ = { 492 | isa = XCBuildConfiguration; 493 | buildSettings = { 494 | BUNDLE_LOADER = "$(TEST_HOST)"; 495 | FRAMEWORK_SEARCH_PATHS = ( 496 | "$(SDKROOT)/Developer/Library/Frameworks", 497 | "$(inherited)", 498 | ); 499 | INFOPLIST_FILE = DownloaderDemoTests/Info.plist; 500 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 501 | PRODUCT_BUNDLE_IDENTIFIER = "com.xgf.$(PRODUCT_NAME:rfc1034identifier)"; 502 | PRODUCT_NAME = "$(TARGET_NAME)"; 503 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/DownloaderDemo.app/DownloaderDemo"; 504 | }; 505 | name = Release; 506 | }; 507 | /* End XCBuildConfiguration section */ 508 | 509 | /* Begin XCConfigurationList section */ 510 | E85AD02A1BAFC0E600588FC9 /* Build configuration list for PBXProject "DownloaderDemo" */ = { 511 | isa = XCConfigurationList; 512 | buildConfigurations = ( 513 | E85AD0501BAFC0E600588FC9 /* Debug */, 514 | E85AD0511BAFC0E600588FC9 /* Release */, 515 | ); 516 | defaultConfigurationIsVisible = 0; 517 | defaultConfigurationName = Release; 518 | }; 519 | E85AD0521BAFC0E600588FC9 /* Build configuration list for PBXNativeTarget "DownloaderDemo" */ = { 520 | isa = XCConfigurationList; 521 | buildConfigurations = ( 522 | E85AD0531BAFC0E600588FC9 /* Debug */, 523 | E85AD0541BAFC0E600588FC9 /* Release */, 524 | ); 525 | defaultConfigurationIsVisible = 0; 526 | defaultConfigurationName = Release; 527 | }; 528 | E85AD0551BAFC0E600588FC9 /* Build configuration list for PBXNativeTarget "DownloaderDemoTests" */ = { 529 | isa = XCConfigurationList; 530 | buildConfigurations = ( 531 | E85AD0561BAFC0E600588FC9 /* Debug */, 532 | E85AD0571BAFC0E600588FC9 /* Release */, 533 | ); 534 | defaultConfigurationIsVisible = 0; 535 | defaultConfigurationName = Release; 536 | }; 537 | /* End XCConfigurationList section */ 538 | }; 539 | rootObject = E85AD0271BAFC0E600588FC9 /* Project object */; 540 | } 541 | -------------------------------------------------------------------------------- /DownloaderDemo.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /DownloaderDemo.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /DownloaderDemo/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // DownloaderDemo 4 | // 5 | // Created by xgf on 15/9/21. 6 | // Copyright (c) 2015年 xgf. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface AppDelegate : UIResponder 12 | 13 | @property (strong, nonatomic) UIWindow *window; 14 | 15 | 16 | @end 17 | 18 | -------------------------------------------------------------------------------- /DownloaderDemo/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // DownloaderDemo 4 | // 5 | // Created by xgf on 15/9/21. 6 | // Copyright (c) 2015年 xgf. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | #import "ViewController.h" 11 | 12 | @interface AppDelegate () 13 | 14 | @end 15 | 16 | @implementation AppDelegate 17 | 18 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 19 | UINavigationController *navi=[[UINavigationController alloc] initWithRootViewController:[ViewController new]]; 20 | self.window.rootViewController=navi; 21 | return YES; 22 | } 23 | 24 | 25 | @end 26 | -------------------------------------------------------------------------------- /DownloaderDemo/Base.lproj/LaunchScreen.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 24 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /DownloaderDemo/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /DownloaderDemo/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "29x29", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "40x40", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "40x40", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "60x60", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "60x60", 31 | "scale" : "3x" 32 | } 33 | ], 34 | "info" : { 35 | "version" : 1, 36 | "author" : "xcode" 37 | } 38 | } -------------------------------------------------------------------------------- /DownloaderDemo/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | NSAppTransportSecurity 26 | 27 | NSAllowsArbitraryLoads 28 | 29 | 30 | UIBackgroundModes 31 | 32 | newsstand-content 33 | 34 | UILaunchStoryboardName 35 | LaunchScreen 36 | UIMainStoryboardFile 37 | Main 38 | UIRequiredDeviceCapabilities 39 | 40 | armv7 41 | 42 | UISupportedInterfaceOrientations 43 | 44 | UIInterfaceOrientationPortrait 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /DownloaderDemo/TaskCell.h: -------------------------------------------------------------------------------- 1 | // 2 | // TaskCell.h 3 | // DownloaderDemo 4 | // 5 | // Created by xgf on 15/9/22. 6 | // Copyright (c) 2015年 xgf. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "TaskModel.h" 11 | 12 | @interface TaskCell : UITableViewCell 13 | @property (weak, nonatomic) IBOutlet UIButton *downloadBtn; 14 | @property (weak, nonatomic) IBOutlet UIProgressView *progressView; 15 | @property (weak, nonatomic) IBOutlet UILabel *nameLabel; 16 | @property (weak, nonatomic) IBOutlet UILabel *progressLabel; 17 | @property (weak, nonatomic) IBOutlet UILabel *sizeLabel; 18 | @property (weak, nonatomic) IBOutlet UILabel *speedLabel; 19 | 20 | 21 | @property(nonatomic,copy)void (^downloadBlock)(UIButton *sender); 22 | 23 | 24 | - (void)cellWithModel:(TaskModel *)model; 25 | 26 | 27 | @end 28 | -------------------------------------------------------------------------------- /DownloaderDemo/TaskCell.m: -------------------------------------------------------------------------------- 1 | // 2 | // TaskCell.m 3 | // DownloaderDemo 4 | // 5 | // Created by xgf on 15/9/22. 6 | // Copyright (c) 2015年 xgf. All rights reserved. 7 | // 8 | 9 | #import "TaskCell.h" 10 | #import "FGDownloadManager.h" 11 | 12 | @implementation TaskCell 13 | 14 | - (IBAction)downloadAction:(UIButton *)sender { 15 | if(self.downloadBlock) { 16 | self.downloadBlock(sender); 17 | } 18 | } 19 | - (void)cellWithModel:(TaskModel *)model { 20 | _nameLabel.text = model.name; 21 | _nameLabel.adjustsFontSizeToFitWidth = YES; 22 | //检查之前是否已经下载,若已经下载,获取下载进度。 23 | BOOL exist = [[NSFileManager defaultManager] fileExistsAtPath:model.destinationPath]; 24 | if(exist) { 25 | //获取原来的下载进度 26 | _progressView.progress = [[FGDownloadManager shredManager] lastProgress:model.url]; 27 | //获取原来的文件已下载部分大小及文件总大小 28 | _sizeLabel.text = [[FGDownloadManager shredManager] filesSize:model.url]; 29 | //原来的进度 30 | _progressLabel.text = [NSString stringWithFormat:@"%.2f%%",_progressView.progress*100]; 31 | } 32 | if(_progressView.progress == 1.0) { 33 | [_downloadBtn setTitle:@"完成" forState:UIControlStateNormal]; 34 | _downloadBtn.enabled = NO; 35 | } else if(_progressView.progress == 0.0) { 36 | [_downloadBtn setTitle:@"开始" forState:UIControlStateNormal]; 37 | } else {//这里是简写,正确的做法是判断是否正在下载中,若正在下载则为“暂停”,否则为“恢复” 38 | [_downloadBtn setTitle:@"恢复" forState:UIControlStateNormal]; 39 | } 40 | } 41 | @end 42 | -------------------------------------------------------------------------------- /DownloaderDemo/TaskCell.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 31 | 32 | 33 | 34 | 43 | 53 | 63 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | -------------------------------------------------------------------------------- /DownloaderDemo/TaskModel.h: -------------------------------------------------------------------------------- 1 | // 2 | // TaskModel.h 3 | // DownloaderDemo 4 | // 5 | // Created by xgf on 15/9/22. 6 | // Copyright (c) 2015年 xgf. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface TaskModel : NSObject 12 | 13 | @property(nonatomic,strong)NSString *name; 14 | @property(nonatomic,strong)NSString *url; 15 | @property(nonatomic,strong)NSString *destinationPath; 16 | 17 | + (instancetype)model; 18 | 19 | @end 20 | -------------------------------------------------------------------------------- /DownloaderDemo/TaskModel.m: -------------------------------------------------------------------------------- 1 | // 2 | // TaskModel.m 3 | // DownloaderDemo 4 | // 5 | // Created by xgf on 15/9/22. 6 | // Copyright (c) 2015年 xgf. All rights reserved. 7 | // 8 | 9 | #import "TaskModel.h" 10 | 11 | @implementation TaskModel 12 | 13 | +(instancetype)model { 14 | return [[[self class]alloc] init]; 15 | } 16 | 17 | @end 18 | -------------------------------------------------------------------------------- /DownloaderDemo/ViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.h 3 | // DownloaderDemo 4 | // 5 | // Created by xgf on 15/9/21. 6 | // Copyright (c) 2015年 xgf. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ViewController : UIViewController 12 | 13 | 14 | @end 15 | 16 | -------------------------------------------------------------------------------- /DownloaderDemo/ViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.m 3 | // DownloaderDemo 4 | // 5 | // Created by xgf on 15/9/21. 6 | // Copyright (c) 2015年 xgf. All rights reserved. 7 | // 8 | 9 | #import "ViewController.h" 10 | #import "FGHeader.h" 11 | #import "TaskCell.h" 12 | 13 | #define kWidth [UIScreen mainScreen].bounds.size.width 14 | #define kHeight [UIScreen mainScreen].bounds.size.height 15 | #define kCachePath (NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES)[0]) 16 | 17 | @interface ViewController () { 18 | NSMutableArray *_dataArray; 19 | } 20 | @property(nonatomic,strong)UITableView *tbView; 21 | 22 | @end 23 | 24 | @implementation ViewController 25 | 26 | - (void)viewDidLoad { 27 | [super viewDidLoad]; 28 | [self setup]; 29 | [self prepareData]; 30 | [self createUI]; 31 | } 32 | - (void)setup { 33 | self.view.backgroundColor=[UIColor whiteColor]; 34 | self.navigationController.navigationBar.barStyle=UIBarStyleBlackOpaque; 35 | self.automaticallyAdjustsScrollViewInsets=NO; 36 | self.title=@"Demo"; 37 | _dataArray=[NSMutableArray array]; 38 | } 39 | //添加3个任务模型 40 | - (void)prepareData { 41 | TaskModel *model=[TaskModel model]; 42 | model.name=@"GDTSDK.zip"; 43 | model.url=@"http://imgcache.qq.com/qzone/biz/gdt/dev/sdk/ios/release/GDT_iOS_SDK.zip"; 44 | model.destinationPath=[kCachePath stringByAppendingPathComponent:model.name]; 45 | [_dataArray addObject:model]; 46 | 47 | TaskModel *anotherModel=[TaskModel model]; 48 | anotherModel.name=@"CONTENT.jar"; 49 | anotherModel.url=@"http://android-mirror.bugly.qq.com:8080/eclipse_mirror/juno/content.jar"; 50 | anotherModel.destinationPath=[kCachePath stringByAppendingPathComponent:anotherModel.name]; 51 | [_dataArray addObject:anotherModel]; 52 | 53 | TaskModel *third=[TaskModel model]; 54 | third.name=@"Dota2"; 55 | third.url=@"http://dota2.dl.wanmei.com/dota2/client/DOTA2Setup20160329.zip"; 56 | third.destinationPath=[kCachePath stringByAppendingString:third.name]; 57 | [_dataArray addObject:third]; 58 | } 59 | 60 | - (void)createUI { 61 | _tbView=[[UITableView alloc] initWithFrame:CGRectMake(0, 64, kWidth, kHeight-64) style:UITableViewStylePlain]; 62 | _tbView.delegate=self; 63 | _tbView.dataSource=self; 64 | [self.view addSubview:_tbView]; 65 | 66 | UIButton *uploadBtn = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, 60, 44)]; 67 | uploadBtn.contentHorizontalAlignment = UIControlContentHorizontalAlignmentRight; 68 | [uploadBtn setTitle:@"测试上传" forState:UIControlStateNormal]; 69 | [uploadBtn setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal]; 70 | uploadBtn.titleLabel.font = [UIFont systemFontOfSize:12]; 71 | [uploadBtn addTarget:self action:@selector(testUpload:) forControlEvents:UIControlEventTouchUpInside]; 72 | UIBarButtonItem *uploadItem = [[UIBarButtonItem alloc] initWithCustomView:uploadBtn]; 73 | self.navigationItem.rightBarButtonItem = uploadItem; 74 | } 75 | 76 | //MARK: - test upload 77 | - (void)testUpload:(UIButton *)sender { 78 | sender.enabled = NO; 79 | NSString *host = @"http://games.kmbopai.com/Services/UserInfoService.asmx/UploadFile"; 80 | NSDictionary *paramaters = nil; 81 | NSString *name = @"media"; 82 | UIImage *image = [UIImage imageNamed:@"test.jpg"]; 83 | NSData *data = UIImageJPEGRepresentation(image, 1.0); 84 | [[FGUploadManager shared] upload:host 85 | parama:paramaters 86 | file:data 87 | mimeType:@"image/jpeg" 88 | fileName:@"1.jpg" 89 | name:name 90 | process:^(float progress, NSString *sizeString, NSString *speedString) { 91 | NSLog(@"上传中... 进度: %.2f",progress); 92 | } completion:^(NSData *resultData) { 93 | NSString *result = [[NSString alloc] initWithData:resultData encoding:NSUTF8StringEncoding]; 94 | NSLog(@"上传成功:\n%@",result); 95 | sender.enabled = YES; 96 | } failure:^(NSError *error) { 97 | NSLog(@"上传失败"); 98 | sender.enabled = YES; 99 | }]; 100 | } 101 | //MARK: - UITableViewDelegate 102 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { 103 | return _dataArray.count; 104 | } 105 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { 106 | static NSString *cellId=@"TaskCellID"; 107 | TaskCell *cell=[tableView dequeueReusableCellWithIdentifier:cellId]; 108 | if(!cell) 109 | cell=[[[NSBundle mainBundle] loadNibNamed:@"TaskCell" owner:nil options:nil] lastObject]; 110 | TaskModel *model=_dataArray[indexPath.row]; 111 | [cell cellWithModel:model]; 112 | //点击下载按钮时回调的代码块 113 | __weak typeof(cell) weakCell = cell; 114 | cell.downloadBlock = ^(UIButton *sender) { 115 | if([sender.currentTitle isEqualToString:@"开始"]||[sender.currentTitle isEqualToString:@"恢复"]) { 116 | [sender setTitle:@"暂停" forState:UIControlStateNormal]; 117 | //添加下载任务 118 | [[FGDownloadManager shredManager] downloadUrl:model.url toPath:model.destinationPath process:^(float progress, NSString *sizeString, NSString *speedString) { 119 | //更新进度条的进度值 120 | weakCell.progressView.progress = progress; 121 | //更新进度值文字 122 | weakCell.progressLabel.text = [NSString stringWithFormat:@"%.2f%%",progress*100]; 123 | //更新文件已下载的大小 124 | weakCell.sizeLabel.text = sizeString; 125 | //显示网速 126 | weakCell.speedLabel.text = speedString; 127 | if(speedString) 128 | weakCell.speedLabel.hidden = NO; 129 | } completion:^{ 130 | [sender setTitle:@"完成" forState:UIControlStateNormal]; 131 | sender.enabled = NO; 132 | weakCell.speedLabel.hidden = YES; 133 | UIAlertView *alert = [[UIAlertView alloc]initWithTitle:@"提示" message:[NSString stringWithFormat:@"%@下载完成✅",model.name] delegate:nil cancelButtonTitle:@"好" otherButtonTitles:nil, nil]; 134 | [alert show]; 135 | } failure:^(NSError *error) { 136 | [[FGDownloadManager shredManager] cancelDownloadTask:model.url]; 137 | [sender setTitle:@"恢复" forState:UIControlStateNormal]; 138 | weakCell.speedLabel.hidden=YES; 139 | UIAlertView *alert = [[UIAlertView alloc]initWithTitle:@"Error" message:error.localizedDescription delegate:nil cancelButtonTitle:@"确定" otherButtonTitles:nil, nil]; 140 | [alert show]; 141 | 142 | }]; 143 | } else if([sender.currentTitle isEqualToString:@"暂停"]) { 144 | [sender setTitle:@"恢复" forState:UIControlStateNormal]; 145 | [[FGDownloadManager shredManager] cancelDownloadTask:model.url]; 146 | TaskCell *cell=(TaskCell *)[tableView cellForRowAtIndexPath:indexPath]; 147 | cell.speedLabel.hidden = YES; 148 | } 149 | }; 150 | cell.selectionStyle = UITableViewCellSelectionStyleNone; 151 | return cell; 152 | } 153 | 154 | - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { 155 | return 60; 156 | } 157 | 158 | - (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath { 159 | TaskModel *model = [_dataArray objectAtIndex:indexPath.row]; 160 | [[FGDownloadManager shredManager] removeForUrl:model.url file:model.destinationPath]; 161 | [_dataArray removeObjectAtIndex:indexPath.row]; 162 | [tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic]; 163 | } 164 | - (NSString *)tableView:(UITableView *)tableView titleForDeleteConfirmationButtonForRowAtIndexPath:(NSIndexPath *)indexPath { 165 | return @"移除"; 166 | } 167 | @end 168 | -------------------------------------------------------------------------------- /DownloaderDemo/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // DownloaderDemo 4 | // 5 | // Created by xgf on 15/9/21. 6 | // Copyright (c) 2015年 xgf. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "AppDelegate.h" 11 | 12 | int main(int argc, char * argv[]) { 13 | @autoreleasepool { 14 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /DownloaderDemo/test.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Insfgg99x/FGDownloader/d2ab36ca9d9a93abee8062ae5f2b056117599a11/DownloaderDemo/test.jpg -------------------------------------------------------------------------------- /DownloaderDemoTests/DownloaderDemoTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // DownloaderDemoTests.m 3 | // DownloaderDemoTests 4 | // 5 | // Created by xgf on 15/9/21. 6 | // Copyright (c) 2015年 xgf. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | @interface DownloaderDemoTests : XCTestCase 13 | 14 | @end 15 | 16 | @implementation DownloaderDemoTests 17 | 18 | - (void)setUp { 19 | [super setUp]; 20 | // Put setup code here. This method is called before the invocation of each test method in the class. 21 | } 22 | 23 | - (void)tearDown { 24 | // Put teardown code here. This method is called after the invocation of each test method in the class. 25 | [super tearDown]; 26 | } 27 | 28 | - (void)testExample { 29 | // This is an example of a functional test case. 30 | XCTAssert(YES, @"Pass"); 31 | } 32 | 33 | - (void)testPerformanceExample { 34 | // This is an example of a performance test case. 35 | [self measureBlock:^{ 36 | // Put the code you want to measure the time of here. 37 | }]; 38 | } 39 | 40 | @end 41 | -------------------------------------------------------------------------------- /DownloaderDemoTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /FGDownloader.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = "FGDownloader" 3 | s.version = "2.2" 4 | s.summary = "FGDownloader is used for resume from break point downloading." 5 | s.homepage = "https://github.com/Insfgg99x/FGDownloader" 6 | s.license = "MIT" 7 | s.authors = { "CGPointZero" => "newbox0512@yahoo.com" } 8 | s.source = { :git => "https://github.com/Insfgg99x/FGDownloader.git", :tag => "2.2" } 9 | s.frameworks = 'Foundation','UIKit' 10 | s.platform = :ios, '6.0' 11 | s.source_files = 'FGDownloader/*.{h,m}' 12 | s.requires_arc = true 13 | #s.dependency 'SDWebImage' 14 | #s.dependency 'pop' 15 | end 16 | 17 | -------------------------------------------------------------------------------- /FGDownloader/FGCommon.h: -------------------------------------------------------------------------------- 1 | // 2 | // FGCommon.h 3 | // DownloaderDemo 4 | // 5 | // Created by xgf on 2018/1/6. 6 | // Copyright © 2018年 夏桂峰. All rights reserved. 7 | // 8 | 9 | #ifndef FCCommon_h 10 | #define FCCommon_h 11 | 12 | typedef void (^FGProcessHandle)(float progress,NSString *sizeString,NSString *speedString); 13 | typedef void (^FGUploadCompletionHandle)(NSData *resultData); 14 | typedef void (^FGDownloadCompletionHandle)(void); 15 | typedef void (^FGFailureHandle)(NSError *error); 16 | 17 | static NSString *const FGDownloadTaskDidFinishNotification = @"FGDownloadTaskDidFinishNotification"; 18 | static NSString *const FGUploadTaskDidFinishNotification = @"FGUploadTaskDidFinishNotification"; 19 | static NSString *const FGInsufficientSystemSpaceNotification = @"FGInsufficientSystemSpaceNotification"; 20 | static NSString *const FGProgressDidChangeNotificaiton = @"FGProgressDidChangeNotificaiton"; 21 | 22 | static NSString *boundary = @"FGUploaderBoundary"; 23 | static NSString *randomId = @"FGUploaderRandomId"; 24 | 25 | static NSInteger kFGDwonloadMaxTaskCount = 2; 26 | static NSInteger kFGUploaderMaxTaskCount = 2; 27 | 28 | #endif /* FCCommon_h */ 29 | -------------------------------------------------------------------------------- /FGDownloader/FGHeader.h: -------------------------------------------------------------------------------- 1 | // 2 | // FGHeader.h 3 | // DownloaderDemo 4 | // 5 | // Created by xgf on 2018/1/6. 6 | // Copyright © 2018年 夏桂峰. All rights reserved. 7 | // 8 | 9 | #ifndef FGHeader_h 10 | #define FGHeader_h 11 | 12 | #import "FGCommon.h" 13 | #import "FGDownloadManager.h" 14 | #import "FGUploadManager.h" 15 | 16 | #endif /* FGHeader_h */ 17 | -------------------------------------------------------------------------------- /FGDownloader/lib/FGDownloadManager.h: -------------------------------------------------------------------------------- 1 | // 2 | // FGDownloadManager.h 3 | // DownloaderDemo 4 | // 5 | // Created by xgf on 15/9/23. 6 | // Copyright © 2015年 xgf. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | #import "FGDownloader.h" 12 | 13 | /** 14 | * 轻量级断点续传下载管理类 15 | */ 16 | @interface FGDownloadManager : NSObject 17 | 18 | + (instancetype)shredManager; 19 | 20 | /** 21 | * 断点下载(get) 22 | * 23 | * @param urlString 下载的链接 24 | * @param destinationPath 下载的文件的保存路径 25 | * @param process 进度的回调,会多次调用 26 | * @param completion 下载完成的回调 27 | * @param failure 下载失败的回调 28 | */ 29 | - (void)downloadUrl:(NSString *)urlString 30 | toPath:(NSString *)destinationPath 31 | process:(FGProcessHandle)process 32 | completion:(FGDownloadCompletionHandle)completion 33 | failure:(FGFailureHandle)failure; 34 | /** 35 | * 断点下载(post) 36 | * 37 | * @param host 下载的链接 38 | * @param p post参数 39 | * @param destinationPath 下载的文件的保存路径 40 | * @param process 进度的回调,会多次调用 41 | * @param completion 下载完成的回调 42 | * @param failure 下载失败的回调 43 | */ 44 | - (void)downloadHost:(NSString *)host 45 | param:(NSString *)p 46 | toPath:(NSString *)destinationPath 47 | process:(FGProcessHandle)process 48 | completion:(FGDownloadCompletionHandle)completion 49 | failure:(FGFailureHandle)failure; 50 | /** 51 | * 暂停下载 52 | * 53 | * @param url 下载的链接 54 | */ 55 | - (void)cancelDownloadTask:(NSString *)url; 56 | /** 57 | * 暂停所有下载 58 | */ 59 | - (void)cancelAllTasks; 60 | /** 61 | * 彻底移除下载任务 62 | * 63 | * @param url 下载链接 64 | * @param path 文件路径 65 | */ 66 | - (void)removeForUrl:(NSString *)url file:(NSString *)path; 67 | /** 68 | * 获取上一次的下载进度 69 | * 70 | * @param url 下载链接 71 | * 72 | * @return 下载进度 73 | */ 74 | - (float)lastProgress:(NSString *)url; 75 | /** 76 | * 获取文件已下载的大小和总大小,格式为:已经下载的大小/文件总大小,如:12.00M/100.00M。 77 | * 78 | * @param url 下载链接 79 | * 80 | * @return 有文件大小及总大小组成的字符串 81 | */ 82 | - (NSString *)filesSize:(NSString *)url; 83 | 84 | 85 | @end 86 | -------------------------------------------------------------------------------- /FGDownloader/lib/FGDownloadManager.m: -------------------------------------------------------------------------------- 1 | // 2 | // FGDownloadManager.m 3 | // DownloaderDemo 4 | // 5 | // Created by xgf on 15/9/23. 6 | // Copyright © 2015年 xgf. All rights reserved. 7 | // 8 | 9 | #import "FGDownloadManager.h" 10 | 11 | /**最大同时下载任务数,超过将自动存入排队对列中*/ 12 | #define kFGDwonloadMaxTaskCount 2 13 | 14 | static FGDownloadManager *mgr = nil; 15 | 16 | @implementation FGDownloadManager { 17 | NSMutableDictionary *_taskDict; 18 | /**排队对列*/ 19 | NSMutableArray *_queue; 20 | /** 后台进程id*/ 21 | UIBackgroundTaskIdentifier _backgroudTaskId; 22 | } 23 | 24 | - (instancetype)init { 25 | if(self = [super init]) { 26 | _taskDict = [NSMutableDictionary dictionary]; 27 | _queue = [NSMutableArray array]; 28 | _backgroudTaskId = UIBackgroundTaskInvalid; 29 | //注册系统内存不足的通知 30 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(systemSpaceInsufficient:) name:FGInsufficientSystemSpaceNotification object:nil]; 31 | //注册程序下载完成的通知 32 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(downloadTaskDidFinishDownloading:) name:FGDownloadTaskDidFinishNotification object:nil]; 33 | //注册程序即将失去焦点的通知 34 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(downloadTaskWillResign:) name:UIApplicationWillResignActiveNotification object:nil]; 35 | //注册程序获得焦点的通知 36 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(downloadTaskDidBecomActive:) name:UIApplicationDidBecomeActiveNotification object:nil]; 37 | //注册程序即将被终结的通知 38 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(downloadTaskWillBeTerminate:) name:UIApplicationWillTerminateNotification object:nil]; 39 | } 40 | return self; 41 | } 42 | 43 | /** 44 | * 收到系统存储空间不足的通知调用的方法 45 | * 46 | * @param sender 系统存储空间不足的通知 47 | */ 48 | - (void)systemSpaceInsufficient:(NSNotification *)sender { 49 | NSString *urlString = [sender.userInfo objectForKey:@"urlString"]; 50 | [[FGDownloadManager shredManager] cancelDownloadTask:urlString]; 51 | } 52 | 53 | /** 54 | * 收到程序即将失去焦点的通知,开启后台运行 55 | * 56 | * @param sender 通知 57 | */ 58 | - (void)downloadTaskWillResign:(NSNotification *)sender { 59 | if(_taskDict.count > 0) { 60 | _backgroudTaskId = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{ 61 | 62 | }]; 63 | } 64 | } 65 | 66 | /** 67 | * 收到程序重新得到焦点的通知,关闭后台 68 | * 69 | * @param sender 通知 70 | */ 71 | - (void)downloadTaskDidBecomActive:(NSNotification *)sender { 72 | if(_backgroudTaskId != UIBackgroundTaskInvalid) { 73 | [[UIApplication sharedApplication] endBackgroundTask:_backgroudTaskId]; 74 | _backgroudTaskId = UIBackgroundTaskInvalid; 75 | } 76 | } 77 | 78 | /** 79 | * 程序将要结束时,取消下载 80 | * 81 | * @param sender 通知 82 | */ 83 | - (void)downloadTaskWillBeTerminate:(NSNotification *)sender { 84 | [[FGDownloadManager shredManager] cancelAllTasks]; 85 | } 86 | 87 | /** 88 | * 下载完成通知调用的方法 89 | * 90 | * @param sender 通知 91 | */ 92 | - (void)downloadTaskDidFinishDownloading:(NSNotification *)sender { 93 | //下载完成后,从任务列表中移除下载任务,若总任务数小于最大同时下载任务数, 94 | //则从排队对列中取出一个任务,进入下载 95 | NSString *urlString = [sender.userInfo objectForKey:@"urlString"]; 96 | @synchronized (_taskDict) { 97 | [_taskDict removeObjectForKey:urlString]; 98 | } 99 | if(_taskDict.count < kFGDwonloadMaxTaskCount) { 100 | if(_queue.count > 0) { 101 | @synchronized(_queue) { 102 | NSDictionary *first = [_queue objectAtIndex:0]; 103 | [self downloadUrl:first[@"urlString"] 104 | toPath:first[@"destinationPath"] 105 | process:first[@"process"] 106 | completion:first[@"completion"] 107 | failure:first[@"failure"]]; 108 | //从排队对列中移除一个下载任务 109 | [_queue removeObjectAtIndex:0]; 110 | } 111 | } 112 | } 113 | } 114 | 115 | + (instancetype)shredManager { 116 | static dispatch_once_t onceToken; 117 | dispatch_once(&onceToken, ^{ 118 | mgr = [[FGDownloadManager alloc]init]; 119 | }); 120 | return mgr; 121 | } 122 | 123 | - (void)downloadUrl:(NSString *)urlString toPath:(NSString *)destinationPath process:(FGProcessHandle)process completion:(FGDownloadCompletionHandle)completion failure:(FGFailureHandle)failure { 124 | //若同时下载的任务数超过最大同时下载任务数, 125 | //则把下载任务存入对列,在下载完成后,自动进入下载。 126 | if(_taskDict.count >= kFGDwonloadMaxTaskCount) { 127 | NSDictionary *dict = @{@"urlString":urlString, 128 | @"destinationPath":destinationPath, 129 | @"process":process, 130 | @"completion":completion, 131 | @"failure":failure}; 132 | [_queue addObject:dict]; 133 | return; 134 | } 135 | FGDownloader *downloader = [FGDownloader downloader]; 136 | @synchronized (self) { 137 | [_taskDict setObject:downloader forKey:urlString]; 138 | } 139 | [downloader downloadUrl:urlString 140 | toPath:destinationPath 141 | process:process 142 | completion:completion 143 | failure:failure]; 144 | } 145 | 146 | - (void)downloadHost:(NSString *)host 147 | param:(NSString *)p 148 | toPath:(NSString *)destinationPath 149 | process:(FGProcessHandle)process 150 | completion:(FGDownloadCompletionHandle)completion 151 | failure:(FGFailureHandle)failure { 152 | //若同时下载的任务数超过最大同时下载任务数, 153 | //则把下载任务存入对列,在下载完成后,自动进入下载。 154 | NSString *tmpUrl = @""; 155 | if(p != nil) { 156 | tmpUrl = [NSString stringWithFormat:@"%@?%@",host,p]; 157 | } else { 158 | tmpUrl = host; 159 | } 160 | if(_taskDict.count >= kFGDwonloadMaxTaskCount) { 161 | NSDictionary *dict = @{@"urlString":tmpUrl, 162 | @"destinationPath":destinationPath, 163 | @"process":process, 164 | @"completion":completion, 165 | @"failure":failure}; 166 | [_queue addObject:dict]; 167 | return; 168 | } 169 | FGDownloader *downloader = [FGDownloader downloader]; 170 | @synchronized (self) { 171 | [_taskDict setObject:downloader forKey:tmpUrl]; 172 | } 173 | [downloader downloadHost:host 174 | param:p 175 | toPath:destinationPath 176 | process:process 177 | completion:completion 178 | failure:failure]; 179 | } 180 | /** 181 | * 取消下载任务 182 | * 183 | * @param url 下载的链接 184 | */ 185 | - (void)cancelDownloadTask:(NSString *)url { 186 | FGDownloader *downloader = [_taskDict objectForKey:url]; 187 | [downloader cancel]; 188 | @synchronized (self) { 189 | [_taskDict removeObjectForKey:url]; 190 | } 191 | if(_queue.count > 0) { 192 | NSDictionary *first = [_queue objectAtIndex:0]; 193 | [self downloadUrl:first[@"urlString"] 194 | toPath:first[@"destinationPath"] 195 | process:first[@"process"] 196 | completion:first[@"completion"] 197 | failure:first[@"failure"]]; 198 | //从排队对列中移除一个下载任务 199 | [_queue removeObjectAtIndex:0]; 200 | } 201 | } 202 | 203 | /** 204 | * 彻底移除下载任务 205 | * 206 | * @param url 下载链接 207 | * @param path 文件路径 208 | */ 209 | - (void)removeForUrl:(NSString *)url file:(NSString *)path { 210 | FGDownloader *downloader = [_taskDict objectForKey:url]; 211 | if(downloader) { 212 | [downloader cancel]; 213 | } 214 | @synchronized (self) { 215 | [_taskDict removeObjectForKey:url]; 216 | } 217 | NSUserDefaults *usd = [NSUserDefaults standardUserDefaults]; 218 | NSString *totalLebgthKey = [NSString stringWithFormat:@"%@totalLength",url]; 219 | NSString *progressKey = [NSString stringWithFormat:@"%@progress",url]; 220 | [usd removeObjectForKey:totalLebgthKey]; 221 | [usd removeObjectForKey:progressKey]; 222 | [usd synchronize]; 223 | NSFileManager *fileManager = [NSFileManager defaultManager]; 224 | BOOL fileExist = [fileManager fileExistsAtPath:path]; 225 | if(fileExist) { 226 | [fileManager removeItemAtPath:path error:nil]; 227 | } 228 | } 229 | 230 | - (void)cancelAllTasks { 231 | NSMutableArray *keys = [NSMutableArray array]; 232 | @synchronized(_taskDict) { 233 | [_taskDict enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) { 234 | FGDownloader *downloader = obj; 235 | [downloader cancel]; 236 | [keys addObject:key]; 237 | }]; 238 | [_taskDict removeObjectsForKeys:keys]; 239 | } 240 | } 241 | 242 | - (float)lastProgress:(NSString *)url { 243 | return [FGDownloader lastProgress:url]; 244 | } 245 | 246 | - (NSString *)filesSize:(NSString *)url { 247 | return [FGDownloader filesSize:url]; 248 | } 249 | 250 | - (void)dealloc { 251 | [[NSNotificationCenter defaultCenter] removeObserver:self]; 252 | } 253 | 254 | @end 255 | -------------------------------------------------------------------------------- /FGDownloader/lib/FGDownloader.h: -------------------------------------------------------------------------------- 1 | // 2 | // FGDownloader.h 3 | // DownloaderDemo 4 | // 5 | // Created by xgf on 15/9/21. 6 | // Copyright (c) 2015年 峰哥哥. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "FGCommon.h" 11 | 12 | /**请使用FGDownloadManager*/ 13 | @interface FGDownloader : NSObject 14 | 15 | @property(nonatomic,strong)NSURLConnection *con; 16 | @property(nonatomic,copy,readonly)FGProcessHandle process;//下载进度回调(会多次调用) 17 | @property(nonatomic,copy,readonly)FGDownloadCompletionHandle completion; 18 | @property(nonatomic,copy,readonly)FGFailureHandle failure; 19 | 20 | + (instancetype)downloader; 21 | /** 22 | * 断点下载(get) 23 | * 24 | * @param urlString 下载的链接 25 | * @param destinationPath 下载的文件的保存路径 26 | * @param process 进度的回调,会多次调用 27 | * @param completion 下载完成的回调 28 | * @param failure 下载失败的回调 29 | */ 30 | - (void)downloadUrl:(NSString *)urlString 31 | toPath:(NSString *)destinationPath 32 | process:(FGProcessHandle)process 33 | completion:(FGDownloadCompletionHandle)completion 34 | failure:(FGFailureHandle)failure; 35 | 36 | /** 37 | * 断点下载(post) 38 | * 39 | * @param host 下载的链接 40 | * @param p post参数 41 | * @param destinationPath 下载的文件的保存路径 42 | * @param process 进度的回调,会多次调用 43 | * @param completion 下载完成的回调 44 | * @param failure 下载失败的回调 45 | */ 46 | - (void)downloadHost:(NSString *)host 47 | param:(NSString *)p 48 | toPath:(NSString *)destinationPath 49 | process:(FGProcessHandle)process 50 | completion:(FGDownloadCompletionHandle)completion 51 | failure:(FGFailureHandle)failure; 52 | /**取消下载*/ 53 | - (void)cancel; 54 | /**获取上一次的下载进度*/ 55 | + (float)lastProgress:(NSString *)url; 56 | /**已下载的大小/文件总大小,如:12.00M/100.00M*/ 57 | + (NSString *)filesSize:(NSString *)url; 58 | 59 | @end 60 | -------------------------------------------------------------------------------- /FGDownloader/lib/FGDownloader.m: -------------------------------------------------------------------------------- 1 | // 2 | // FGDownloader.m 3 | // DownloaderDemo 4 | // 5 | // Created by xgf on 15/9/21. 6 | // Copyright (c) 2015年 xgf. All rights reserved. 7 | // 8 | 9 | #import "FGDownloader.h" 10 | #import "FGTool.h" 11 | #import 12 | 13 | @implementation FGDownloader { 14 | NSString *_url_string; 15 | NSString *_destination_path; 16 | NSFileHandle *_writeHandle; 17 | NSURLConnection *_con; 18 | long long _lastSize; 19 | long long _growth; 20 | NSTimer *_timer; 21 | } 22 | //计算一次文件大小增加部分的尺寸 23 | - (void)getGrowthSize { 24 | long long size = (long long)[[[[NSFileManager defaultManager] attributesOfItemAtPath:_destination_path error:nil] objectForKey:NSFileSize] integerValue]; 25 | _growth = size-_lastSize; 26 | _lastSize = size; 27 | } 28 | 29 | - (instancetype)init { 30 | if(self = [super init]) { 31 | //每0.5秒计算一次文件大小增加部分的尺寸 32 | _timer = [NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:@selector(getGrowthSize) userInfo:nil repeats:YES]; 33 | } 34 | return self; 35 | } 36 | /** 37 | * 获取对象的类方法 38 | */ 39 | + (instancetype)downloader { 40 | return [[[self class] alloc]init]; 41 | } 42 | /** 43 | * 断点下载 44 | * 45 | * @param urlString 下载的链接 46 | * @param destinationPath 下载的文件的保存路径 47 | * @param process 下载过程中回调,会多次调用 48 | * @param completion 下载完成的回调 49 | * @param failure 下载失败的回调 50 | */ 51 | - (void)downloadUrl:(NSString *)urlString 52 | toPath:(NSString *)destinationPath 53 | process:(FGProcessHandle)process 54 | completion:(FGDownloadCompletionHandle)completion 55 | failure:(FGFailureHandle)failure { 56 | if(urlString && destinationPath) { 57 | _url_string = urlString; 58 | _destination_path = destinationPath; 59 | _process = process; 60 | _completion = completion; 61 | _failure = failure; 62 | NSURL *url = [NSURL URLWithString:urlString]; 63 | NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url]; 64 | NSFileManager *fileManager = [NSFileManager defaultManager]; 65 | BOOL fileExist = [fileManager fileExistsAtPath:destinationPath]; 66 | if(fileExist) { 67 | long long length = (long long)[[[fileManager attributesOfItemAtPath:destinationPath error:nil] objectForKey:NSFileSize] integerValue]; 68 | NSString *rangeString = [NSString stringWithFormat:@"bytes=%lld-",length]; 69 | [request setValue:rangeString forHTTPHeaderField:@"Range"]; 70 | } 71 | _con = [NSURLConnection connectionWithRequest:request delegate:self]; 72 | } 73 | } 74 | 75 | - (void)downloadHost:(NSString *)host 76 | param:(NSString *)p 77 | toPath:(NSString *)destinationPath 78 | process:(FGProcessHandle)process 79 | completion:(FGDownloadCompletionHandle)completion 80 | failure:(FGFailureHandle)failure { 81 | if(host && destinationPath) { 82 | if(p != nil) { 83 | _url_string = [NSString stringWithFormat:@"%@?%@",host,p]; 84 | } else { 85 | _url_string = host; 86 | } 87 | _destination_path = destinationPath; 88 | _process = process; 89 | _completion = completion; 90 | _failure = failure; 91 | NSURL *url = [NSURL URLWithString:host]; 92 | NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url]; 93 | request.HTTPMethod = @"POST"; 94 | if(p != nil) { 95 | request.HTTPBody = [p dataUsingEncoding:NSUTF8StringEncoding]; 96 | } 97 | NSFileManager *fileManager = [NSFileManager defaultManager]; 98 | BOOL fileExist = [fileManager fileExistsAtPath:destinationPath]; 99 | if(fileExist) { 100 | long long length = (long long)[[[fileManager attributesOfItemAtPath:destinationPath error:nil] objectForKey:NSFileSize] integerValue]; 101 | NSString *rangeString = [NSString stringWithFormat:@"bytes=%lld-",length]; 102 | [request setValue:rangeString forHTTPHeaderField:@"Range"]; 103 | } 104 | _con = [NSURLConnection connectionWithRequest:request delegate:self]; 105 | } 106 | } 107 | 108 | /** 109 | * 取消下载 110 | */ 111 | - (void)cancel { 112 | [self.con cancel]; 113 | self.con = nil; 114 | if(_timer) { 115 | [_timer invalidate]; 116 | } 117 | } 118 | /** 119 | * 获取上一次的下载进度 120 | */ 121 | + (float)lastProgress:(NSString *)url { 122 | if(url) { 123 | return [[NSUserDefaults standardUserDefaults] floatForKey:[NSString stringWithFormat:@"%@progress",url]]; 124 | } 125 | return 0.0; 126 | } 127 | 128 | /**获取文件已下载的大小和总大小,格式为:已经下载的大小/文件总大小,如:12.00M/100.00M 129 | */ 130 | + (NSString *)filesSize:(NSString *)url { 131 | NSString *totalLebgthKey = [NSString stringWithFormat:@"%@totalLength",url]; 132 | NSUserDefaults *usd = [NSUserDefaults standardUserDefaults]; 133 | long long totalLength = (long long)[usd integerForKey:totalLebgthKey]; 134 | if(totalLength == 0) { 135 | return @"0.00K/0.00K"; 136 | } 137 | NSString *progressKey = [NSString stringWithFormat:@"%@progress",url]; 138 | float progress = [[NSUserDefaults standardUserDefaults] floatForKey:progressKey]; 139 | long long currentLength = progress*totalLength; 140 | NSString *currentSize = [FGTool convertSize:currentLength]; 141 | NSString *totalSize = [FGTool convertSize:totalLength]; 142 | return [NSString stringWithFormat:@"%@/%@",currentSize,totalSize]; 143 | } 144 | 145 | /** 146 | * 获取系统可用存储空间 147 | * 148 | * @return 系统空用存储空间,单位:字节 149 | */ 150 | - (long long)systemFreeSpace { 151 | NSString *docPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject]; 152 | NSDictionary *dict = [[NSFileManager defaultManager] attributesOfFileSystemForPath:docPath error:nil]; 153 | return (long long)[[dict objectForKey:NSFileSystemFreeSize] integerValue]; 154 | } 155 | 156 | #pragma mark - NSURLConnection 157 | /** 158 | * 下载失败 159 | */ 160 | - (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error { 161 | if(_failure) { 162 | _failure(error); 163 | } 164 | } 165 | 166 | /** 167 | * 接收到响应请求 168 | */ 169 | - (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response { 170 | NSString *key = [NSString stringWithFormat:@"%@totalLength",_url_string]; 171 | NSUserDefaults *usd = [NSUserDefaults standardUserDefaults]; 172 | long long totalLength = (long long)[usd integerForKey:key]; 173 | if(totalLength == 0) { 174 | [usd setInteger:response.expectedContentLength forKey:key]; 175 | [usd synchronize]; 176 | } 177 | NSFileManager *fileManager = [NSFileManager defaultManager]; 178 | BOOL fileExist = [fileManager fileExistsAtPath:_destination_path]; 179 | if(!fileExist) { 180 | [fileManager createFileAtPath:_destination_path contents:nil attributes:nil]; 181 | } 182 | _writeHandle = [NSFileHandle fileHandleForWritingAtPath:_destination_path]; 183 | } 184 | 185 | /** 186 | * 下载过程,会多次调用 187 | */ 188 | - (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data { 189 | [_writeHandle seekToEndOfFile]; 190 | long long freeSpace=[self systemFreeSpace]; 191 | if(freeSpace<1024*1024*20) { 192 | UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"提示" message:@"系统可用存储空间不足20M" preferredStyle:UIAlertControllerStyleAlert]; 193 | UIAlertAction *confirm = [UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleDefault handler:nil]; 194 | [alertController addAction:confirm]; 195 | [[UIApplication sharedApplication].keyWindow.rootViewController presentViewController:alertController animated:YES completion:nil]; 196 | //发送系统存储空间不足的通知,用户可自行注册该通知,收到通知时,暂停下载,并更新界面 197 | [[NSNotificationCenter defaultCenter] postNotificationName:FGInsufficientSystemSpaceNotification object:nil userInfo:@{@"urlString":_url_string}]; 198 | return; 199 | } 200 | [_writeHandle writeData:data]; 201 | long long length = (long long)[[[[NSFileManager defaultManager] attributesOfItemAtPath:_destination_path error:nil] objectForKey:NSFileSize] integerValue]; 202 | NSString *key = [NSString stringWithFormat:@"%@totalLength",_url_string]; 203 | long long totalLength = (long long)[[NSUserDefaults standardUserDefaults] integerForKey:key]; 204 | //计算下载进度 205 | float progress = (float)length/totalLength; 206 | [[NSUserDefaults standardUserDefaults] setFloat:progress forKey:[NSString stringWithFormat:@"%@progress",_url_string]]; 207 | [[NSUserDefaults standardUserDefaults] synchronize]; 208 | //获取文件大小,格式为:格式为:已经下载的大小/文件总大小,如:12.00M/100.00M 209 | NSString *sizeString = [FGDownloader filesSize:_url_string]; 210 | //发送进度改变的通知(一般情况下不需要用到,只有在触发下载与显示下载进度在不同界面的时候才会用到) 211 | NSDictionary *userInfo = @{@"url" : _url_string, 212 | @"progress" : @(progress), 213 | @"sizeString" : sizeString}; 214 | [[NSNotificationCenter defaultCenter] postNotificationName:FGProgressDidChangeNotificaiton object:nil userInfo:userInfo]; 215 | //计算网速 216 | NSString *speedString = @"0.00Kb/s"; 217 | NSString *growString = [FGTool convertSize:_growth*(1.0/0.1)]; 218 | speedString = [NSString stringWithFormat:@"%@/s",growString]; 219 | //回调下载过程中的代码块 220 | if(_process) { 221 | _process(progress,sizeString,speedString); 222 | } 223 | } 224 | 225 | /** 226 | * 下载完成 227 | */ 228 | - (void)connectionDidFinishLoading:(NSURLConnection *)connection { 229 | [[NSNotificationCenter defaultCenter] postNotificationName:FGDownloadTaskDidFinishNotification object:nil userInfo:@{@"urlString":_url_string}]; 230 | if(_completion) { 231 | _completion(); 232 | } 233 | } 234 | 235 | @end 236 | -------------------------------------------------------------------------------- /FGDownloader/lib/FGTool.h: -------------------------------------------------------------------------------- 1 | // 2 | // FGTool.h 3 | // DownloaderDemo 4 | // 5 | // Created by xgf on 2018/1/6. 6 | // Copyright © 2018年 夏桂峰. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface FGTool : NSObject 12 | /**将文件大小(bytes)转换成string*/ 13 | + (NSString *)convertSize:(long long)length; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /FGDownloader/lib/FGTool.m: -------------------------------------------------------------------------------- 1 | // 2 | // FGTool.m 3 | // DownloaderDemo 4 | // 5 | // Created by xgf on 2018/1/6. 6 | // Copyright © 2018年 夏桂峰. All rights reserved. 7 | // 8 | 9 | #import "FGTool.h" 10 | 11 | @implementation FGTool 12 | + (NSString *)convertSize:(long long)length { 13 | if(length<1024) { 14 | return [NSString stringWithFormat:@"%lldB",length]; 15 | }else if(length>=1024&&length<1024*1024) { 16 | return [NSString stringWithFormat:@"%.0fK",(float)length/1024]; 17 | }else if(length >=1024*1024&&length<1024*1024*1024) { 18 | return [NSString stringWithFormat:@"%.1fM",(float)length/(1024*1024)]; 19 | }else { 20 | return [NSString stringWithFormat:@"%.1fG",(float)length/(1024*1024*1024)]; 21 | } 22 | } 23 | @end 24 | -------------------------------------------------------------------------------- /FGDownloader/lib/FGUploadManager.h: -------------------------------------------------------------------------------- 1 | // 2 | // FGUploadManager.h 3 | // DownloaderDemo 4 | // 5 | // Created by xgf on 2018/1/6. 6 | // Copyright © 2018年 xgf. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "FGUploader.h" 11 | 12 | @interface FGUploadManager : NSObject 13 | 14 | + (instancetype)shared; 15 | 16 | /** 17 | * 上传 18 | * @param host 服务器地址 19 | * @param data 文件二进制数据 20 | * @param p post请求的参数 21 | * @param fileName 文件名(如1.jpg) 22 | * @param name 服务器文件的变量名 23 | * @param mimeType 文件的mimeType(如image/jpeg) 24 | * @param process 进度的回调(会多次调用) 25 | * @param completion 成功的回调 26 | * @param failure 失败的回调 27 | */ 28 | - (void)upload:(NSString *)host 29 | parama:(NSDictionary *)p 30 | file:(NSData *)data 31 | mimeType:(NSString *)type 32 | fileName:(NSString *)n1 33 | name:(NSString *)n2 34 | process:(FGProcessHandle)process 35 | completion:(FGUploadCompletionHandle)completion 36 | failure:(FGFailureHandle)failure; 37 | 38 | - (void)cancelTask:(NSString *)url; 39 | 40 | - (void)cancelAllTasks; 41 | 42 | @end 43 | -------------------------------------------------------------------------------- /FGDownloader/lib/FGUploadManager.m: -------------------------------------------------------------------------------- 1 | // 2 | // FGUploadManager.m 3 | // DownloaderDemo 4 | // 5 | // Created by xgf on 2018/1/6. 6 | // Copyright © 2018年 xgf. All rights reserved. 7 | // 8 | 9 | #import "FGUploadManager.h" 10 | #import 11 | 12 | static FGUploadManager *uploadMgr = nil; 13 | 14 | @implementation FGUploadManager { 15 | NSMutableDictionary *_taskDict; 16 | /**排队对列*/ 17 | NSMutableArray *_queue; 18 | /** 后台进程id*/ 19 | UIBackgroundTaskIdentifier _backgroudTaskId; 20 | } 21 | 22 | + (instancetype)shared { 23 | static dispatch_once_t onceToken; 24 | dispatch_once(&onceToken, ^{ 25 | uploadMgr = [[FGUploadManager alloc] init]; 26 | }); 27 | return uploadMgr; 28 | } 29 | 30 | - (instancetype)init { 31 | if(self = [super init]) { 32 | _taskDict = [NSMutableDictionary dictionary]; 33 | _queue = [NSMutableArray array]; 34 | _backgroudTaskId = UIBackgroundTaskInvalid; 35 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(taskDidFinishUploading:) name:FGUploadTaskDidFinishNotification object:nil]; 36 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(taskWillResign:) name:UIApplicationWillResignActiveNotification object:nil]; 37 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(taskDidBecomActive:) name:UIApplicationDidBecomeActiveNotification object:nil]; 38 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(taskWillBeTerminate:) name:UIApplicationWillTerminateNotification object:nil]; 39 | 40 | } 41 | return self; 42 | } 43 | /** 44 | * 收到程序即将失去焦点的通知,开启后台运行 45 | * 46 | * @param sender 通知 47 | */ 48 | - (void)taskWillResign:(NSNotification *)sender { 49 | if(_taskDict.count>0) { 50 | _backgroudTaskId = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{ 51 | 52 | }]; 53 | } 54 | } 55 | 56 | /** 57 | * 收到程序重新得到焦点的通知,关闭后台 58 | * 59 | * @param sender 通知 60 | */ 61 | - (void)taskDidBecomActive:(NSNotification *)sender { 62 | if(_backgroudTaskId != UIBackgroundTaskInvalid) { 63 | [[UIApplication sharedApplication] endBackgroundTask:_backgroudTaskId]; 64 | _backgroudTaskId = UIBackgroundTaskInvalid; 65 | } 66 | } 67 | 68 | /** 69 | * 程序将要结束时,取消下载 70 | * 71 | * @param sender 通知 72 | */ 73 | - (void)taskWillBeTerminate:(NSNotification *)sender { 74 | [self cancelAllTasks]; 75 | } 76 | 77 | /** 78 | * 下载完成通知调用的方法 79 | * 80 | * @param sender 通知 81 | */ 82 | - (void)taskDidFinishUploading:(NSNotification *)sender { 83 | //下载完成后,从任务列表中移除下载任务,若总任务数小于最大同时下载任务数, 84 | //则从排队对列中取出一个任务,进入下载 85 | NSString *key = [sender.userInfo objectForKey:@"key"]; 86 | @synchronized (_taskDict) { 87 | [_taskDict removeObjectForKey:key]; 88 | } 89 | if(_taskDict.count < kFGDwonloadMaxTaskCount) { 90 | if(_queue.count > 0) { 91 | @synchronized(_queue) { 92 | NSDictionary *first = [_queue objectAtIndex:0]; 93 | [self upload:first[@"host"] 94 | parama:first[@"paramaters"] 95 | file:first[@"data"] 96 | mimeType:first[@"type"] 97 | fileName:first[@"fileName"] 98 | name:first[@"name"] 99 | process:first[@"process"] 100 | completion:first[@"completion"] 101 | failure:first[@"failure"]]; 102 | //从排队对列中移除一个下载任务 103 | [_queue removeObjectAtIndex:0]; 104 | } 105 | } 106 | } 107 | } 108 | 109 | + (instancetype)shred { 110 | static dispatch_once_t onceToken; 111 | dispatch_once(&onceToken, ^{ 112 | uploadMgr = [[FGUploadManager alloc]init]; 113 | }); 114 | return uploadMgr; 115 | } 116 | 117 | - (void)upload:(NSString *)host 118 | parama:(NSDictionary *)p 119 | file:(NSData *)data 120 | mimeType:(NSString *)type 121 | fileName:(NSString *)n1 122 | name:(NSString *)n2 123 | process:(FGProcessHandle)process 124 | completion:(FGUploadCompletionHandle)completion 125 | failure:(FGFailureHandle)failure { 126 | if(!host || 127 | ![host hasPrefix:@"http"] || 128 | !type || 129 | !n1 || 130 | !n2) { 131 | return; 132 | } 133 | //若同时下载的任务数超过最大同时下载任务数, 134 | //则把下载任务存入对列,在下载完成后,自动进入下载。 135 | if(_taskDict.count >= kFGDwonloadMaxTaskCount) { 136 | NSDictionary *dict = @{@"host" : host, 137 | @"mimeType" : type, 138 | @"paramaters" : p == nil ? @"" : p, 139 | @"data" : data, 140 | @"fileName" : n1, 141 | @"name" : n2, 142 | @"process" : process, 143 | @"completion" : completion, 144 | @"failure" : failure}; 145 | [_queue addObject:dict]; 146 | return; 147 | } 148 | FGUploader *uploader = [FGUploader uploader]; 149 | NSString *key = @""; 150 | if(p != nil) { 151 | key = [NSString stringWithFormat:@"%@?%@",host,p]; 152 | } else { 153 | key = host; 154 | } 155 | @synchronized (self) { 156 | [_taskDict setObject:uploader forKey:key]; 157 | } 158 | [uploader upload:host 159 | parama:p 160 | file:data 161 | mimeType:type 162 | fileName:n1 163 | name:n2 164 | process:process 165 | completion:completion 166 | failure:failure]; 167 | } 168 | 169 | - (void)cancelTask:(NSString *)url { 170 | FGUploader *uploader = [_taskDict objectForKey:url]; 171 | [uploader cancel]; 172 | @synchronized (_taskDict) { 173 | [_taskDict removeObjectForKey:url]; 174 | } 175 | @synchronized(_queue) { 176 | if(_queue.count>0) { 177 | NSDictionary *first = [_queue objectAtIndex:0]; 178 | [self upload:first[@"host"] 179 | parama:first[@"paramaters"] 180 | file:first[@"data"] 181 | mimeType:first[@"type"] 182 | fileName:first[@"fileName"] 183 | name:first[@"name"] 184 | process:first[@"process"] 185 | completion:first[@"completion"] 186 | failure:first[@"failure"]]; 187 | //从排队对列中移除一个下载任务 188 | [_queue removeObjectAtIndex:0]; 189 | } 190 | } 191 | } 192 | 193 | - (void)cancelAllTasks { 194 | NSMutableArray *keys = [NSMutableArray array]; 195 | @synchronized(_taskDict) { 196 | [_taskDict enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) { 197 | FGUploader *uploader = obj; 198 | [uploader cancel]; 199 | [keys addObject:key]; 200 | }]; 201 | [_taskDict removeObjectsForKeys:keys]; 202 | } 203 | } 204 | 205 | - (void)dealloc { 206 | [[NSNotificationCenter defaultCenter] removeObserver:self]; 207 | } 208 | @end 209 | 210 | -------------------------------------------------------------------------------- /FGDownloader/lib/FGUploader.h: -------------------------------------------------------------------------------- 1 | // 2 | // FGUploader.h 3 | // DownloaderDemo 4 | // 5 | // Created by xgf on 2018/1/6. 6 | // Copyright © 2018年 xgf. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "FGCommon.h" 11 | 12 | /**请使用FGUploadManager*/ 13 | @interface FGUploader : NSObject 14 | 15 | @property(nonatomic,strong)NSURLConnection *con; 16 | @property(nonatomic,copy,readonly)FGProcessHandle process;//下载进度回调(会多次调用) 17 | @property(nonatomic,copy,readonly)FGUploadCompletionHandle completion; 18 | @property(nonatomic,copy,readonly)FGFailureHandle failure; 19 | 20 | + (instancetype)uploader; 21 | /** 22 | * 上传 23 | * @param host 服务器地址 24 | * @param data 文件二进制数据 25 | * @param p post请求的参数 26 | * @param fileName 文件名(如1.jpg) 27 | * @param name 服务器文件的变量名 28 | * @param mimeType 文件的mimeType(如image/jpeg) 29 | * @param process 进度的回调(会多次调用) 30 | * @param completion 成功的回调 31 | * @param failure 失败的回调 32 | */ 33 | - (void)upload:(NSString *)host 34 | parama:(NSDictionary *)p 35 | file:(NSData *)data 36 | mimeType:(NSString *)type 37 | fileName:(NSString *)n1 38 | name:(NSString *)n2 39 | process:(FGProcessHandle)process 40 | completion:(FGUploadCompletionHandle)completion 41 | failure:(FGFailureHandle)failure; 42 | /**取消上传*/ 43 | - (void)cancel; 44 | 45 | @end 46 | 47 | -------------------------------------------------------------------------------- /FGDownloader/lib/FGUploader.m: -------------------------------------------------------------------------------- 1 | 2 | // 3 | // FGUploader.m 4 | // DownloaderDemo 5 | // 6 | // Created by xgf on 2018/1/6. 7 | // Copyright © 2018年 xgf. All rights reserved. 8 | // 9 | #import "FGUploader.h" 10 | #import "FGTool.h" 11 | #import 12 | 13 | @implementation FGUploader { 14 | 15 | NSString *_task_key; 16 | NSURLConnection *_con; 17 | int64_t _writenLength; 18 | NSDate *_refrenceDate; 19 | NSMutableData *_receivedData; 20 | } 21 | 22 | + (instancetype)uploader { 23 | return [[[self class] alloc] init]; 24 | } 25 | 26 | static NSData * encode(NSString *s) { 27 | return [s dataUsingEncoding:NSUTF8StringEncoding]; 28 | } 29 | 30 | /** 31 | * 上传 32 | * @param host 服务器地址 33 | * @param data 文件二进制数据 34 | * @param p post请求的参数 35 | * @param fileName 文件名(如1.jpg) 36 | * @param name 服务器文件的变量名 37 | * @param mimeType 文件的mimeType(如image/jpeg) 38 | * @param process 进度的回调(会多次调用) 39 | * @param completion 成功的回调 40 | * @param failure 失败的回调 41 | */ 42 | - (void)upload:(NSString *)host 43 | parama:(NSDictionary *)p 44 | file:(NSData *)data 45 | mimeType:(NSString *)type 46 | fileName:(NSString *)n1 47 | name:(NSString *)n2 48 | process:(FGProcessHandle)process 49 | completion:(FGUploadCompletionHandle)completion 50 | failure:(FGFailureHandle)failure { 51 | if(!host || ![host hasPrefix:@"http"]) { 52 | NSError *error = [NSError errorWithDomain:@"FGUploader.upload" code:-1 userInfo:@{NSLocalizedDescriptionKey:@"--host不能为空--"}]; 53 | if (failure) { 54 | failure(error); 55 | } 56 | return; 57 | } 58 | if(p != nil) { 59 | _task_key = [NSString stringWithFormat:@"%@?%@",host,[p description]]; 60 | } else { 61 | _task_key = host; 62 | } 63 | _process = process; 64 | _completion = completion; 65 | _failure = failure; 66 | NSString *bountry = @"haha"; 67 | NSString *line = @"\r\n"; 68 | NSMutableData *container = [NSMutableData data]; 69 | [container appendData:encode(@"--")]; 70 | [container appendData:encode(bountry)]; 71 | [container appendData:encode(line)]; 72 | NSString *disposition = [NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\"; filename=\"%@\"", n1,n2]; 73 | [container appendData:encode(disposition)]; 74 | [container appendData:encode(line)]; 75 | NSString *typedispos = [NSString stringWithFormat:@"Content-Type: %@",type]; 76 | [container appendData:encode(typedispos)]; 77 | [container appendData:encode(line)]; 78 | [container appendData:encode(line)]; 79 | [container appendData:data]; 80 | [container appendData:encode(line)]; 81 | [p enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) { 82 | [container appendData:encode(@"--")]; 83 | [container appendData:encode(bountry)]; 84 | [container appendData:encode(line)]; 85 | NSString *dipos = [NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\"", key]; 86 | [container appendData:encode(dipos)]; 87 | [container appendData:encode(line)]; 88 | [container appendData:encode(line)]; 89 | [container appendData:encode([obj description])]; 90 | [container appendData:encode(line)]; 91 | }]; 92 | [container appendData:encode(@"--")]; 93 | [container appendData:encode(bountry)]; 94 | [container appendData:encode(@"--")]; 95 | [container appendData:encode(line)]; 96 | 97 | NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:host]]; 98 | request.HTTPBody = container; 99 | request.HTTPMethod = @"POST"; 100 | [request addValue:@(container.length).stringValue forHTTPHeaderField:@"Content-Length"]; 101 | NSString *strContentType = [NSString stringWithFormat:@"multipart/form-data; boundary=%@", bountry]; 102 | [request setValue:strContentType forHTTPHeaderField:@"Content-Type"]; 103 | _con = [NSURLConnection connectionWithRequest:request delegate:self]; 104 | } 105 | 106 | /** 107 | * 取消下载 108 | */ 109 | - (void)cancel { 110 | [self.con cancel]; 111 | self.con = nil; 112 | } 113 | 114 | #pragma mark - NSURLConnection 115 | - (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error { 116 | if(_failure) { 117 | _failure(error); 118 | } 119 | } 120 | 121 | - (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response { 122 | _receivedData = [NSMutableData data]; 123 | } 124 | 125 | - (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data { 126 | [_receivedData appendData:data]; 127 | } 128 | 129 | - (void)connection:(NSURLConnection *)connection didSendBodyData:(NSInteger)bytesWritten totalBytesWritten:(NSInteger)totalBytesWritten totalBytesExpectedToWrite:(NSInteger)totalBytesExpectedToWrite { 130 | if (_refrenceDate == nil) { 131 | _refrenceDate = [NSDate date]; 132 | } else {//计算大致网速 133 | NSTimeInterval gap = [[NSDate date] timeIntervalSinceDate:_refrenceDate]; 134 | long long grow = bytesWritten - _writenLength; 135 | CGFloat speed = grow/gap;//bytes per seconds 136 | NSString *speedExpress = [FGTool convertSize:speed]; 137 | NSString *progressExpress = [NSString stringWithFormat:@"%@/s",speedExpress]; 138 | NSString *progressInfo = [NSString stringWithFormat:@"%@/%@", [FGTool convertSize:bytesWritten], [FGTool convertSize:totalBytesExpectedToWrite]]; 139 | CGFloat progress = (CGFloat)totalBytesWritten / (CGFloat)totalBytesExpectedToWrite; 140 | //发送进度改变的通知(一般情况下不需要用到,只有在触发下载与显示下载进度在不同界面的时候才会用到) 141 | NSDictionary *userInfo = @{@"url":_task_key, 142 | @"progress":@(progress), 143 | @"sizeString":progressInfo}; 144 | [[NSNotificationCenter defaultCenter] postNotificationName:FGProgressDidChangeNotificaiton object:nil userInfo:userInfo]; 145 | //回调下载过程中的代码块 146 | if(_process) { 147 | _process(progress,progressInfo,progressExpress); 148 | } 149 | } 150 | //reset 151 | _refrenceDate = [NSDate date]; 152 | _writenLength = bytesWritten; 153 | } 154 | /** 155 | * 下载完成 156 | */ 157 | - (void)connectionDidFinishLoading:(NSURLConnection *)connection { 158 | [[NSNotificationCenter defaultCenter] postNotificationName:FGUploadTaskDidFinishNotification object:nil userInfo:@{@"key":_task_key}]; 159 | if(_completion) { 160 | _completion(_receivedData); 161 | } 162 | } 163 | 164 | @end 165 | 166 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 CGPointZero 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # FGGDownloadManager 2 | 3 | 断点续传下载文件,文件上传显示进度 4 | 5 | [![Version](https://img.shields.io/cocoapods/v/FGDownloader.svg?style=flat)](http://cocoadocs.org/docsets/FGVideoEditor) 6 | [![License](https://img.shields.io/cocoapods/l/FGDownloader.svg?style=flat)](http://cocoadocs.org/docsets/FGVideoEditor) 7 | [![Platform](https://img.shields.io/cocoapods/p/FGDownloader.svg?style=flat)](http://cocoadocs.org/docsets/FGVideoEditor) 8 | 9 | ![](https://github.com/Insfgg99x/FGGDownloader/blob/master/demo.gif) 10 | 11 | ### Usage 12 | - download 13 | ``` 14 | /** 15 | * 断点下载(get) 16 | * 17 | * @param urlString 下载的链接 18 | * @param destinationPath 下载的文件的保存路径 19 | * @param process 进度的回调,会多次调用 20 | * @param completion 下载完成的回调 21 | * @param failure 下载失败的回调 22 | */ 23 | - (void)downloadUrl:(NSString *)urlString 24 | toPath:(NSString *)destinationPath 25 | process:(FGProcessHandle)process 26 | completion:(FGCompletionHandle)completion 27 | failure:(FGFailureHandle)failure; 28 | 29 | /** 30 | * 断点下载(post) 31 | * 32 | * @param host 下载的链接 33 | * @param p post参数 34 | * @param destinationPath 下载的文件的保存路径 35 | * @param process 进度的回调,会多次调用 36 | * @param completion 下载完成的回调 37 | * @param failure 下载失败的回调 38 | */ 39 | - (void)downloadHost:(NSString *)host 40 | param:(NSString *)p 41 | toPath:(NSString *)destinationPath 42 | process:(FGProcessHandle)process 43 | completion:(FGCompletionHandle)completion 44 | failure:(FGFailureHandle)failure; 45 | ``` 46 | - upload 47 | ``` 48 | /** 49 | * 上传 50 | * @param host 服务器地址 51 | * @param data 文件二进制数据 52 | * @param p post请求的参数 53 | * @param fileName 文件名(如1.jpg) 54 | * @param name 服务器文件的变量名 55 | * @param mimeType 文件的mimeType(如image/jpeg) 56 | * @param process 进度的回调(会多次调用) 57 | * @param completion 成功的回调 58 | * @param failure 失败的回调 59 | */ 60 | - (void)upload:(NSString *)host 61 |        parama:(NSDictionary *)p 62 | file:(NSData *)data 63 | mimeType:(NSString *)type 64 | fileName:(NSString *)n1 65 | name:(NSString *)n2 66 | process:(FGProcessHandle)process 67 | completion:(FGUploadCompletionHandle)completion 68 | failure:(FGFailureHandle)failure; 69 | ``` 70 | ### Install 71 | - Cocopods 72 | ``` 73 | pod repo update 74 | pod 'FGGDownloader', '~> 2.2' 75 | pod install 76 | ``` 77 | - Manual 78 | 下载 [FGGDownloader](https://github.com/Insfgg99x/FGGDownloader.git) 并引入项目,导入`FGHeader.h`文件 79 | 80 | -------------------------------------------------------------------------------- /demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Insfgg99x/FGDownloader/d2ab36ca9d9a93abee8062ae5f2b056117599a11/demo.gif --------------------------------------------------------------------------------