fastapi+使用dio和image_picker上载多个图像时,颤动422无法处理的实体

发布于 2025-02-07 11:54:43 字数 6577 浏览 0 评论 0原文

在Flutter Frontend上将多个图像与一些文本一起上传到Fastapi时,我会遇到422错误。我相信这与我的前端(颤音)有关,因为FastAPI上传端点在Swagger Docs UI localhost中起作用:8000/docs 。 我已经浏览了与此错误有关的所有堆栈流问题,并尝试在多部分添加标头和内容类型并仍然有错误。请有人告诉我我做错了什么? 下面是我的add_product.dart:

import 'dart:io';

import 'package:afia4/data/dio/dio_client.dart';
import 'package:dio/dio.dart';
import 'package:flutter/material.dart';
import 'package:http_parser/http_parser.dart';
import 'package:image_picker/image_picker.dart';

class AddProduct extends StatefulWidget {
  const AddProduct({Key? key}) : super(key: key);

  @override
  State<AddProduct> createState() => _AddProductState();
}

class _AddProductState extends State<AddProduct> {
  final formKey = GlobalKey<FormState>();
  final productNameController = TextEditingController();
  final productPriceController = TextEditingController();
  final productDescriptionController = TextEditingController();
  List<dynamic>? _product = [];
  Dio dio = Dio();
  final ImagePicker imagePicker = ImagePicker();

  List<XFile>? imageFileList = [];
  List<dynamic>? _images = [];

  void selectImages() async {
    final List<XFile>? selectedImages = await imagePicker.pickMultiImage();
    if (selectedImages!.isNotEmpty) {
      imageFileList!.addAll(selectedImages);
    }
    setState(() {});
  }

  @override
  void initState() {
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          leading: const Icon(Icons.close),
        ),
        body: Form(
          key: formKey,
          child: Column(
            children: [
              const SizedBox(
                height: 20,
              ),
              productNameField(),
              const SizedBox(
                height: 20,
              ),
              productPriceMethod(),
              const SizedBox(
                height: 20,
              ),
              productDescriptionMethod(),
              imageSelectionMethod(),
              const SizedBox(
                height: 20,
              ),
              imagePreview(),
              ElevatedButton(
                onPressed: _uploadImage,
                child: const Text("Upload images"),
              ),
            ],
          ),
        ));
  }

  _uploadImage() async {
    for (int i = 0; i < imageFileList!.length; i++) {
      var path = imageFileList![i].path;
      _images!.add(await MultipartFile.fromFile(path,
          filename: path.split('/').last,
          contentType: MediaType("image", "jpg")));
      var formData = FormData.fromMap(
        {
          // need to await for this async operation
          "name": productNameController,
          "price": productPriceController,
          "description": productDescriptionController,
          "image": _images,
        },
      );
      var response = await DioClient.dio.post(
          "http://10.0.2.2:8000/products/addProductFD",
          data: formData,
          options: Options(contentType: 'multipart/form-data'));
      debugPrint(response.toString());
    }
  }
}

这是后端(fastapi)的途径,它运行良好:

@router.post('/addProductFD', status_code=status.HTTP_201_CREATED)
async def create(
    name: str = Form(...),
    price: float = Form(...),
    description: str = Form(...),
    files: List[UploadFile] = File(...),
    db: Session = Depends(get_db),
    # current_user: Vendor = Depends(get_current_active_user)
):
    fileList = []
    for file in files:
        try:
            FILEPATH = "./static/product_images/"
            pimage_name = FILEPATH + imghex(file.filename)
            contents = await file.read()
            with open(pimage_name, 'wb') as f:
                f.write(contents)
        except Exception:
            return {"message": "There was an error uploading the file(s)"}
        finally:
            await file.close()
        fileList.append("localhost:8000" + pimage_name[1:])

    file_urls = ",".join(fileList)
    new_item = Product(
        name=name,
        price=price,
        description=description,
        imgs_url=[file_urls],
        # owner_id=current_user.id
    )

    db.add(new_item)
    db.commit()
    db.refresh(new_item)
    return new_item

这是Swaggerui或Postman的示例响应:

