强子的第一篇~
第一篇
好激动
正题
最近在IM开发中遇到了个奇葩的bug,使用AFNetWorking上传语音在iOS 7 上传音频文件直接失败,返回3840服务端直接拒绝~~~最后跟后台童鞋定位了,缺少了Content-Length字段,好奇特,iOS10 上没问题啊~好蛋疼...开始断点一步步滴跟~使用了下面的方法进行上传:
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
manager.responseSerializer.acceptableContentTypes= [NSSet setWithObjects:@"application/json", @"text/json", @"text/javascript",@"text/html",@"image/jpeg",nil];NSMutableDictionary*para = [NSMutableDictionarydictionary];
para[@"action"] = @"999";
[manager POST:@"http://push.hjourney.cn/api.php?c=Index2"parameters:para constructingBodyWithBlock:^(id _Nonnull formData) {
[formData appendPartWithFileData:audioData name:@"file"fileName:@"m4a"mimeType:@"audio/mpeg"];
} progress:^(NSProgress * _Nonnull uploadProgress) {floatprogress =1.0* uploadProgress.completedUnitCount/ uploadProgress.totalUnitCount;NSLog(@"上传进度----- %f",progress);
} success:^(NSURLSessionDataTask * _Nonnull task,id_Nullable responseObject) {NSLog(@"上传成功 %@",responseObject);
} failure:^(NSURLSessionDataTask * _Nullable task,NSError* _Nonnull error) {NSLog(@"上传失败 %@",error);
}];
生成NSMutableURLRequest 头部还有Content-Length
然后再往下走~生成NSURLSessionUploadTask Content-Length字段没了。。。
然后就收到了这个错误:
_block_invoke fail:Error Domain=NSCocoaErrorDomain Code=3840 "The operation couldn’t be completed. (Cocoa error 3840.)" (JSON text did not start with array or object and option to allow fragments not set.) UserInfo=0x16fbb260 {NSDebugDescription=JSON text did not start with array or object and option to allow fragments not set.
不要怀疑人生了...iOS7上Content-Length就是被"吃"了,各种谷歌~~~有结论了~NSURLSessionTask中有一个bug(苹果坑爹了。。。),当HTTP body的内容是来自NSStream的时候,request无法发送Content-Length到服务器端,此问题在Amazon S3的Web服务中尤为显著。
解决方案;
https://github.com/afnetworking/afnetworking/issues/1398
上代码~自己亲测可用!
第一种采用afnetworking 的api:
+ (void)sendAudioData
{
NSMutableDictionary*dic = [NSMutableDictionary dictionary];
[dic setObject:@"附加数据" forKey:@"mydata"];
NSString*filePath = @"音频文件路径";
NSString*url = @"上传文件的url路径";
NSString* tmpFilename = [NSString stringWithFormat:@"%f", [NSDate timeIntervalSinceReferenceDate]];
NSURL* tmpFileUrl = [NSURL fileURLWithPath:[NSTemporaryDirectory() stringByAppendingPathComponent:tmpFilename]];
// Create a multipart form request.
NSMutableURLRequest*multipartRequest = [[AFHTTPRequestSerializer serializer] multipartFormRequestWithMethod:@"POST"
URLString:transcodeUrl
parameters:dic constructingBodyWithBlock:^(id formData)
{
[formData appendPartWithFileURL:[NSURL fileURLWithPath:filePath]
name:@"file"
fileName:@"m4a"
mimeType:@"audio/mpeg"error:nil];
} error:nil];
//Create default session manager.
AFURLSessionManager *manager = [[AFURLSessionManager alloc]initWithSessionConfiguration:[NSURLSessionConfigurationdefaultSessionConfiguration]];
[[AFHTTPRequestSerializer serializer] requestWithMultipartFormRequest:multipartRequest
writingStreamContentsToFile:tmpFileUrl
completionHandler:^(NSError *error) {
//将数据写入临时文件,不在使用NSStream 生成NSURLSessionUploadTask,解决Content-Length丢失问题
NSURLSessionUploadTask *uploadTask = [manager uploadTaskWithRequest:multipartRequestfromFile:tmpFileUrl progress:^(NSProgress *_NonnulluploadProgress) {
}
completionHandler:^(NSURLResponse *response,idresponseObject, NSError *error)
{
// Cleanup: remove temporary file.
[[NSFileManager defaultManager] removeItemAtURL:tmpFileUrl error:nil];
// Do something with the result.
if(error) {
[manager.session finishTasksAndInvalidate];
}else{
if(!responseObject ||0!= ((NSString *)responseObject[@"error_code"]).integerValue)
{return;}
NSLog(@"Success:%@", responseObject);
}
}];
// Start the file upload.
[uploadTask resume];
}];
}
第二种方式不使用AFNetWorking,使用NSURLSessionUploadTask上传:
代码如下
//转码
+ (void)sendAudioData
{
NSMutableDictionary*dic = [NSMutableDictionary dictionary];
[dic setObject:@"附加数据" forKey:@"mydata"];
NSString*filePath = @"音频文件路径";
NSString*url = @"上传文件的url路径";
// Create a multipart form request.
NSMutableURLRequest*request = [NSMutableURLRequestrequestWithURL:[NSURLURLWithString:url]];
request.HTTPMethod=@"POST";
NSString*boundary = [NSStringstringWithFormat:@"Boundary+%08X%08X",arc4random(),arc4random()];
//设置ContentType
NSString*contentType = [NSStringstringWithFormat:@"multipart/form-data; boundary=%@", boundary];
[requestsetValue:contentTypeforHTTPHeaderField:@"Content-Type"];
//设置请求头,告诉服务器本次长传的时文件信息//这一块的内容是不变的!
NSData*bodyData = [selfcreateBodyWithBoundary:boundaryparameters:dicpaths:@[filePath]fieldName:@"file"];
// 2.发送请求
NSURLSession*session = [NSURLSessionsharedSession];
NSURLSessionUploadTask*task = [sessionuploadTaskWithRequest:requestfromData:bodyDatacompletionHandler:^(NSData*_Nullabledata,NSURLResponse*_Nullableresponse,NSError*_Nullableerror) {
NSDictionary*responseObject = [NSJSONSerializationJSONObjectWithData:dataoptions:NSJSONReadingAllowFragmentserror:nil];
if(!responseObject ||0!= ((NSString*)responseObject[@"error_code"]).integerValue)
{
}
}];
//5 resume
[taskresume];
}
+ (NSData*)createBodyWithBoundary:(NSString*)boundary
parameters:(NSDictionary*)parameters
paths:(NSArray*)paths
fieldName:(NSString*)fieldName {
NSMutableData*httpBody = [NSMutableDatadata];
//文本参数
[parametersenumerateKeysAndObjectsUsingBlock:^(NSString*parameterKey,NSString*parameterValue,BOOL*stop) {
[httpBodyappendData:[[NSStringstringWithFormat:@"--%@\r\n", boundary]dataUsingEncoding:NSUTF8StringEncoding]];
[httpBodyappendData:[[NSStringstringWithFormat:@"Content-Disposition: form-data; name=\"%@\"\r\n\r\n", parameterKey]dataUsingEncoding:NSUTF8StringEncoding]];
[httpBodyappendData:[[NSStringstringWithFormat:@"%@\r\n", parameterValue]dataUsingEncoding:NSUTF8StringEncoding]];
}];
//本地文件的NSData
for(NSString*pathinpaths) {
NSString*filename=@"m4a";
NSData*data= [NSDatadataWithContentsOfFile:path];
NSString*mimetype= [selfmimeTypeForPath:path];
[httpBodyappendData:[[NSStringstringWithFormat:@"--%@\r\n", boundary]dataUsingEncoding:NSUTF8StringEncoding]];
[httpBodyappendData:[[NSStringstringWithFormat:@"Content-Disposition: form-data; name=\"%@\"; filename=\"%@\"\r\n", fieldName, filename]dataUsingEncoding:NSUTF8StringEncoding]];
[httpBodyappendData:[[NSStringstringWithFormat:@"Content-Type: %@\r\n\r\n", mimetype]dataUsingEncoding:NSUTF8StringEncoding]];
[httpBodyappendData:data];
[httpBodyappendData:[@"\r\n"dataUsingEncoding:NSUTF8StringEncoding]];
}
[httpBodyappendData:[[NSStringstringWithFormat:@"--%@--\r\n", boundary]dataUsingEncoding:NSUTF8StringEncoding]];
returnhttpBody;
}
+ (NSString*)mimeTypeForPath:(NSString*)path {
CFStringRefextension = (__bridgeCFStringRef)[pathpathExtension];
CFStringRefUTI =UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, extension,NULL);
NSString*mimetype =CFBridgingRelease(UTTypeCopyPreferredTagWithClass(UTI,kUTTagClassMIMEType));
CFRelease(UTI);
returnmimetype;
}
求打赏,请喝可乐~~~哈哈