Network programming 6 of IOS development (file upload POST and download HEAD)

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];
}

Keywords: iOS

Added by mancroft on Sun, 19 Dec 2021 11:09:57 +0200