Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

http.MultipartRequest with utf-8 support for body fields #159

Closed
Eimji opened this issue Apr 23, 2018 · 19 comments
Closed

http.MultipartRequest with utf-8 support for body fields #159

Eimji opened this issue Apr 23, 2018 · 19 comments
Labels
needs-info Additional information needed from the issue author

Comments

@Eimji
Copy link

Eimji commented Apr 23, 2018

Hello

As suggested here, I would like to submit one issue with http.MultipartRequest. How to support utf-8 format for the body fields?

I'm writing a Flutter application, which includes a form to upload one image along with form fields

Future<Map<String, dynamic>> multipartPostRequest(String method, String route, Map<String, String> bodyFields,  List<http.MultipartFile> files) async {
    // method = POST or PUT...
    var url = Uri.parse( Application.backendBaseURL + route);
    var request = new http.MultipartRequest(method, url)
    ..fields.addAll(bodyFields);
    if (files != null && files.length > 0)
      request.files.addAll(files);    


    return LocalAuth.getJWToken()
    .then((token) {
      request.headers[HttpHeaders.AUTHORIZATION] = 'Bearer $token';
      return request.send();
    })
    .then((streamedResponse) {
      _updateJWTokenFrom(streamedResponse);
      return http.Response.fromStream(streamedResponse);
    })
    .then((response) {
      return response.body;
    })
    .then((body) {
      if (isJSON(body))
        return json.decode(body);
      else 
        return {'mssg': body};
    })
    .catchError((e) {
      return {'mssg': 'Got error: $e'};
    });
  }
}

This code for making a POST as multipart/form-data works well, but whan a body field contains characters such as é à ç, the field is reset to empty. So my question is how can I make a post multipart/form-data with fields in utf-8 ?
Trying to add request.headers[HttpHeaders.CONTENT_TYPE] = 'multipart/form-data;charset=utf-8'; does not change.

Thanks a lot in advance

@nex3
Copy link
Member

nex3 commented Apr 24, 2018

I'm not sure I understand exactly what's going on. You say "when a body field contains characters such as é à ç, the field is reset to empty"... what does it mean for the field to be "reset to empty"? Is the field empty in the in-flight request data? Or are you checking based on how the server treats the data? Are you sure it's not an issue with the server's handling of UTF-8?

@Eimji
Copy link
Author

Eimji commented Apr 24, 2018

Thanks. My form in my mobile application mainly contains two text inputs (title and content) and one file input (for image).
The API server is developed in Go, and I test the condition if the body parameters corresponding to the text inputs are not empty. My server implementation works. I tested with Postman with the characters ç à é etc
Here is code on the server side

