getx icon indicating copy to clipboard operation
getx copied to clipboard

GetConnect stream has already been listened to on multipart use

Open Ashkan4472 opened this issue 4 years ago • 7 comments

When using multipart for file upload, the "stream has already been listened to" will be thrown

example:

class ModelProvider extends HttpProvider {

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

  Future<Response<MyModel>> addImageToModel({
    Uint8List? image,
  }) {
    final formData = FormData({
      "image": image == null
          ? null
          : MultipartFile(image, filename: 'image'),
      "image2": image == null
          ? null
          : MultipartFile(image, filename: 'image2'),
    });

    return post('myRoute', formData);
  }
}

and this is HttpProvider:

import 'package:get/get.dart';

const API_ENDPOINT = 'http://10.0.2.2:3000/';

class HttpProvider extends GetConnect {
  static String? token = "";
  SecureStorageController secureStorage = Get.find();

  @override
  void onInit() {
    httpClient.baseUrl = API_ENDPOINT;
    httpClient.maxAuthRetries = 2;

    httpClient.addAuthenticator<dynamic>((request) async {
      final token = await this.secureStorage.getAuthToken();
      HttpProvider.token = token;
      if (token != null && token != "") {
        request.headers['Authorization'] = 'Bearer $token';
      }
      return request;
    });

    super.onInit();
  }
}

Expected behavior Upload files without errors

Flutter Version: 2.2.3

Getx Version: ^4.3.7

Ashkan4472 avatar Aug 13 '21 00:08 Ashkan4472

@Ashkan4472 I got same problem with newest version of getx 4.3.8. Did you find the solution ?

phamduoc avatar Sep 12 '21 18:09 phamduoc

this is how i upload multiple images to server using getxConnect

Future postReport(files,description) async{

try{

  final form=FormData({
    'description':description,
  });

  for(var f in files){
    form.files.addAll([
      MapEntry('img[]',MultipartFile(f,filename: f.path.split('/').last))
    ]);
  }


  var response=await post('/reports',form);
  return response.body;
}catch(error){
  return Future.error(error.toString());
};

}

shelzTimesignature avatar Dec 08 '21 20:12 shelzTimesignature

I am using version 4.6.1 and still have this problem. I had to switch to the DIO package.

// simplified code

final picker = ImagePicker();
final Rx<XFile?> image = Rx<XFile?>(null);

// Get image after user action
image.value = await picker.pickImage(
    source: ImageSource.gallery,
);

// Make a form data body
final _body = {
    "image": MultipartFile(await image.value.readAsBytes(), filename: image.value.name),
    "notes": "testing",
};
FormData formData = FormData(_body);

// Sending with GetConnect
post("/target-endpoint", formData, contentType: "multipart/form-data");

roberto-ayala avatar Feb 15 '22 15:02 roberto-ayala

Just to add my voice, I also have this problem with 4.6.1 (uploading a single file with other form fields). I had to switch to a plain http/http.dart implementation to continue with my project.

mp035 avatar May 12 '22 06:05 mp035

Any updates?

andersomdutra avatar Nov 30 '22 18:11 andersomdutra

I'm facing the same problem... 😕

LUK3D avatar Dec 15 '22 00:12 LUK3D

I hope it can help you or someone else @LUK3D @andersomdutra @mp035 @roberto-ayala @shelzTimesignature @phamduoc DON'T USE Content-Type fixed in your headers; I resolve this removing the Content-Type of the headers, the get make this for you.

My example:

import 'package:path/path.dart' as path;
import 'package:get/get_connect/http/src/multipart/form_data.dart' as fd;
import 'package:get/get_connect/http/src/multipart/multipart_file.dart' as mf;
...
      final formData = fd.FormData({
        'type': type,
        'file1': mf.MultipartFile(pathImgFront,
            filename: path.basename(pathImgFront),
            contentType: 'multipart/form-data'),
        'file2': pathImgVerse == null
            ? null
            : await mf.MultipartFile(pathImgVerse,
                filename: path.basename(pathImgVerse),
                contentType: 'multipart/form-data')
      });

      response = await apiRequest.httpClient.post(
        'https://your.url/api',
        body: formData,
        contentType: 'multipart/form-data',
      );

In my class that extends WebRequest that extends GetConnect, and i have the request modifier that add my headers

const baseUrl = 'https://yourbaseurl.com'
class APIRequest extends WebRequest implements GetxService {
  Future<APIRequest> init() async {
    httpClient.baseUrl = baseUrlr;

    httpClient.addAuthenticator((Request request) async {
      request.headers.addAll(HeadersAPI(token: _fetchToken()).getHeaders());

      return request;
    });

And in this headers not use Content-Type.

WebRequest


class WebRequest extends GetConnect implements GetxService {
  Future<WebRequest> init() async {
    httpClient.addRequestModifier((Request request) async {
    
      return request;
    });

    httpClient.addResponseModifier((request, response) async {
      print({
        'event': event,
        'method': request.method,
        'url': request.url.toString(),
        'status': response.statusCode
      });
    
      return response;
    });

    return this;
  }
}

kauemurakami avatar Aug 22 '24 18:08 kauemurakami