You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flutter调用API上传图片遇‘仅允许图片’错误求助

图片上传API返回"only images allowed"错误的解决思路

问题背景

在Flutter中通过代码上传图片时,API始终返回only images allowed错误(状态码422),且Postman测试也出现相同问题。

图片上传调用代码

Future<Either<Failure, String>> uploadImage({required File image}) async {
  final String? accessToken = await secureStorage!.read(key: "access_token");

  return _tokenHandler.executeWithToken(
    apiCall: () async {
      var response = await _apiHelper.uploadFile(
        endPoint: "/upload/image",
        file: image,
        bodyKey: 'image',
        fileName: image.path.split("/").last,
        headers: {
          "Authorization": "Bearer $accessToken",
          "Content-Type": "multipart/form-data",
          "Accept": "*/*"
        },
      );
      return response["image"];
    },
  );
}

原始上传文件实现

Future<Map<String, dynamic>> uploadFile({
    required String endPoint,
    required File file,
    required String bodyKey,
    required String fileName,
    Map<String, dynamic>? headers,
  }) async {
    log("file:$fileName");
    var response = await _dio.post(
      '$_baseUrl$endPoint',
      data: FormData.fromMap({
        bodyKey: await MultipartFile.fromFile(
          file.path,
          filename: fileName,
        )
      }),
      options: Options(
        headers: headers,
      ),
    );
    return response.data;
  }

API错误响应

{
    "message": "only images allowed",
    "error": "Unprocessable Entity",
    "statusCode": 422
}

已尝试的无效方案

添加MIME类型检测并指定contentType,但问题未解决:

import 'package:mime/mime.dart';
import 'package:dio/dio.dart';

Future<Map<String, dynamic>> uploadFile({
  required String endPoint,
  required File file,
  required String bodyKey,
  required String fileName,
  Map<String, dynamic>? headers,
}) async {
  // Detect MIME type
  final mimeType = lookupMimeType(file.path);
  
  if (mimeType == null || !mimeType.startsWith('image/')) {
    throw Exception('Invalid file type. Only images are allowed.');
  }

  // Convert MIME type string to DioMediaType
  final mediaType = MediaType.parse(mimeType);

  var response = await _dio.post(
    '$_baseUrl$endPoint',
    data: FormData.fromMap({
      bodyKey: await MultipartFile.fromFile(
        file.path,
        filename: fileName,
        contentType: mediaType,  // Use DioMediaType
      )
    }),
    options: Options(
      headers: headers,
      contentType: 'multipart/form-data',
    ),
  );
  return response.data;
}

相关日志

[log] file:image_cropper_1735085618268.jpg
[log] file_path:/data/user/0/com.example.todo/cache/image_cropper_1735085618268.jpg
D/EGL_emulation( 5121): app_time_stats: avg=2308.98ms min=2308.98ms max=2308.98ms count=1
[log] add_new_task_body - AddNewTaskFailure:only images allowed

解决建议

  • 验证文件实际内容:
    文件名后缀为.jpg不代表文件是有效图片,可通过读取文件头字节校验:

    final fileBytes = await file.readAsBytes();
    final isJpg = fileBytes.length >= 3 && fileBytes[0] == 0xFF && fileBytes[1] == 0xD8 && fileBytes[2] == 0xFF;
    final isPng = fileBytes.length >=4 && fileBytes[0] == 0x89 && fileBytes[1] == 0x50 && fileBytes[2] == 0x4E && fileBytes[3] == 0x47;
    if (!isJpg && !isPng) {
        throw Exception("文件不是有效图片");
    }
    
  • 检查API校验规则:
    由于Postman也报错,需确认API端的限制:

    • 是否仅允许特定图片格式(如JPG/PNG,不支持WebP等);
    • 是否通过文件名后缀而非MIME类型校验,需确保文件名后缀完全符合API要求;
    • 是否设置了图片大小、分辨率限制。
  • 调整请求配置:
    移除手动设置的Content-Type: multipart/form-data,Dio处理FormData时会自动生成带边界的正确请求头,手动设置会导致服务端解析异常:

    1. 在uploadImage方法的headers中删除"Content-Type": "multipart/form-data";
    2. 在uploadFile的Options中不要手动指定contentType,交由Dio自动处理。
  • 测试原始图片:
    尝试上传未经过裁剪工具处理的原始相册图片,排除裁剪工具生成的文件格式异常问题。


内容的提问来源于stack exchange,提问作者Ahmed Majed

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 08:37:40