func CreateNewPost(c *gin.Context) {
	title := strings.TrimSpace(c.PostForm("title"))
	content := strings.TrimSpace(c.PostForm("content"))
	latitude := c.PostForm("latitude")
	longitude := c.PostForm("longitude")
	imageFile, _ := c.FormFile("images")
	replyTo := c.PostForm("reply_to")
	private := c.PostForm("private") != ""
	createdBy := c.PostForm("from")

	language := c.Query("lang")

	var (
		imageFilename string = ""
		user          User
		newPost       Post
	)

	if title == "" && content == "" && (imageFile == nil || imageFile.Filename == "") {
		if language == "fr" {
			c.JSON(http.StatusBadRequest, gin.H{"code": 400, "mssg": "Votre ping est vide !"})
		} else {
			c.JSON(http.StatusBadRequest, gin.H{"code": 400, "mssg": "Your ping is empty!"})
		}
		return
	}
....

Dart http.MultipartRequest works well with my implementation. However, I'm in France, and we have letters such as é à ç ö etc, so when filling my form in French with those characters, on the server side, the body parameters are empty ! I think it is a utf8 issue with http.MultipartRequest ?

@Eimji
Copy link
Author

Eimji commented Apr 25, 2018

Does anybody can help me?
This code doesn't work, the content field is reset to empty string

    var url = Uri.parse( Application.backendBaseURL + route);
    var request = new http.MultipartRequest('POST', url);
    request.fields['content'] = 'à éviter, ça marche pas!';

    if (files != null && files.length > 0)
      request.files.addAll(files);    

    request.send().then((response) {
       if (response.statusCode == 200) print("Uploaded!");
    });

But if I write
request.fields['content'] = 'a eviter, ca marche pas!';
The content field is sent correctly to the backend API
This means there may be an issue with utf-8 fields, right ?

@nex3
Copy link
Member

nex3 commented Apr 25, 2018

Can you post a pure-Dart reproduction, that includes server code as well?

@lucasjinreal
Copy link

Damn, I got this issue too...... I think this is imergency cause Not-English has seriously problem with http.MultipartRequest this class.
I am in China, I do exactly like @Eimji , with field contains Chinese such as 妈的,我日了, and the server side gots empty.
However I am not test on both Android or iOS side, at least, on iOS side we have this trouble.

I wanna suggested Flutter authors or it's contributors work on this issue quickly, cause flutter is groth very fast, but this basic package are still with so many bugs... Just report this, help solve it very soon.

BTW, @Eimji Did'u find any solution on this? #

@Eimji
Copy link
Author

Eimji commented May 27, 2018

Hello

I didn't find any solution. My backend implementation works (tested with Postman and browser) I tried many workarounds to fix the issue but no success. Due to deadlines in my dev planning, I finally implemented a trick in my Flutter application. Instead of sending one POST request with multipart I send two successive POST requests: one for text inputs and one for file inputs. Not the best workaround but it works!
Here is my app https://play.google.com/store/apps/details?id=com.orange.labs.spilhenn
You can also find the iOS version here
https://itunes.apple.com/fr/app/spilhenn/id1385377725

I will post a sample code in Dart for client and in Go for backend to reproduce the issue

@asmh1989
Copy link

asmh1989 commented Jul 10, 2018

@Eimji @jinfagang

In my flutter app, I commented on the following code, I got the Go backend to recognize it properly.

multipart_request.dart

  /// Returns the header string for a field. The return value is guaranteed to
  /// contain only ASCII characters.
  String _headerForField(String name, String value) {
    var header =
        'content-disposition: form-data; name="${_browserEncode(name)}"';
//    if (!isPlainAscii(value)) {
//      header = '$header\r\n'
//          'content-type: text/plain; charset=utf-8\r\n'
//          'content-transfer-encoding: binary';
//    }
    return '$header\r\n\r\n';
  }

@iamsaurabhc
Copy link

Hello

I didn't find any solution. My backend implementation works (tested with Postman and browser) I tried many workarounds to fix the issue but no success. Due to deadlines in my dev planning, I finally implemented a trick in my Flutter application. Instead of sending one POST request with multipart I send two successive POST requests: one for text inputs and one for file inputs. Not the best workaround but it works!
Here is my app https://play.google.com/store/apps/details?id=com.orange.labs.spilhenn
You can also find the iOS version here
https://itunes.apple.com/fr/app/spilhenn/id1385377725

I will post a sample code in Dart for client and in Go for backend to reproduce the issue

Did you find any answer to this? I am also facing the same

@phuquy2114
Copy link

phuquy2114 commented Jan 5, 2019

please follow my code, It's not working when upload file audio and video, please help me!
Map<String, String> headers = {
"Authorization": "Bearer $token",
"Content-Type": "multipart/form-data"
};
var uri = Uri.parse(settings.baseUrl + "/update-introduce");
print(uri);
var request = new http.MultipartRequest("POST", uri);
// add header
request.headers.addAll(headers);

request.fields['bio'] =  " Hello hi everybody ",

request.files.add(await http.MultipartFile.fromPath(
'audio_file', _formData['audio_file'].path));
request.files.add(await http.MultipartFile.fromPath(
'movie_file', _formData['movie_file'].path));

StreamedResponse response = await request.send().then((response) {
  print("successful");
  print(response.toString());
});

print(response.toString());
response.stream.transform(utf8.decoder).listen((value) {
  print(value);
});

this is error show log
I/flutter ( 9636): null
E/flutter ( 9636): [ERROR:flutter/shell/common/shell.cc(184)] Dart Error: Unhandled exception:
E/flutter ( 9636): NoSuchMethodError: The getter 'stream' was called on null.
E/flutter ( 9636): Receiver: null

@jeeali
Copy link

jeeali commented May 5, 2020

Does anybody can help me?
This code doesn't work, the content field is reset to empty string

    var url = Uri.parse( Application.backendBaseURL + route);
    var request = new http.MultipartRequest('POST', url);
    request.fields['content'] = 'à éviter, ça marche pas!';

    if (files != null && files.length > 0)
      request.files.addAll(files);    

    request.send().then((response) {
       if (response.statusCode == 200) print("Uploaded!");
    });

But if I write
request.fields['content'] = 'a eviter, ca marche pas!';
The content field is sent correctly to the backend API
This means there may be an issue with utf-8 fields, right ?

I'm getting the same error for Arabic.
It is sending the English text easily. But, I Can't send the Arabic.

@asdk2006
Copy link

any solution i face that in arabic

@wfe8006
Copy link

wfe8006 commented May 30, 2020

So I faced the same issue too (using utf8 / non-ascii charset) when doing http.MutipartRequest with slightly a different result:

HttpException: Unsupported contentTransferEncoding: binary
#0      new HttpMultipartFormDataImpl (package:http_server/src/http_multipart_form_data_impl.dart:36:7)
#1      HttpMultipartFormDataImpl.parse (package:http_server/src/http_multipart_form_data_impl.dart:86:12)
#2      HttpMultipartFormData.parse (package:http_server/src/http_multipart_form_data.dart:70:33)
#3      HttpBodyHandlerImpl.process.asFormData.<anonymous closure> (package:http_server/src/http_body_impl.dart:100:48)
#4      _MapStream._handleData (dart:async/stream_pipe.dart:229:21)
---snip---

In my case the dart backend threw Unsupported contentTransferEncoding: binary and a good chap came to my rescue: dart-archive/http_server@1cf7853

So I just changed the source to the commit id directly and it works ok now.

  #http_server: ^0.9.8+3
  http_server:
    git:
      url: https://github.com/dart-lang/http_server.git
      ref: df31d20
sotisoti@debian:~/dart_api$ pub get
Resolving dependencies... (2.6s)
* http_server 0.9.8+4-dev from git https://github.com/dart-lang/http_server.git at df31d2 (was 0.9.8+3)
Changed 1 dependency!

@insinfo
Copy link

insinfo commented Jun 29, 2020

@sotisoti

I'm also having this problem with a backend made with Angel Framework

@caseyryan
Copy link

caseyryan commented May 19, 2021

Any dart backend is having this issue. But the funny thing is that Postman can send the same data and dart will accept it normally. I've been struggling with it for several days already and still haven't found a solution

@insinfo
Copy link

insinfo commented May 22, 2021

I made a fork of the Angel Framework, called Galileo Framework which is working very well for me, even without this problem and already updated to work with dart 2.12, I use it already in production together with AngularDart 6 with dart 2.12 without problems.

https://pub.dev/packages/galileo_framework

@caseyryan
Copy link

caseyryan commented May 22, 2021

Hi

I made a fork of the Angel Framework, called Galileo Framework which is working very well for me, even without this problem and already updated to work with dart 2.12, I use it already in production together with AngularDart 6 with dart 2.12 without problems.

https://pub.dev/packages/galileo_framework

Hi! Thanks but this is not really a solution, at least for me because I'm using Jaguar (I'll take a look at your framework though, for further projects maybe). I found a true reason why it happens. It happens because dart formdata adds a content-transport-encoding: binary for all text fields that are non english. Probably dart 2.12 already has a fix for this but I didn't check. I've implemented FormData and created my on class that sends them without this header. BTW Postman also doesn't add this header. So, it's not only a backend or frontend error but an error of dart in all

@busslina
Copy link

busslina commented Sep 5, 2022

Facing same issue with spanish accent characters. As HTTP client I'm using dio with multipart support. The funny thing is that dio's default request encoder is UTF8 which would normally work with different language and culture charsets.

@jibon0070
Copy link

jibon0070 commented Dec 21, 2023

I have the same problem, any solution???

Copy link

github-actions bot commented Nov 1, 2024

Without additional information we're not able to resolve this issue. Feel free to add more info or respond to any questions above and we can reopen the case. Thanks for your contribution!

@github-actions github-actions bot closed this as not planned Won't fix, can't repro, duplicate, stale Nov 1, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
needs-info Additional information needed from the issue author
Projects
None yet
Development

No branches or pull requests