Objective-C中Multipart/FormData多图上传问题及代码正确性验证
问题分析与修复方案
你的代码无法成功上传两张图片的核心问题是把两个文件的表单字段合并到了同一个multipart区块中——multipart/form-data协议要求每个表单项(包括每个上传的文件)都必须是独立的、由boundary分隔的区块,服务器才能正确解析出多个文件。
错误点拆解
你当前代码里把两个文件的Content-Disposition写在了同一个区块内:
[body appendData:[@"Content-Disposition: attachment; name=\"uploadedfile1\"; filename=\".jpeg\"\r\n" dataUsingEncoding:NSUTF8StringEncoding]]; [body appendData:[@"Content-Disposition: attachment; name=\"uploadedfile2\"; filename=\".jpeg\"\r\n" dataUsingEncoding:NSUTF8StringEncoding]];
这会导致服务器无法识别两个独立的文件,只会解析到一个无效的表单项,自然只能收到一张(甚至收不到)图片。
修改后的完整代码
下面是严格遵循multipart/form-data规范的修正版本,每个文件都作为独立的区块处理:
-(void)processPostMultipartRequestNew{ [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES]; NSString *urlStr = [self.dataModel.apiUrl stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; urlStr = [urlStr stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; urlStr= [urlStr stringByReplacingOccurrencesOfString:@"+" withString:@"%2B"]; NSURL *reqUrl = [NSURL URLWithString:urlStr]; NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:reqUrl]; NSString *boundary = @"---------------------------14737809831466499882746641449"; NSString *contentType = [NSString stringWithFormat:@"multipart/form-data; boundary=%@",boundary]; [request addValue:contentType forHTTPHeaderField: @"Content-Type"]; NSDictionary *requestBody = self.dataModel.requestParams2; UIImage *imageToUpload = [requestBody objectForKey:keyUploadImage]; NSDictionary *requestBody2 = self.dataModel.requestParams; UIImage *imageToUpload2 = [requestBody2 objectForKey:keyUploadImage]; // 增加非空判断,避免传入nil导致崩溃 if(requestBody && imageToUpload && imageToUpload2){ NSData *imageData = UIImageJPEGRepresentation(imageToUpload, 0); NSData *imageData2 = UIImageJPEGRepresentation(imageToUpload2, 0); NSMutableData *body = [NSMutableData data]; // 第一个图片的独立区块 [body appendData:[[NSString stringWithFormat:@"--%@\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]]; [body appendData:[@"Content-Disposition: form-data; name=\"uploadedfile1\"; filename=\"image1.jpeg\"\r\n" dataUsingEncoding:NSUTF8StringEncoding]]; [body appendData:[@"Content-Type: image/jpeg\r\n\r\n" dataUsingEncoding:NSUTF8StringEncoding]]; [body appendData:imageData]; [body appendData:[@"\r\n" dataUsingEncoding:NSUTF8StringEncoding]]; // 第二个图片的独立区块 [body appendData:[[NSString stringWithFormat:@"--%@\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]]; [body appendData:[@"Content-Disposition: form-data; name=\"uploadedfile2\"; filename=\"image2.jpeg\"\r\n" dataUsingEncoding:NSUTF8StringEncoding]]; [body appendData:[@"Content-Type: image/jpeg\r\n\r\n" dataUsingEncoding:NSUTF8StringEncoding]]; [body appendData:imageData2]; [body appendData:[@"\r\n" dataUsingEncoding:NSUTF8StringEncoding]]; // 文本参数SyncId的区块 NSString *param1 = @"111"; [body appendData:[[NSString stringWithFormat:@"--%@\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]]; [body appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"SyncId\"\r\n\r\n"] dataUsingEncoding:NSUTF8StringEncoding]]; [body appendData:[param1 dataUsingEncoding:NSUTF8StringEncoding]]; [body appendData:[@"\r\n" dataUsingEncoding:NSUTF8StringEncoding]]; // 结束标记 [body appendData:[[NSString stringWithFormat:@"--%@--\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]]; [request setHTTPBody:body]; } [request setHTTPShouldHandleCookies:NO]; [request setHTTPMethod:REQUEST_TYPE_POST]; [request setTimeoutInterval:60*4]; [NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) { NSLog(@"completionHandler with response:%@",[NSHTTPURLResponse localizedStringForStatusCode:[(NSHTTPURLResponse*)response statusCode]]); NSLog(@"reponse: %ld",(long)[(NSHTTPURLResponse*)response statusCode]); NSInteger status = [(NSHTTPURLResponse*)response statusCode]; if(error){ NSLog(@"http request error: %@", error.localizedDescription); self.dataModel.responseFailed = YES; self.dataModel.error = error; NSLog(@"Result in error case %@",[[error userInfo] objectForKey:@"NSDebugDescription"]); } else{ if (status == 201){ self.dataModel.responseFailed = NO; self.dataModel.error = nil; NSString *responseStr = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; if(responseStr){ // 根据响应内容类型处理数据 if(!([responseStr rangeOfString:@".jpg"].location==NSNotFound)) { self.dataModel.responseData = responseStr; } else { NSError *jsonError = nil; NSDictionary *resultDict = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingAllowFragments error:&jsonError]; if(!jsonError){ self.dataModel.responseData = resultDict; } else { self.dataModel.responseFailed = YES; self.dataModel.error = jsonError; } } } else { self.dataModel.responseFailed = YES; self.dataModel.error = [NSError errorWithDomain:@"ParseError" code:-1 userInfo:@{NSLocalizedDescriptionKey:@"无法解析响应数据"}]; } } else{ NSLog(@"error: status code %ld", (long)status); self.dataModel.responseFailed = YES; self.dataModel.error = [NSError errorWithDomain:@"HTTPError" code:status userInfo:@{NSLocalizedDescriptionKey:[NSHTTPURLResponse localizedStringForStatusCode:status]}]; } } NSLog(@"response data: %@",[[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding]); NSLog(@"error is %@",error); self.executing = NO; [self setFinishedState:YES]; [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO]; [self.reciever performSelectorOnMainThread:self.callBack withObject:self.dataModel waitUntilDone:YES]; }]; }
额外优化说明
- 增加了图片非空判断,避免
UIImageJPEGRepresentation传入nil导致崩溃 - 把文件的
Content-Type改为更准确的image/jpeg,而非通用的application/octet-stream - 给每个文件设置了有意义的文件名,方便服务器端识别和处理
- 整理了回调内的错误处理逻辑,避免重复代码和潜在的空指针问题
内容的提问来源于stack exchange,提问作者iVJ
相关产品推荐
相关产品推荐

