Flutter http 400 error when sending an XFile Image - flutter

I want to send a jpg file from my flutter app to .Net backend. I'm using http package.
My code is as follows:
var uri = Uri.parse('$url/upload/$id');
var request = http.MultipartRequest('POST', uri);
var headers = {'accept': '*/*', 'Content-Type': 'multipart/form-data'};
request.headers.addAll(headers);
var x = await file.readAsBytes();
var mFile = http.MultipartFile.fromBytes('file', x);
request.files.add(mFile);
var response = await request.send();
Here file is an XFile file from package cross_file.
Unfortunately I get an error code - 400 "Bad request".
On the backend side code looks as follows
[HttpPost("/upload/{id}")]
public IActionResult UploadImage(IFormFile imageFormFile, [FromRoute] Guid id)
{
// program does not even enter the function
}
I've tested this using Swagger and it works, it generates following curl:
curl -X 'POST' \
'http://localhost:44383/apiname/f7765448-be93-4e72-b62e-04623b4ccdb1' \
-H 'accept: */*' \
-H 'Content-Type: multipart/form-data' \
-F 'imageFormFile=#sample.jpg;type=image/jpeg'
I've searched some forums and tutorials, but nothing works.
I've tried adding the file using fromBytes, fromPath and fromString, none worked.
I've tried experimenting with different combinations of headers and fields, this didn't work either. In particular I've tried to add fields "imageFormFile" and "type", as in Swagger curl, but it didn't work as well.
I've also tried to rewrite this using dio, but got the same result (also I'd rather stick to http, as the rest of my project uses it).

Future uploadRequest(String url, String filePath) async {
final dio = Dio();
dio.options.contentType = "multipart/form-data";
final multiPartFile = await MultipartFile.fromFile(
filePath,
filename: filePath.split('/').last,
);
FormData formData = FormData.fromMap({
"file": multiPartFile,
});
final response = await dio.post(
url,
data: formData,
);
return response.data;
}
This is 100% working solution but with dio package as I prefer it over http. But It doesn't mean that with http it is impossible.
IMPORTANT: formData could be different according to your API

Related

Flutter receives 422 response from Fastapi when posting a PNG file