{
  "name": "iPhone 44",
  "price": 1800,
  "is_active": true,
  "imgs_url": [
    "localhost:8000/static/product_images/0337698056d37da14fae.jpg,localhost:8000/static/product_images/db2f6998aa87a611c89e.jpg"
  ],
  "id": 4,
  "description": "Brand New with a fresh box.",
  "owner_id": null
}

这是卷曲响应:

curl -X 'POST' \
  'http://127.0.0.1:8000/products/addProductFD' \
  -H 'accept: application/json' \
  -H 'Content-Type: multipart/form-data' \
  -F 'name=iPhone 44' \
  -F 'price=1800' \
  -F 'description=Brand New with a fresh box.' \
  -F '[email protected];type=image/jpeg' \
  -F '[email protected];type=image/jpeg'

请在我的情况下如何修复此422?任何帮助将不胜感激。谢谢。

更多的颤音erros

I/Choreographer(10219): Skipped 34 frames!  The application may be doing too much work on its main thread.
D/EGL_emulation(10219): app_time_stats: avg=259.93ms min=16.55ms max=592.22ms count=6
I/flutter (10219): {"detail":[{"loc":["body","price"],"msg":"value is not a valid float","type":"type_error.float"},{"loc":["body","files"],"msg":"field required","type":"value_error.missing"}]}
D/EGL_emulation(10219): app_time_stats: avg=47.31ms min=10.68ms max=340.63ms count=21
D/EGL_emulation(10219): app_time_stats: avg=500.29ms min=498.81ms max=501.77ms count=2
D/EGL_emulation(10219): app_time_stats: avg=500.14ms min=498.66ms max=501.04ms count=3
D/EGL_emulation(10219): app_time_stats: avg=500.88ms min=499.01ms max=504.05ms count=3
D/EGL_emulation(10219): app_time_stats: avg=498.13ms min=495.97ms max=500.18ms count=3
D/EGL_emulation(10219): app_time_stats: avg=500.05ms min=498.99ms max=501.57ms count=3
D/EGL_emulation(10219): app_time_stats: avg=500.46ms min=499.61ms max=501.69ms count=3
D/EGL_emulation(10219): app_time_stats: avg=499.81ms min=496.76ms max=501.59ms count=3

When uploading multiple images alongside some text to fastAPI from flutter frontend i encounter the 422 error. I believe it has to do with my frontend(flutter) because the fastAPI upload endpoint works in the swagger docs ui localhost:8000/docs.
I have looked through all the stackoverflow questions related to this error and tried adding a header and content type to the multipart and still having the error. PLease can someone tell me what i am doing wrong?
Below is my add_product.dart:

import 'dart:io';

import 'package:afia4/data/dio/dio_client.dart';
import 'package:dio/dio.dart';
import 'package:flutter/material.dart';
import 'package:http_parser/http_parser.dart';
import 'package:image_picker/image_picker.dart';

class AddProduct extends StatefulWidget {
  const AddProduct({Key? key}) : super(key: key);

  @override
  State<AddProduct> createState() => _AddProductState();
}

class _AddProductState extends State<AddProduct> {
  final formKey = GlobalKey<FormState>();
  final productNameController = TextEditingController();
  final productPriceController = TextEditingController();
  final productDescriptionController = TextEditingController();
  List<dynamic>? _product = [];
  Dio dio = Dio();
  final ImagePicker imagePicker = ImagePicker();

  List<XFile>? imageFileList = [];
  List<dynamic>? _images = [];

  void selectImages() async {
    final List<XFile>? selectedImages = await imagePicker.pickMultiImage();
    if (selectedImages!.isNotEmpty) {
      imageFileList!.addAll(selectedImages);
    }
    setState(() {});
  }

