iOS图片选择器传图至API报错:无法找到指定文件问题排查
你碰到的这个文件找不到错误,核心是几个细节处理失当导致的,我来一步步拆解问题并给出修正方案:
1. 硬编码文件路径是最大的坑
你在parameters数组里把fileName写死成了固定路径@"/private/var/mobile/Containers/Data/Application/C7C8D8A8-B9E7-4E4E-B35C-DBFB061804D6/tmp/IMG_0082.JPG",但iOS沙盒的容器ID(路径里那串随机字符)每次重启App都会变化,而且你实际生成的有效路径是NSTemporaryDirectory()拼接的path变量,应该直接用这个动态生成的路径,而不是写死的旧路径。
2. 图片文件不能用文本编码读取
你用[NSString stringWithContentsOfFile:param[@"fileName"] encoding:NSUTF8StringEncoding error:&error]读取图片完全错误——图片是二进制数据,不是UTF-8文本,这样读出来的内容会损坏,根本无法正确传递给API。应该直接用NSData读取二进制内容。
3. 图片格式与文件名后缀不匹配
你用UIImagePNGRepresentation(image)把图片转成PNG格式保存,但文件名保留了原图片的.JPG后缀,这会导致文件实际格式和后缀不符,不仅可能触发API解析错误,也会增加调试难度。要么统一改成PNG后缀,要么用对应格式的转换方法(比如UIImageJPEGRepresentation)。
4. 请求头与Content-Type设置混乱
- 你重复设置了
content-type和Content-Type请求头,这会让服务器解析请求时出现歧义,应该只保留带boundary的正确声明; - 图片字段的
Content-Type没有设置,需要根据图片格式指定为image/png或image/jpeg。
5. 可选优化:处理编辑图片为空的情况
UIImagePickerControllerEditedImage只有在用户编辑过图片时才有值,如果用户直接选择原图,这个key会返回nil,最好做个判断, fallback到UIImagePickerControllerOriginalImage。
修正后的完整代码示例
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary<NSString *,id> *)info { [picker dismissViewControllerAnimated:YES completion:nil]; // 优先取编辑后的图片,无编辑则取原图 UIImage *image = info[UIImagePickerControllerEditedImage] ?: info[UIImagePickerControllerOriginalImage]; if (!image) { NSLog(@"Failed to get image from picker"); return; } // 获取原文件名并统一后缀为PNG(和保存格式匹配) NSURL *refURL = info[UIImagePickerControllerReferenceURL]; PHFetchResult *result = [PHAsset fetchAssetsWithALAssetURLs:@[refURL] options:nil]; NSString *originalFilename = [[result firstObject] filename]; NSString *filename = [originalFilename stringByReplacingOccurrencesOfString:[originalFilename pathExtension] withString:@"png"]; // 保存图片到临时目录 NSData *imageData = UIImagePNGRepresentation(image); NSString *path = [NSTemporaryDirectory() stringByAppendingPathComponent:filename]; BOOL saveSuccess = [imageData writeToFile:path atomically:YES]; if (!saveSuccess) { NSLog(@"Failed to save image to temp path"); return; } // 构建正确的请求头:避免重复设置Content-Type NSString *boundary = @"----WebKitFormBoundary7MA4YWxkTrZu0gW"; NSDictionary *headers = @{ @"Content-Type": [NSString stringWithFormat:@"multipart/form-data; boundary=%@", boundary], @"Accept": @"application/json", @"Authorization": @"Token 60f6be2a21bdf731d86a8817b440a1afba692fed", @"Cache-Control": @"no-cache" }; // 用NSMutableData拼接请求体,适配二进制内容 NSMutableData *postData = [NSMutableData data]; // 添加task参数 [postData appendData:[[NSString stringWithFormat:@"--%@\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]]; [postData appendData:[@"Content-Disposition: form-data; name=\"task\"\r\n\r\n" dataUsingEncoding:NSUTF8StringEncoding]]; [postData appendData:[@"4b363547-58da-48a8-b76d-8877dd816d13" dataUsingEncoding:NSUTF8StringEncoding]]; [postData appendData:[@"\r\n" dataUsingEncoding:NSUTF8StringEncoding]]; // 添加图片文件参数 [postData appendData:[[NSString stringWithFormat:@"--%@\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]]; [postData appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"image_file\"; filename=\"%@\"\r\n", filename] dataUsingEncoding:NSUTF8StringEncoding]]; [postData appendData:[@"Content-Type: image/png\r\n\r\n" dataUsingEncoding:NSUTF8StringEncoding]]; [postData appendData:imageData]; // 直接复用保存时的imageData,无需重复读文件 [postData appendData:[@"\r\n" dataUsingEncoding:NSUTF8StringEncoding]]; // 结束请求体边界 [postData appendData:[[NSString stringWithFormat:@"--%@--\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]]; // 发起请求 NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"https://api.vize.ai/v1/classify/"] cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:10.0]; [request setHTTPMethod:@"POST"]; [request setAllHTTPHeaderFields:headers]; [request setHTTPBody:postData]; NSURLSession *session = [NSURLSession sharedSession]; NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) { if (error) { NSLog(@"Request error: %@", error); } else { NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *) response; NSLog(@"Response status code: %ld", (long)httpResponse.statusCode); if (data) { NSString *responseString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; NSLog(@"Response body: %@", responseString); } } }]; [dataTask resume]; }
额外说明
- 改用
NSMutableData拼接请求体,彻底避免文本编码对二进制内容的损坏; - 直接复用保存图片时的
imageData,省去重复读取文件的步骤,提升效率; - 增加了保存图片失败的判断,方便快速排查问题;
- 移除了不必要的
Postman-Token请求头,这个是Postman测试专用的,实际App请求不需要。
内容的提问来源于stack exchange,提问作者fi12

