Search code examples
flutterrestmultipartform-datafastapidio

Unhandled Exception: Bad state: Can't finalize a finalized MultipartFile and only 1 image gets uploaded


Using DIO to upload images + Data to my fastAPI. In my case, the data gets uploaded but only one image gets uploaded. And i recieve this Unhandled exception error in the debug console. Has anyone faced this issue recently? In the backend, i get the 201 created so the fastAPI backend works really well. Here is the upload method i am using:

  _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": _pNameC.text,
          "price": _pPriceC.text,
          "description": _pDescriptionC.text,
          "files": _images,
        },
      );
      var response =
          await DioClient.dio.post("http://10.0.2.2:8000/products/addProductFD",
              data: formData,
              options: Options(
                  contentType: 'multipart/form-data',
                  followRedirects: false,
                  validateStatus: (status) {
                    return status! < 500;
                  }));

      debugPrint(response.toString());
    }
  }

The error i am getting:

I/flutter ( 9021): {"name":"ugu","price":25.0,"is_active":true,"imgs_url":["localhost:8000/static/product_images/30e2b2d6d58f8a23a5d6.jpg"],"id":11,"description":"kjhkaz","owner_id":null}
E/flutter ( 9021): [ERROR:flutter/lib/ui/ui_dart_state.cc(198)] Unhandled Exception: Bad state: Can't finalize a finalized MultipartFile.
E/flutter ( 9021): #0      MultipartFile.finalize
package:dio/src/multipart_file.dart:133

Please help!


Solution

  • I was forcing the for loop to work not knowing that the DIO docs said that mapping the files to list is the correct way to upload multiple images. So i removed the for-loop and just mapped the images to list inside the form data and everyting is working fine now. Thanks to this post https://stackoverflow.com/a/64683825/12986987. My new upload code for Flutter(image_picker) to FastAPI is:

      _uploadImage() async {
        var formData = FormData.fromMap(
          {
            // need to await for this async operation
            "name": _pNameC.text,
            "price": _pPriceC.text,
            "description": _pDescriptionC.text,
            "files": imageFileList!
                .map((item) => MultipartFile.fromFileSync(item.path,
                    filename: item.path.split('/').last))
                .toList()
          },
        );
        var response =
            await DioClient.dio.post("http://10.0.2.2:8000/products/addProductFD",
                data: formData,
                options: Options(
                    contentType: 'multipart/form-data',
                    followRedirects: false,
                    validateStatus: (status) {
                      return status! < 500;
                    }));
        debugPrint(_images!.join(","));
        debugPrint(response.toString());
      }
    

    FastAPI Backend:

    @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
    

    For the image_picker in flutter:

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

    Thats all.