I am trying to upload a jpg image using Form data through the Dio package, the request is working properly on Postman, so i think the problem is either in my code or in the image am trying to upload.
As for the image, I have a bitmap with some edits applied on it, i turn this btm into a Uint8List and then I encode it into a jpg file, i use the Bitmap plugin to do that.
final directory = await getApplicationDocumentsDirectory();
File image = await File('${directory.path}/image.jpg').create();
await image.writeAsBytes(widget.editedBitmap.buildHeaded());
Then i get the image width and height through decoding it
var decodedImage = await decodeImageFromList(imageBytes);
print(decodedImage.width);
print(decodedImage.height);
and then i create my form data
FormData formData = new FormData.fromMap({
'title': titleController.text,
'description': descriptionController.text,
'is_public': privacy == 'Public' ? true : false,
'photo_width': decodedImage.width,
'photo_height': decodedImage.height,
'media_file': await MultipartFile.fromFile(
image.path,
filename: image.path.split("/").last,
contentType: new MediaType("image", "jpeg"),
),
});
then i create my dio instance and configure the options
var dio = new Dio();
dio.options.baseUrl = globals.HttpSingleton().getBaseUrl();
dio.options.connectTimeout = 5000; //5s
dio.options.receiveTimeout = 3000;
dio.options.headers = {
HttpHeaders.acceptHeader: '*/*',
HttpHeaders.authorizationHeader: 'Bearer ' + globals.accessToken,
HttpHeaders.contentTypeHeader: 'multipart/form-data'
};
and here is my request
Response response;
try {
response = await dio.post(
'/photos/upload',
data: formData,
onSendProgress: (int sent, int total) {
print('$sent $total');
},
);
} on DioError catch (e) {
print(e.response.data);
}
And here is the response (e) i get
I/flutter (12154): <html>
I/flutter (12154): <head><title>413 Request Entity Too Large</title></head>
I/flutter (12154): <body>
I/flutter (12154): <center><h1>413 Request Entity Too Large</h1></center>
I/flutter (12154): <hr><center>nginx/1.18.0 (Ubuntu)</center>
I/flutter (12154): </body>
I/flutter (12154): </html>
when I searched about this, it said that 413 happens when my request is too large for the server, however, using postman, and while uploading a bigger sized image, it still works which doesn't make sense to me, any idea what could be the problem?
If any further info is needed please let me know.
Turns out when the server was deployed, if not specified, the upload limit is set to 1MB by default, and due to my mistake of uploading different images with different sizes from phone and postman, it took me a lot of time to figure it out. I contacted the DevOps and it was solved.
Related
I have a button which when clicked, prints out response.
This is how the response is
{
"status": "success",
"user": "Worked well"
}
when I test it with postman it works fine, but when I try it from my flutter project, I get this error
I/flutter ( 5147): Response: - Instance of 'Response'
I/flutter ( 5147): FormatException: Unexpected character (at character 1)
I/flutter ( 5147): <!DOCTYPE html>
I/flutter ( 5147): ^
This is my flutter code:
http.Response response = await http.post(
Uri.parse(url + 'testMe.php'),
headers: headers,
body: body,
);
print('response ${response}');
if (response.body.isNotEmpty) {
json.decode(json.encode(response.body));
} else {
print('Response is empty...');
}
One thing I noticed is that, sometimes it does not throw the error above in flutter, it works fine and sometimes it throws the error, so I don't why it happen that way.
Flu
Postman Header
This worked for me:
Map<String, String> headers = {
'Content-Type': 'application/json',
'Charset': 'utf-8',
};
Your api return you a html instead of json, you can do this to avoid getting FormatException:
http.Response response = await http.post(
Uri.parse(url + 'testMe.php'),
headers: headers,
body: body,
);
print('response ${response}');
if (response.statusCode == 200) {
json.decode(response.body);
} else {
print('Response is empty...');
}
usually when statuscode is 500 or 404 this happened, when you check for status code 200, you can avoid getting this FormatException.
Also you don't need to encode the response and decode it again, your response is already encoded in server side, just decode it.
I am trying to download a .pdf file from an URL having a POST request with body in Flutter.
I am using Dio plugin for network calls.
Here is what I tried to do so far:
Dio dio = Dio();
late Response response;
Future<APIResponse> downloadFile({token}) async {
await dio
.post('https://www.example.com/sample/download',
data: {
{"month": "January", "year": "2022"}
},
options: Options(
headers: {
'Authorization': 'Bearer $token',
},
))
.then((value) {
if (value.statusCode == 200) {
response = value;
// here I need the file to be downloaded
// specific folder would be /downloads folder in Internal Storage
}
});
return APIResponse.fromJson(response.data);
}
But Dio doesn't have POST method with download option, as far as I have checked.
This is what is given in Dio docs:
For downloading a file:
response = await dio.download('https://www.google.com/', './xx.html');
But here we cannot add request body or headers.
Also I need to download the file to a specific folder in the device such as /downloads folder in internal storage.
When download is successful, we can open the file right from that screen.
How to proceed? I am very new to Flutter.
You can use options parameter. You also can add cookie or other parameters.
response = await dio.download(
'https://www.google.com/', './xx.html',
options: Options(
headers: {'cookie': 'any_cookie'},
method: 'POST',
),
);
I'm currently working on a project which like a lot of other projects works with s3 storage. In this case the storage is linked via the back-end.
The situation is like this, I can get the 'attachment' via an URL, lets say example.com/api/attachments/{uuid}. If the user is authorized (via the header Authorization) it should return a 302 statuscode and redirect to the s3 url. The problem is that after the redirect the Authorization header persists and the http client return a 400 response and it's because of the persisting Authorization header. Is there any way I can remove the Authorization header after redirect without catching the first request and firing a new one?
My http client code currently looks like this:
#override
Future get({
String url,
Map<String, dynamic> data,
Map<String, String> parameters,
}) async {
await _refreshClient();
try {
final response = await dio.get(
url,
data: json.encode(data),
queryParameters: parameters,
);
return response.data;
} on DioError catch (e) {
throw ServerException(
statusCode: e.response.statusCode,
message: e.response.statusMessage,
);
}
}
Future<void> _refreshClient() async {
final token = await auth.token;
dio.options.baseUrl = config.baseUrl;
dio.options.headers.addAll({
'Authorization': 'Bearer $token',
'Accept': 'application/json',
});
dio.options.contentType = 'application/json';
}
Good news! This has been fixed recently with Dart 2.16 / Flutter v2.10!
Related bugs in dart issue tracker:
https://github.com/dart-lang/sdk/issues/47246
https://github.com/dart-lang/sdk/issues/45410
Official announcement:
https://medium.com/dartlang/dart-2-16-improved-tooling-and-platform-handling-dd87abd6bad1
TLDR: upgrade to Flutter v2.10!
Looking at the Dio docs, it seems like this is intentional behaviour.
All headers added to the request will be added to the redirection request(s). However, any body send with the request will not be part of the redirection request(s).
https://api.flutter.dev/flutter/dart-io/HttpClientRequest/followRedirects.html
However, I understand (and agree!) that this is generally undesirable behaviour. My solution is to manually follow the redirects myself, which is not very nice but works in a pinch.
Response<String> response;
try {
response = await dio.get(
url,
options: Options(
// Your headers here, which might be your auth headers
headers: ...,
// This is the key - avoid following redirects automatically and handle it ourselves
followRedirects: false,
),
);
} on DioError catch (e) {
final initialResponse = e.response;
// You can modify this to understand other kinds of redirects like 301 or 307
if (initialResponse != null && initialResponse.statusCode == 302) {
response = await dio.get(
initialResponse.headers.value("location")!, // We must get a location header if we got a redirect
),
);
} else {
// Rethrow here in all other cases
throw e;
}
}
This is my postman request with a header auth token.
I am trying to upload image everything is set up as mentioned with dio docs and exactly the same as postman parameter but 500 error is thrown, could not find any mistake here. been stuck here like for 3 hours.
please find any mistake here i am stuck here thanks! (ps: postaman file takes only image file i.e jpg, png other file excluding images will also throw same as 500 error like app is throwing)
and my dio request is:
Future requestChangePhoto(
String wardenToken, String wardenId, File imageFile) async {
String fileName = imageFile.path.split('/').last;
print(fileName);
print(getWardenPhotoChange);
FormData data = FormData.fromMap({
"wardenId": "${wardenId.trim()}",
"photo": await MultipartFile.fromFile(imageFile.path,
filename: fileName, contentType: MediaType("image", "jpg")),
});
Dio dio = new Dio();
dio.options.headers['content-Type'] = 'application/json';
dio.options.headers["authorization"] = "$wardenToken";
await dio
.post("$getWardenPhotoChange", data: data)
.then((response) => print(response.data));
}
This is my ImagePicker and request:
var imageFile = await ImagePicker.pickImage(source: imageType == ImageType.camera? ImageSource.camera: ImageSource.gallery,
imageQuality: 50, maxHeight: 500, maxWidth: 500
);
print(imageFile);
NetworkHandler networkHandler = NetworkHandler();
networkHandler.requestChangePhoto(xybaData.WardenToken, xybaData.wardernId, imageFile);
This is my error:
Since the latest updates, content-type is not treated by Dio as a "normal" header. What I mean is that it ignores that header.
To make it work, set the dio.options.contentType property instead.
Wrapping it up, instead of this:
dio.options.headers['content-Type'] = 'application/json';
try this:
dio.options.contentType = 'application/json';
Bonus:
When creating a Dio instance, you can pass a BaseOptions to its constructor like this:
Dio dio = Dio(
BaseOptions(
headers: {"authorization": wardenToken},
contentType = "application/json",
)
);
I believe its a cleaner way of doing things :D
I have tried uploading image using formData which Dio plugin supports.
FormData formData = new FormData.from(
{"profile_image": UploadFileInfo(image, "profile_image.jpg")});
var response = await _dio.post(ApiConfiguration.getUploadImageUrl().toString(),data: formData);
But its returning error.
DioError [DioErrorType.DEFAULT]: SocketException: OS Error: Connection reset by peer, errno = 54, address = 3.122.199.93, port = 62181
Any help would be appreciated.
UploadFileInfo and FormData.from deprecated in v3. Documentation needs to be clearer and updated. I wasted a good part of a day with this.
I used dio for post a file path with some other information in this way :
Dio dio = new Dio();
FormData formData = new FormData();
formData.add(
"apiKey",
"my_api_key",
);
formData.add(
"file",
"image_path",
);
Response response = await dio.post(
"https://localhost",
data: formData,
onSendProgress: (int sent, int total) {
// do something
},
).catchError((onError) {
throw Exception('something');
});
Faced something similar in Release version of the app, Dio was failing to upload images. This could be due to two reasons, one is mentioned above that you are using a deprecated methods in v3 or You can try adding this permission to the Manifest
Add this permission
<uses-permission android:name="android.permission.INTERNET" />
to your app level Manifest.
android/app/src/main/AndroidManifest.xml
Solution can be found here
https://stackoverflow.com/a/59392036/14641365
Although the other work around to upload an image to the server that I used is something as follows
String url = "YOUR_URL_TO_UPLOAD";
FormData formData = FormData.fromMap({
"QUERY_PARAM_NAME": await MultipartFile.fromFile("IMAGE_PATH", filename:"TESTING.jpg"),
});
var response = await dio.post(url, data: formData);