In daily development there are often upload forms and pictures to the server scene, there are two ways to achieve: one, separate package a picture file format storage code, the server onResponseThe return value returns the server image path, and then binds the server image path through other interfaces; Submit forms and pictures directly. So, in fact, it is two ways, in the final analysis is one: data transmission and reception. So, here we areOCHow does the simple simulation server parse the form data and picture format data from the client

Previous article address:

# iOS simply emulates HTTPS certificate trust logic

# iOS Build mobile phone internal server based on CocoaHTTPServer to achieve HTTP and HTTPS access and data transmission

How does the simulation server parse the form data and picture format data from the client

The effect is as follows:

Introduction:

Here is a brief description of how to submit data parameters and upload files at the same time under AFNetwork. Here is just a brief idea:

Start with a simple AF request code

AFHTTPSessionManager * m = [[AFHTTPSessionManager alloc] initWithBaseURL:[NSURL URLWithString:@"https://10.10.60.20"]]; NSDictionary * dic = @ {@ "title" : @ "long live the Chinese," @ "name:" @ "Chinese"}; [m POST: @ "https://10.10.60.20:12345/doPost" parameters: dic headers: @ {} constructingBodyWithBlock:^(**id**<AFMultipartFormData> _Nonnull formData) { NSDate *date = [NSDate DateWithTimeIntervalSinceNow: 0]; / / get the current time 0 seconds time NSTimeInterval time = [date timeIntervalSince1970] * 1000; NSString *timeString = [NSString stringWithFormat:@"iOS%.0f", time]; // *timeString = [NSString stringWithFormat:@"iOS%.0f", time]; UIImage * image = [UIImage imageNamed: @ "sea"]; NSData * data = UIImageJPEGRepresentation (image, 0.5 f);  [formData appendPartWithFileData:data name:@"file" fileName:[NSString stringWithFormat:@"%@.jpg",timeString] mimeType:@"image/jpg"];  } progress:^(NSProgress * _Nonnull uploadProgress) { } success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) { } failure:^(NSURLSessionDataTask * **_Nullable** task, NSError * _Nonnull error) { } ];Copy the code

1. Passing in network request parameters:

Dic is the request parameter to be transmitted. After the parameter is completed, AFNetworking stores the parameter and uses NSData to combine the data when uploading the following image.

2. Image data acquisition andNSDataJoining together

AF calls the following method to concatenate the request data.

[formData appendPartWithFileData:data name:@"file" fileName:[NSString stringWithFormat:@"%@.jpg",timeString] mimeType:@"image/jpg"];
Copy the code

3, Based on the second step, create multiple data read objects, throughStreamforNSDataRead in turn, becauseAFUnder thePOSTThe request will be followed by oneStreamTo bind

[self.request setHTTPBodyStream:self.bodyStream];
Copy the code

Then, AF overrides the Stream before starting the send request

- (NSInteger)read:(uint8_t *)buffer maxLength:(NSUInteger)length
Copy the code

Methods. Then, multiple file data can be spliced during Stream reading, and the whole data can be transmitted at one time.

4. Precautions :(1) AF will calibrate the total length of data in the header, so that the server can know the total length of data transmitted when it gets the header first. (2) AF will randomly generate a boundary and put it into the header. The purpose of this parameter is to divide the boundary between the parameters and files that are not available in the request, so that the server can know which data is a complete data when parsing. Of course, AF will also calibrate the transport Type in the header, such as Content-type.

Ok, this is just a prelude to how the request parameters and image files are finally resolved in the total data

Step 1. Based onCocoaHTTPServerSet up localOCThe server parses the data

Please refer to the above article link for how to set up

The following method is used to execute data sent from the client. Since there is a limit on the number of streams a system can read at one time, this method is used multiple times for large file uploads.

- (void)processBodyData:(NSData *)postDataChunk;
Copy the code

Since all data received by the server is separated by newlines, the data between the “\r\n” newlines is a complete parameter.

- (void)parseData (NSData *)postDataChunk {// fileDataStartIndex = 0; // UInt16 separatorBytes = 0X0A0D; NSData * separatorData = [NSData dataWithBytes:&separatorBytes length:2]; int l = (int)[separatorData length]; For (int I = 0; int I = 0; i < [postDataChunk length] - l; NSRange searchRange = {I,l}; NSRange searchRange = {I,l}; / / is a newline if ([[postDataChunk subdataWithRange: searchRange] isEqualToData: separatorData]) {/ / get the location of the data between the newline NSRange newDataRange = {self.dataStartIndex,i - self.dataStartIndex}; self.dataStartIndex = i + l; // If the file data is stored in a different location, Need the self from the start. ParamReceiveComplete logo whether to calibrate the screen to the file data if (self. ParamReceiveComplete) {fileDataStartIndex = I + l; continue; } // Skip the newline I += (l-1); / / to get full data format between a newline NSData * newData = [postDataChunk subdataWithRange: newDataRange]; If ([newData Length]) {NSString *content = [[NSString alloc] initWithData:newData encoding:NSUTF8StringEncoding]; / / replace all special characters a new line content = [content stringByReplacingOccurrencesOfString: @ "\ r \ n withString:" @ ""]. If (content.length &&! [content containsString:@"--Boundary"]) {// Name ="file" If ([content containsString:@"name= "file" "]){self.currentParserType = @"file"; } else {// Request argument self.currentParserType = @"text/plain"; If ([self.currentParserType] containsString:@"text/plain"]){// Content contains form-data. If ([content containsString:@"form-data"]) {NSString * key = [content componentsSeparatedByString:@"name="].lastObject; key = [key stringByReplacingOccurrencesOfString:@"\"" withString:@""]; Self.currentparamkey = key; self.currentParamKey = key; self.currentParamKey = key; self.currentParamKey = key; self.currentParamKey = key; If (self.currentParamKey && Content) {[self.receiveParamdic setValue:content forKey:self.currentParamKey]; }}} else {/ / file processing, calibration, because due to the influence of the file size, this method will go many times, so, in the first place after calibration, the next time come in directly to the file data splicing self. ParamReceiveComplete = YES; }}}}} // File write (actually this is not very strict, because the request parameters are small reasons, so, even if the first time to execute this method, Will also include a file data to read) NSRange fileDataRange = {fileDataStartIndex postDataChunk. Length - fileDataStartIndex}; NSData * fileData = [postDataChunk subdataWithRange:fileDataRange]; [self.outputStream write:[fileData bytes] maxLength:fileData.length]; }Copy the code

Step 2. Write data to the sandbox

Declare an NSOutputStream object

@property (nonatomic,strong) NSOutputStream * outputStream;
Copy the code

The CocoaHTTPServer -> HTTPConnection class is not routinely init, so we initialize the outputStream lazily.

- (NSOutputStream *)outputStream { if (! _outputStream) { NSString * cachePath = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES).firstObject; NSString * filePath = [cachePath stringByAppendingPathComponent:@"wsl.png"]; NSLog(@"filePath = %@",filePath); _outputStream = [[NSOutputStream alloc] initToFileAtPath:filePath append:**YES**]; [_outputStream open]; } return _outputStream; }Copy the code

To sandbox a file:

    NSRange fileDataRange = {fileDataStartIndex,postDataChunk.length - fileDataStartIndex};
    NSData * fileData = [postDataChunk subdataWithRange:fileDataRange];
    [self.outputStream write:[fileData bytes] maxLength:fileData.length];
Copy the code

Close the stream after processing the data

- (NSObject<HTTPResponse> *)httpResponseForMethod:(NSString *)method URI:(NSString *)path
{
    [self.outputStream close];
    self.outputStream = nil;
}
Copy the code

Step 3 View the running result

First check whether the request parameters are obtained:

To see if the picture is saved, print the sandbox path of the emulator and go directly to the folder to find the sandbox file

As you can see, I saved the image here as well.

Here’s an explanation:

Follow MultipartFormDataParserDelegate agreement may also directly file data, to read directly, to save again can. It does not expose the key of the external data request, but only the value, but it is very convenient if it is only used as a file transfer.

As follows:

Compliance with agency Agreement

Declare the MultipartFormDataParser object

The MultipartFormDataParser object performs data parsing

File data parsing agent execution

The same is true of the parsing utility classes packaged by CocoaHTTPServer.

Well, the simple simulation server how to parse the form data and image format data from the client and local save function is finished, the code is poor, god do not laugh.