NSURLConnection file upload
- Use POST
- Content-Type: multipart/form-data;
#define kBOUNDARY @"abc" -(void)uploadFile:(NSString *)urlString fieldName:(NSString *)fieldName filePath:(NSString *)filePath{ NSURL *url = [NSURL URLWithString:urlString]; NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url]; //Set post request.HTTPMethod = @"post"; //Set request header [request setValue:[NSString stringWithFormat:@"multipart/form-data;boundary=%@",kBOUNDARY] forHTTPHeaderField:@"Content-Type"]; //Set request body request.HTTPBody = [self makeBodyWithfieldName:fieldName filePath:filePath]; [NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *_Nullable response,NSData *_Nullable data,NSError *_Nullable connectionError){ NSString *html = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; NSLog(@"%@",html); }]; } -(NSData *)makeBodyWithfieldName:(NSString *)fieldName filePath:(NSString *)filePath{ NSMutableData *mData = [NSMuatbelData data]; //Part I NSMutableString *mString = [NSMuatbleString string]; [mString appendFormat:@"--%@\r\n",kBOUNDARY]; [mString appendFormat:@"Content-Disposittion: form-data; name=\"%@\";filename=\"%@\"\r\n",fieldName,[filePath lastPathComponent]]; [mString appendString:@"Content-Type: image/hpeg\r\n"]; [mString appendString:@"\r\n"]; [nData appendData:[mString dataUsingEncoding:NSUTF8StringEncoding]]; //Part II //load file NSData *data = [NSData dataWithContentsOfFile:filePath]; [mData appendData:data]; //Part III NSString *end = [NSString stringWithFormat:@"\r\n--%@--",kBOUNDARY]; [mData appendData:[end dataUsingEncoding:NSUTF8StringEncoding]]; return mData.copy; }
Meaning of each item in the request body
- Name attribute value of the name form
- filename the file name passed to the server
- Content type tells the server the type of file passed
- Text / plain image / jpeg image / JPG image / PNG application / octet stream, etc
- NSData to upload binary data
- ------WebKitFormBoundaryuWw18YzUxr2ygEJi--
Upload multiple files at the same time
- When splicing the request body, use the circular block of NSArray for splicing
- AFNetworking is recommended
HEAD request
- HEAD request
Do not get the response body, only get the response header
Generally, the file size is obtained before downloading - Properties of the NSURLResponse
Content type of the file returned by MIMEType
Expected size of expectedContentLength file (actual size)
suagestedFilename is the name of the recommended file to save - Code example: asynchronous request
The output data is empty
NSURL *url = [NSURL URLWithString:urlString]; NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url]; //Set post request.HTTPMethod = @"post"; [NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *_Nullable response,NSData *_Nullable data,NSError *_Nullable connectionError){ NSLog(@"%@",response); }];
File download
- You can use GET to download, but directly save it to memory and then enter it into disk. Memory pressure is high, memory is not enough, flash back, and there is no progress bar
[data writeToFile:@"/Users/Apple/Desktop/123.hm" atomically:YES];
- NSURLConnectionDownloadDelegate
It can be downloaded bit by bit, with progress bar and breakpoint continuation
However, it can only be used to save APP files of newspapers and magazines, so it is abandoned
comply with the agreement<NSURLConnectionDownloadDelegate> NSURL *url = [NSURL URLWithString:urlString]; NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url]; NSURLConnection *conn = [NSURLConnection alloc]initWithRequest:request delegate:self]; Proxy method: //Download progress, automatically called after a period of time -(void)connection:(NSURLConnection *)connection didWriteData:(long long)bytesWritten totalBytesWritten:(long long)totalBytesWritten expectedTotalBytes:(long long)expectedTotalBytes{ //How many bytes did byteswriten download this time //How many bytes did totalbyteswriten download in total //Size of expectedTotalBytes file float process = totalBytesWritten *1.0 /expectedTotalBytes; NSLog(@"%@",process); } //File renewal -(void)connectionDidResumeDownloading:(NSURLConnection *)connection totalBytesWritten:(long long)totalBytesWritten expectedTotalBytes:(long long)expectedTotalBytes{ } //Download complete call -(void)connectionDidFinishDownloading:(NSURLConnection *)connection destinationURL:(NSURL *)destinationURL{ //destinationURL the sandbox address of the downloaded file } //Processing error -(void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error{ NSLog(@"Download error%@",error) }
- NSURLConnectionDataDelegate
NSURLConnectionDownloadDelegate inherits from NSURLConnectionDataDelegate
//Response header received -(void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response{ //Remember to write this @ property(nonatomic,assign)long long currentFileSize; self.currentFileSize = response.expectedContentlength; } //Accept data bit by bit //Use an NSMutableData to save data. Remember to rewrite his get function //@property(nonatomic,strong)NSMutableData currentFileSize; -(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data{ //Remember to write this @ property(nonatomic,assign)long long currentFileSize; self.currentFileSize += data.length; //Save the file slowly [self.mutableData appendData:data]; //progress bar float process = self.currentFileSize *1.0 /expectedContentLength; NSLog(@"%@",process); } //Download complete -(void)connectionDidFinishLoading:(NSURLConnection *)connection{ NSLog(@"Download complete!"); [self.mutableData writeToFile:@"/Users/Apple/Desktop/111.hm" atomically:YES]; } //Processing error -(void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error{ NSLog(@"Download error%@",error) }
Solve the memory explosion
- Reason: accumulate the binary data downloaded each time into memory
- Solution: after receiving binary file data each time, save it directly to the file
- Classes to save files: NSFileHandle (read / write binary) and NSOutputStream
- Nsfilemanager (create delete copy file)
NSFileHandle
-(void)saveFile:(NSData *)data{ //Save file path NSString *filePath = @"/Users/Apple/Desktop/111.hm""; //File creation, file does not exist, nil is returned NSFileHandle *file = [NSFileHandle fileHandleForWritingAtPath:filePath]; // if(file == nil){ //data direct write [data writeToFile:path atomically:YES]; }else{ //The file pointer moves to the end of the file [file seekToEndOfFile]; //The file pointer moves to the specified location //[file seekToFileOffset:<#(unsigned long long)>]; //write file [file writeData:data]; //close a file handle [file closeFile]; } }
NSOutputStream
@property(nonatomic,strong)NSOutputStream *stream; //Create stream self.stream = [NSOutputStream outputSteamToFileAtPath:@"/Users/Apple/Desktop/111.hm",append:YES]; //Open stream [self.stream open]; //Save data [self.stream write:data.bytes maxLength:data.length]; //Close flow [self.stream close];
Breakpoint continuation
- Judge whether the file exists. If the file does not exist, download it again
- If the file exists
- Judge the local file size = server file size, no need to download
- Judge the local file size < server file size and download from the current location
- Determine the local file size > server file size, delete the file and download it again
Breakpoint continuation of NSURLConnection
It runs on the message circulation mechanism. If it runs on a sub thread, it is necessary to manually turn on the message circulation mechanism of the sub thread, so it is not recommended
@property(nonatomic,copy)NSString *filePath; @property(nonatomic,copy)NSString *currentFileSize; //Get the size and name of the server file before downloading -(void)getServerFileInfo:(NSURL *)url{ NSURLRequest *request = [NSURLRequest requestWtihURL:url]; request.HTTPMethod = @"head"; NSURLResponse *response = nil; //&Response is used for parameter value transfer [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:NULL]; //Current file size self.currentFileSize = response.expectedContentLength; //File path self.filePath = [NSTemporaryDirectory() stringByAppendingPathComponent:response.suggestedFilename]; } //Get the size of the local file and compare it with the server file //Judge whether the file exists or not. 0 does not exist -(long long)checkLocalFileInfo{ long long fileSize = 0; NSFileManager *fileManager = [NSFileManager defaultManager]; if(![fileManager fileExistsAtPath:self.filePath]){ //Returns the attribute dictionary of the file NSDictionary *attrs = [fileManager attributesOfItemAtPath:self.selfPath error:NULL]; fileSize = attrs.fileSize if(fileSize == self.expectedContentLength){ fileSize = -1;//Download complete } if(fileSize > self.expectedContentLength){ fileSize = 0;//Download error, delete the file and download again //Delete file [fileManager removeItemAtPath:self.filePath error:NULL]; } } return fileSize; } //If you need to download files asynchronously, add a thread -(void)downloadFile(NSURL *)url{ //NSOperation multithreading [[NSOperationQueue new] addOperationWithBlock:^{ NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url] //Modify request header //Range:bytes=x-y download from x to y //Range:bytes=x - download from X to last //Range:bytes=-y download from scratch to y [request setVale:[NSString stringWithFormat:@"bytes=%lld-",self.currentFileSize] forHTTPHeaderField:@"Range"]; //In the message loop of the current thread, the message loop of the default child thread is not started and needs to be started manually NSURLConnection *con = [[NSURLConnecttion alloc]initWithRequest: request delegate:self]; //Open the message loop of the child thread [[NSRunLoop currentRunLoop] run]; }]; } //Mainstream Download -(void)download:(NSString *)urlString successBlock:(void(^)(NSString *path))successBlock processBlock:(void(^)(float process))processBlock errorBlock:(void(^)(ESError *error))errorBlock{ //Easy to transfer value self.successBlock = successBlock; self.processBlock = processBlock; self.errorBlock = errorBlock; NSURL *url = [NSURL URLWithString:urlString]; //Get the size and name of the server file before downloading [self getServerFileInfo:url]; //Get the size of the local file and compare it with the server file self.currentFileSize = [self checkLocalFileInfo]; //Download complete if(self.currentFileSize == -1){ if(self.successBlock){ //You need to go back to the main thread dispatch_async(dispatch_get_main_queue(),^{ self.successBlock(self.filePath); }); } return; } //Download File [self downloadFile:url]; }
//Pause Download -(void)pause{ [self.conn cancel]; }
Download callback
@property(noatomic,copy) void(^successBlock)(NSString *path); @property(noatomic,copy) void(^processBlock)(float process); @property(noatomic,copy) void(^errorBlock)(NSError *error);
//Join where appropriate if(self.processBlock){ self.pro cessBlock(process); } if(self.successBlock){ //You need to go back to the main thread GCD dispatch_async(dispatch_get_main_queue(),^{ self.successBlock(self.filePath); }); } if(self.errorBlock){ self.errorBlock(error); }
Draw progress circle (UI content)
-(void)drawRect:(CGRect)rect{ //Drawing code UIBezierPath *Path = [UIBezierPath bezierPath]; CGPoint center = CGPointMake(rect.size.width/2,rect.size.height/2); CGFloat radius = MIS(center.x,center.y); CGFloat startAngle = -M_PI_2;//Start point of drawing CGFloat endAngle = 2 * M_PI * self.process + startAngle; //set up [path addArcWithCenter:center radius:radius startAngle:startAngle endAngle:endAngle clockwise:YES]; path.lineWidth = 5 ; path.lineCapStyle = kCGLineCapRound; [[UIColor orangeColor] setStroke]; //Start drawing [path stroke]; }
Management class of download operation
Write a singleton mode management class with an operation cache pool
@property(noatomic,strong) NSMuatbleDictionary *downloaderCache; //Lazy loading -(NSMuatbleDictionary *)downloaderCache{ if(_downloaderCache == nil){ _downloaderCache = [NSMuatbleDictionary dictionaryWithCapacity:10]; } return _downloaderCache; } //Add it to the cache pool when using it if(self.downloaderCache[urlString]){ NSLog(@"Downloading!"); return; }else{ //Start downloading ... //Add to the download cache pool [self.downloaderCache setObject:downloader forKey:urlString]; //Remember to move out of the cache pool after success and failure [self.downloaderCache removeObjectForKey:urlString]; }