  @override
  void initState() {
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          leading: const Icon(Icons.close),
        ),
        body: Form(
          key: formKey,
          child: Column(
            children: [
              const SizedBox(
                height: 20,
              ),
              productNameField(),
              const SizedBox(
                height: 20,
              ),
              productPriceMethod(),
              const SizedBox(
                height: 20,
              ),
              productDescriptionMethod(),
              imageSelectionMethod(),
              const SizedBox(
                height: 20,
              ),
              imagePreview(),
              ElevatedButton(
                onPressed: _uploadImage,
                child: const Text("Upload images"),
              ),
            ],
          ),
        ));
  }

  _uploadImage() async {
    for (int i = 0; i < imageFileList!.length; i++) {
      var path = imageFileList![i].path;
      _images!.add(await MultipartFile.fromFile(path,
          filename: path.split('/').last,
          contentType: MediaType("image", "jpg")));
      var formData = FormData.fromMap(
        {
          // need to await for this async operation
          "name": productNameController,
          "price": productPriceController,
          "description": productDescriptionController,
          "image": _images,
        },
      );
      var response = await DioClient.dio.post(
          "http://10.0.2.2:8000/products/addProductFD",
          data: formData,
          options: Options(contentType: 'multipart/form-data'));
      debugPrint(response.toString());
    }
  }
}

This is the route for the backend(fastAPI) and it works well:

@router.post('/addProductFD', status_code=status.HTTP_201_CREATED)
async def create(
    name: str = Form(...),
    price: float = Form(...),
    description: str = Form(...),
    files: List[UploadFile] = File(...),
    db: Session = Depends(get_db),
    # current_user: Vendor = Depends(get_current_active_user)
):
    fileList = []
    for file in files:
        try:
            FILEPATH = "./static/product_images/"
            pimage_name = FILEPATH + imghex(file.filename)
            contents = await file.read()
            with open(pimage_name, 'wb') as f:
                f.write(contents)
        except Exception:
            return {"message": "There was an error uploading the file(s)"}
        finally:
            await file.close()
        fileList.append("localhost:8000" + pimage_name[1:])

    file_urls = ",".join(fileList)
    new_item = Product(
        name=name,
        price=price,
        description=description,
        imgs_url=[file_urls],
        # owner_id=current_user.id
    )

    db.add(new_item)
    db.commit()
    db.refresh(new_item)
    return new_item

This is a sample response from the swaggerUI or postman:

{
  "name": "iPhone 44",
  "price": 1800,
  "is_active": true,
  "imgs_url": [
    "localhost:8000/static/product_images/0337698056d37da14fae.jpg,localhost:8000/static/product_images/db2f6998aa87a611c89e.jpg"
  ],
  "id": 4,
  "description": "Brand New with a fresh box.",
  "owner_id": null
}

This is the curl response:

curl -X 'POST' \
  'http://127.0.0.1:8000/products/addProductFD' \
  -H 'accept: application/json' \
  -H 'Content-Type: multipart/form-data' \
  -F 'name=iPhone 44' \
  -F 'price=1800' \
  -F 'description=Brand New with a fresh box.' \
  -F '[email protected];type=image/jpeg' \
  -F '[email protected];type=image/jpeg'

Please how do i fix this 422 in my case? Any help will be appreciated. Thanks.

enter image description here

More FLutter erros

I/Choreographer(10219): Skipped 34 frames!  The application may be doing too much work on its main thread.
D/EGL_emulation(10219): app_time_stats: avg=259.93ms min=16.55ms max=592.22ms count=6
I/flutter (10219): {"detail":[{"loc":["body","price"],"msg":"value is not a valid float","type":"type_error.float"},{"loc":["body","files"],"msg":"field required","type":"value_error.missing"}]}
D/EGL_emulation(10219): app_time_stats: avg=47.31ms min=10.68ms max=340.63ms count=21
D/EGL_emulation(10219): app_time_stats: avg=500.29ms min=498.81ms max=501.77ms count=2
D/EGL_emulation(10219): app_time_stats: avg=500.14ms min=498.66ms max=501.04ms count=3
D/EGL_emulation(10219): app_time_stats: avg=500.88ms min=499.01ms max=504.05ms count=3
D/EGL_emulation(10219): app_time_stats: avg=498.13ms min=495.97ms max=500.18ms count=3
D/EGL_emulation(10219): app_time_stats: avg=500.05ms min=498.99ms max=501.57ms count=3
D/EGL_emulation(10219): app_time_stats: avg=500.46ms min=499.61ms max=501.69ms count=3
D/EGL_emulation(10219): app_time_stats: avg=499.81ms min=496.76ms max=501.59ms count=3

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文