I have created a working localhost API with FastAPI. The POST takes a PNG, does some image processing and returns a PNG as expected when I click the 'try it out' button in the FastAPI generated docs:
The curl post command shows as follows:
curl -X 'POST' \
'http://localhost:8345/api/predict' \
-H 'accept: application/json' \
-H 'Content-Type: multipart/form-data' \
-F 'file=#test_img.png;type=image/png'
The image File is successfully retrieved from the image picker library. (Where the image1 object has been initialized as File image1; in the app page's class.
Future getImage() async {
var imageTmp = await ImagePicker.pickImage(source: ImageSource.gallery);
setState(() {
image1 = imageTmp;
print('Image Path $image1');
});
}
I tried to emulate the API call with the below function in Flutter.
doUpload() {
/*
curl -X 'POST' \
'http://192.168.178.26:8345/api/predict' \
-H 'accept: application/json' \
-H 'Content-Type: multipart/form-data' \
-F 'file=#test_img.png;type=image/png'
*/
var request = http.MultipartRequest(
'POST',
Uri.parse("http://<my locally hosted ip>:8345/api/predict"),
);
Map<String, String> headers = {"Content-type": "multipart/form-data"};
request.files.add(
http.MultipartFile(
'image',
image1.readAsBytes().asStream(),
image1.lengthSync(),
filename: 'filename',
contentType: MediaType('image', 'png'),
),
);
request.headers.addAll(headers);
print("request: " + request.toString());
request.send().then((value) => print(value.statusCode));
}
When I run the doUpload() function, a POST is successfully sent to the localhost API, but it returns a 422 error 'unprocessable entity'.
What I tried:
I tried to set the image type in doUpload to jpg, jpeg, but I keep getting a 422 error.
I tried looking up where the image_picker is supposed to store the temporary file to see if it's stored correctly, but when I look at the generated filepath, I don't see the actual file and tmp folder:
filepath: File: '/data/user/0/<my package name>/cache/image_picker3300408791299772729jpg'
looking at my local UI filepath, I see:
It shows no folder named cache, so I can't inspect it like this. However, the image picker saves it with a jpg at the end (not .jpg, is this normal?)
I also tried adding this debugger function to my fastAPI server.py, but I'm not sure how I can inspect the resulting data in the current flutter code:
https://fastapi.tiangolo.com/tutorial/handling-errors/#use-the-requestvalidationerror-body
The resulting value has properties like statusCode and reason, but I don't see a full json output option.
To mimic that curl command exactly, use this: (I've used the convenience constructor for simplicity)
final request = http.MultipartRequest(
'POST',
Uri.parse('http://<my locally hosted ip>:8345/api/predict'),
);
request.files.add(
await http.MultipartFile.fromPath(
'file', // NOTE - this value must match the 'file=' at the start of -F
image1.path,
contentType: MediaType('image', 'png'),
),
);
final response = await http.Response.fromStream(await request.send());
print(response.body);

Cannot send DIO post request with multiple form-data body json and image with Flutter

I am currently try to send a post request that works in Postman :
curl --location --request POST 'https://demo-recofaciale.openground.fr/visitors/' \
--form 'visitor="{\"uuid\": \"611633d2766081d524d5fbc3\", \"locale\": \"fr\", \"name\": \"Xxxx XXXXXXX\", \"photoUrl\": \"\"}";type=application/json' \
--form 'photoFile=#"/C:/Users/xxxxxx/Pictures/my-photo.jpeg"'
with Flutter (Flutter 2.2.3) and his Dio (dio: ^4.0.0) package, but I did'nt succeed.
Here is what I tried:
var dio = dio_package.Dio();
File imageFile = File(photo.path!);
String? id = client == null ? _session.user!.id : client.id;
String name = client == null
? "${_session.user!.firstName} ${_session.user!.lastName}"
: "${client.firstName} ${client.lastName}";
var file = await dio_package.MultipartFile.fromFile(imageFile.path,
filename: basename(imageFile.path),
contentType: MediaType("image", basename(imageFile.path)));
var formData = dio_package.FormData.fromMap({
"visitor": json
.encode({"uuid": id, "locale": "fr", "name": name, "photoUrl": ""})
});
formData.files.add(MapEntry('photoFile', file));
dio.options.baseUrl = _config.url!;
await dio.post(
"/visitors",
data: formData,
);
this code returned error Content type 'application/octet-stream' not supported | Status: 415
If anyone has already get thies errors, thanks for advance for your help

Flutter: MultipartRequest returns 403 Forbidden... CURL works fine

I try the following CURL command:
-X PUT \
-H 'Content-Type: application/octet-stream' \
--upload-file ${file_name} \
${url}
To upload a file into Google Storage (https://storage.googleapis.com/...?signature=...&...). This works just fine.
However, when I try to do it using the following Flutter code - I get a response with Error 403 (Forbidden):
Future<http.Response?> uploadVideo(
{required String uploadURL, required filePath}) async {
try {
ByteData bytes = await rootBundle.load(filePath);
var data =
http.MultipartFile.fromBytes('file', bytes.buffer.asInt64List());
Uri uri = Uri.parse(uploadURL);
var request = http.MultipartRequest(
'PUT',
uri,
);
request.files.add(data);
request.headers.addAll({
HttpHeaders.contentTypeHeader: 'Content-Type: application/octet-stream',
});
http.StreamedResponse response = await request.send();
return await http.Response.fromStream(response);
} on SocketException catch (e) {
showNoInternetError(e);
}
Any idea why this happens? The uploadURL is the same as the one I sent through CURL (very long string, with signature and ampersand signs etc).
Thanks a lot!!! <3

How to create a Postman server mock for uploading file and doing some uploading test of Flutter code?

I'm trying to connect to a printer server to be able to save the printing files directly in the printer storage. I'm able to do it using the curl
curl -v -H 'Content-Type:application/octet-stream' 'http://192.168.1.125/upload?X-Filename=model.gcode' --data-binary #model.gcode
Now I'm trying to add this function to a Flutter app but don't works....
So now I am trying to debug the code using a postman server.
Can you help me to create a postman server mock to upload the file as binary, like in this curl code?
curl -v -H 'Content-Type:application/octet-stream' 'http://192.168.1.125/upload?X-Filename=model.gcode' --data-binary #model.gcode
I want to create it because I want to test this Flutter code witch isn't working in the server of the printer.
FLUTTER CODE:
Future<void> uploadFile(File file) async {
///Using HTTP Package
Map<String, String> headers = {
"Content-type": "application/octet-stream",
};
var stream = new http.ByteStream(DelegatingStream.typed(file.openRead()));
var length = await file.length();
var uri = Uri.parse("http://192.168.1.125/upload?X-Filename=nupo.gcode");
var request = new http.MultipartRequest("POST", uri);
var multipartFile = new http.MultipartFile('application', stream, length,
filename: file.path);
request.headers.addAll(headers);
request.files.add(multipartFile);
var response = await request.send();
print(response.statusCode);
response.stream.transform(utf8.decoder).listen((value) {
print(value);
});
}
The server should be able to receive binary file and the command should be upload?X-Filename=filename.gcode, X-Filename is the command to give the name.
(this files are 3D printing files so .gcode is the enstention of motor command)
Postman is not a server usable for this scope. You can use it only for testing an existing server. Best practice with postman or visiti [POstman support][1]

Flutter dio image upload not working throws server 500 error, but works in postman

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