iOS端NSURLSessionDataTask上传音频文件至服务器失败排查
iOS音频上传问题排查与解决
问题背景
iOS应用录制30秒音频后通过HTTPS上传至服务器,录制和本地存储功能正常,但上传环节失败。服务器API经Postman测试可正常接收文件,iOS端调用返回错误。
服务器端API代码
[HttpPost] public async Task<IActionResult> UploadFile([FromForm] IFormFile file, [FromForm] string name) { try { if(webHostEnvironment == null) { throw new SqlNullValueException("webHostEnvironment is null"); } if (webHostEnvironment.ContentRootPath == null) { throw new SqlNullValueException("webHostEnvironment.ContentRootPath is null"); } if(file == null) { throw new SqlNullValueException("File is null"); } String path = Path.Combine(webHostEnvironment.ContentRootPath, "uploads"); if (file.Length > 0) { string filePath = Path.Combine(path, file.FileName); using (Stream fileStream = new FileStream(filePath, FileMode.Create)) { await file.CopyToAsync(fileStream); } } return BuildResponse(Ok().StatusCode, name); } catch(Exception ex) { throw new Exception(ex.Message ); } }
iOS端原上传代码
-(void)uploadFile:(NSData*)fileData{ if(fileData != nil){ NSString *fileName = self.temporaryRecFile.path.lastPathComponent; NSString *urlString = @"some url";//upload file url NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init]; [request setURL:[NSURL URLWithString:urlString]]; [request setHTTPMethod:@"POST"]; NSString *boundary = @"---------------1234567890"; NSString *contentType = [NSString stringWithFormat:@"multipart/form-data; boundary=%@", boundary]; [request addValue:contentType forHTTPHeaderField:@"Content-Type"]; NSMutableData *bodyData = [[NSMutableData alloc] init]; [bodyData appendData:[[NSString stringWithFormat:@"\r\n--%@\r\n",boundary] dataUsingEncoding:NSUTF8StringEncoding]]; [bodyData appendData:[[NSString stringWithString:[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"userfile\"; filename=\"%@\"\r\n", fileName]] dataUsingEncoding:NSUTF8StringEncoding]]; [bodyData appendData:[@"Content-Type: application/octet-stream\r\n\r\n" dataUsingEncoding:NSUTF8StringEncoding]]; [bodyData appendData:[NSData dataWithData:fileData]]; [bodyData appendData:[[NSString stringWithFormat:@"\r\n--%@--\r\n",boundary] dataUsingEncoding:NSUTF8StringEncoding]]; [request setHTTPBody:bodyData]; NSURLSession *session = [NSURLSession sharedSession]; NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) { dispatch_async(dispatch_get_main_queue(), ^{ NSHTTPURLResponse* httpResponse = (NSHTTPURLResponse*)response; NSInteger responseStatusCode = [httpResponse statusCode]; if(error != nil) NSLog(@"error uploading file %@", error.localizedDescription); else{ NSLog(@"response: %lu", responseStatusCode); NSLog(@"response: %@", data); NSLog(@"response: %@", [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]); } }); }]; [dataTask resume]; }
错误1:返回{"code":500,"reason":"File is null"}
原因
服务器API接收的表单字段名为*file*(对应代码[FromForm] IFormFile file),但iOS端代码中设置的字段名为*userfile*(对应代码name=\"userfile\"),字段名不匹配导致服务器无法识别上传文件,返回文件为空错误。同时原代码未添加服务器要求的name参数,也会影响接口正常解析。
解决办法
修改iOS端表单字段名,补充name参数,同时修正multipart格式的边界符开头(原代码多了前置换行):
// 修正后的bodyData构建逻辑 NSMutableData *bodyData = [[NSMutableData alloc] init]; NSString *boundary = @"---------------1234567890"; // 添加服务器要求的name字段 [bodyData appendData:[[NSString stringWithFormat:@"--%@\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]]; [bodyData appendData:[@"Content-Disposition: form-data; name=\"name\"\r\n\r\n" dataUsingEncoding:NSUTF8StringEncoding]]; [bodyData appendData:[@"audio_record" dataUsingEncoding:NSUTF8StringEncoding]]; // 替换为实际业务需要的name值 [bodyData appendData:[[NSString stringWithFormat:@"\r\n"] dataUsingEncoding:NSUTF8StringEncoding]]; // 修正file字段名,匹配服务器要求 [bodyData appendData:[[NSString stringWithFormat:@"--%@\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]]; [bodyData appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"file\"; filename=\"%@\"\r\n", fileName] dataUsingEncoding:NSUTF8StringEncoding]]; [bodyData appendData:[@"Content-Type: application/octet-stream\r\n\r\n" dataUsingEncoding:NSUTF8StringEncoding]]; [bodyData appendData:fileData]; [bodyData appendData:[[NSString stringWithFormat:@"\r\n--%@--\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
错误2:改用NSURLSessionUploadTask后返回Failed to read the request form. Multipart body length limit 16384 exceeded
原因
ASP.NET Core默认限制multipart表单体长度为16KB(16384字节),30秒音频文件大小通常超过该阈值;同时iOS端使用UploadTask时若未正确设置请求头,可能导致服务器误触发长度校验。
解决办法
- 服务器端调整配置:在
Program.cs(或Startup.cs)中修改multipart长度限制:
builder.Services.Configure<FormOptions>(options => { options.MultipartBodyLengthLimit = 10 * 1024 * 1024; // 设置为10MB,可根据实际需求调整 });
- iOS端确保请求头正确:使用
UploadTask时,需保持Content-Type头和dataTask一致,避免格式错误:
// 使用UploadTask时的请求头设置示例 NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:urlString]]; [request setHTTPMethod:@"POST"]; NSString *boundary = @"---------------1234567890"; NSString *contentType = [NSString stringWithFormat:@"multipart/form-data; boundary=%@", boundary]; [request addValue:contentType forHTTPHeaderField:@"Content-Type"]; // 后续上传逻辑保持与修正后的dataTask一致
额外注意事项
- 确保iOS端
urlString替换为实际服务器上传地址。 - 小文件测试时可打印
bodyData的字符串形式,对比Postman生成的请求体,确保格式完全一致。 - 补充网络异常处理逻辑,比如超时、网络不可用等场景。
内容的提问来源于stack exchange,提问作者Sam
相关产品推荐
相关产品推荐

