Flutter app is closing when sending request using Dio with huge data - flutter

I am trying to send a huge image using FormData and Dio, the server side is written in NodeJS
Whenever I send an image for let's say 1 MB and less it's working perfectly, but I've tried to send an image with 17 MB size the application lagged for a while, then exited
here's the flutter code
Future<Uint8List> _convertImageToUint8List(final String imagePath) async {
ByteData imageByteDate = await rootBundle.load(imagePath);
ByteBuffer imageBuffer = imageByteDate.buffer;
return imageBuffer.asUint8List();
}
void _testUploadingImageWithDio(
{final String ip,
final String port,
final String api,
final String imagePath}) async {
Dio dio = new Dio();
Uint8List imageUint8List = await _convertImageToUint8List(imagePath);
FormData formData = new FormData.fromMap({
'image': imageUint8List,
});
String url = 'http://$ip:$port/$api';
print('URL: $url}');
var response = await dio.post(url, data: formData);
print('response: $response');
}
As well as I've tried another class in flutter MultipartRequest, but the image was doubled in its size, I mean the image with 17 MB reached the server with size 32 MB and the image was corrupted.
Here's the code
void _testUploadingImageWithMultiPartRequest(
{final String ip,
final String port,
final String api,
final String imagePath}) async {
// Constructing the request
Uri postUri = Uri.parse('http://$ip:$port/$api');
print('URI ${postUri.toString()}');
http.MultipartRequest request = new http.MultipartRequest("POST", postUri);
// request.fields['user'] = 'someone#somewhere.com';
// Loading the image from image path using root bundle
// convert it to buffer and uint8 list
// append it to the request
Uint8List imageUint8List = await _convertImageToUint8List(imagePath);
http.MultipartFile imageToBeSent = new http.MultipartFile.fromBytes(
'image', imageUint8List,
contentType: new MediaType('image', 'png'));
request.files.add(imageToBeSent);
// Sending the request
request.send().then((response) {
if (response.statusCode == 200) print("Uploaded!");
}).catchError((onError) {
print(onError);
});
}
For server side application I am doing the following
const upload = multer({storage: storage, fileFilter: fileFilter, limits: {fieldSize: 200 * 1024 * 1024}});
app.post('/upload', upload.single('image'), (req, res) => {
const buffer = Buffer.from(req.body['image']);
fs.writeFileSync('./some_image', buffer);
try {
return res.status(200).json({
message: 'File uploded successfully'
});
} catch (error) {
console.error(error);
}
});
Edit
For the 17 MB image
For the corrupted one, on server side
For a small image which works fine

Is this server config related?
For example, for Nginx has a config: client_max_body_size 50M;

Related

Flutter AZURE BLOB IMAGE UPLOAD - How to upload image captured using mobile camera to azure blob storage

I have been working for few since yesterday to try upload an image to azure blob storage taken using mobile camera form iOS/Android device.
I am able to upload the files but for some reason they being corrupted not able to open the image uploaded.
Please check the image error while opening the uploaded image
I am using flutter package http with different approach all work in uploading image file to azure blob store but it gets corrupted somehow , I tried forcing the ContentType to image/jpeg but no help.
Here is code I am using an http API -
takePicture() async {
final pickedFile = await picker.getImage(source: ImageSource.camera);
setState(() {
if (pickedFile != null) {
_image = File(pickedFile.path);
String fileName = basename(pickedFile.path);
uploadFile(fileName, image);
} else {
print('No image selected.');
}
});
}
First approach -->
http.Response response = await http.put(
uri,
headers: {
"Content-Type": 'image/jpeg',
"X-MS-BLOB-TYPE": "BlockBlob",
},
body: image.path,
);
print(response.statusCode);
Using Approach second -->
final data = image.readAsBytesSync();
var dio = Dio();
dio.options.headers['x-ms-blob-type'] = 'BlockBlob';
dio.options.headers['Content-Type'] = 'image/jpeg';
try {
final response = await dio.put(
'$url/$fileName?$token',
data: data,
onSendProgress: (int sent, int total) {
if (total != -1) {
print((sent / total * 100).toStringAsFixed(0) + "%");
}
},
);
print(response.statusCode);
} catch (e) {
print(e);
}
Approach third -->
var request = new http.MultipartRequest("PUT", postUri);
request.headers['X-MS-BLOB-TYPE'] = 'BlockBlob';
request.headers['Content-Type'] = 'image/jpeg';
request.files.add(
new http.MultipartFile.fromBytes(
'picture',
await image.readAsBytes(),
),
);
request.send().then((response) {
uploadResponse.add(response.statusCode);
}, onError: (err) {
print(err);
});
Help here is much appreciated.
If you want to upload the image to Azure Blob Storage in the flutter application, you can use the Dart Package azblob to implement it. Regarding how to use the package, please refer to here.
For example
import 'package:image_picker/image_picker.dart';
import 'package:flutter/material.dart';
import 'package:azblob/azblob.dart';
import 'package:mime/mime.dart';
...
//use image_picker to get image
Future uploadImageToAzure(BuildContext context) async {
try{
String fileName = basename(_imageFile.path);
// read file as Uint8List
Uint8List content = await _imageFile.readAsBytes();
var storage = AzureStorage.parse('<storage account connection string>');
String container="image";
// get the mine type of the file
String contentType= lookupMimeType(fileName);
await storage.putBlob('/$container/$fileName',bodyBytes: content,contentType: contentType,type: BlobType.BlockBlob);
print("done");
} on AzureStorageException catch(ex){
print(ex.message);
}catch(err){
print(err);
}
Unfortunately, the multipart form is causing break of image. I don't know how it works on azure side, because there is little or no information about multipart uploads, but it's clearly broken because of multipart form. I replicated the problem in .net core application and whenever i am using multipart form data to upload image - it is broken. When i am using simple ByteArrayContent - it works. I couldn't find flutter equivalent to ByteArrayContent, so i am lost now :( The package mentioned by #Jim is useless for me, because i want to give clients sas url, so they have permission to upload image on client side. I do not want to store azure storage account secrets in flutter app.
EDIT. I found the solution to send raw byte data with Dio package. You can do that also with http package.
final dio = new Dio();
final fileBytes = file.readAsBytesSync();
var streamData = Stream.fromIterable(fileBytes.map((e) => [e]));
await dio.put(uploadDestinationUrl,
data: streamData,
options: Options(headers: {
Headers.contentLengthHeader: fileBytes.length,
"x-ms-blob-type": "BlockBlob",
"content-type": "image/jpeg"
}));

Upload image to Azure blob using Flutter (iOS/Android)

How can we upload images(.jpg, .png) to Azure blob storage. Not getting any insight to start with.
If you have used example would love to see and try if that is the way to do, any working example/url would help get started with
Here is i am trying to upload .jpg file and getting 400 error,
image.forEach((element) async {
ImageDetailsUpload result = ImageDetailsUpload.fromJson(element);
var postUri = Uri.parse('$url/${result.fileName}?$code'); //Azure blob url
var request = new http.MultipartRequest("PUT", postUri); //Put method
request.files.add(
new http.MultipartFile.fromBytes(
'file',
await File.fromUri(Uri.parse(result.path)).readAsBytes(),
),
);
request.send().then((response) {
print(response.statusCode);
}, onError: (err) {
print(err);
});
});
image is a LIST and holds the fileName and File path, this is what i get as bytes (see image below)
Solved the issue of upload but image is being corrupted now on server -
image.forEach((element) async {
ImageDetailsUpload result = ImageDetailsUpload.fromJson(element);
var postUri = Uri.parse('$url/${result.fileName}?$code');
var request = new http.MultipartRequest("PUT", postUri);
request.headers['X-MS-BLOB-TYPE'] = 'BlockBlob';
request.files.add(
new http.MultipartFile.fromBytes(
'file',
await File.fromUri(Uri.parse(result.path)).readAsBytes(),
),
);
request.send().then((response) {
print(response.statusCode);
}, onError: (err) {
print(err);
});
});
this seems to haunting me.
There is more to the question, what i noticed is the file uploaded using postman to blob storage are store as actual image of type image/jpeg depending on what type of image i am uploading. But when uploading using application i am using as mutipart which is making th e stored file into of type multipart/form-data. Uploaded both types of image jpg/png both gives type as mentioned above.
[![enter image description here][3]][3]
check here if you are using HTTP
How to upload images and file to a server in Flutter?
check this code if you are using Dio
FormData formData = FormData.from({
"name": "wendux",
"age": 25,
"file": await MultipartFile.fromFile("./text.txt",filename: "upload.txt")
});
response = await dio.post("/info", data: formData);
and check Dio's documentation
https://pub.dev/packages/dio
Here is a working example from one of my codes
Future<UploadImageResultModel> uploadImage(File image, String memberToken,
{String type = "IMAGE"}) async {
var uri = Uri.parse('${Constants.baseUrl}member/UploadImage/$type');
var request = new http.MultipartRequest("POST", uri);
request.headers.addAll({
"Authentication": memberToken,
});
var stream = new http.ByteStream(DelegatingStream.typed(image.openRead()));
var length = await image.length();
var multipartFile = new http.MultipartFile('file', stream, length,
filename: image.path.split("/").last);
request.files.add(multipartFile);
var streamedResponse = await request.send();
var response = await http.Response.fromStream(streamedResponse);
if (response.statusCode != 200)
return UploadImageResultModel.fromJson(json.decode("{}"));
return UploadImageResultModel.fromJson(json.decode(response.body));
}
First, convert the image File to Stream as data will be a push to the blob container in form of a stream.
Future uploadImage(File file) async {
String fileName = file.path.split('/').last;
_logger.info("File Path: " + fileName);
String imageToken = "Get your own token";
String containerName = "Blob container name";
final fileBytes = file.readAsBytesSync();
var streamData = Stream.fromIterable(fileBytes.map((e) => [e]));
String uploadDestinationUrl = RollaConstants.HOST_IMAGE +
"/$containerName" +
"/$fileName" +
imageToken;
final Dio _dio = Dio();
Response response;
try {
response = await _dio.put(uploadDestinationUrl,
data: streamData,
options: Options(headers: {
Headers.contentLengthHeader: fileBytes.length,
"x-ms-blob-type": "BlockBlob",
"content-type": "image/jpeg"
}));
} catch (error, stacktrace) {
print("Exception occured: $error.response stackTrace: $stacktrace");
}
print("Blob response: " + response.statusCode.toString());
}

How to upload multiple Images through Api

I am trying to Upload Multiple Images through Api but i am not understanding how to send a list, I can upload a single image though. Tried alot of searches but does'nt helped, i also import multi_image_picker i can pick the images but the problem is in uploading.
Future<Map<String, dynamic>> _uploadImage(File image) async {
String value = '';
SharedPreferences pref2 = await SharedPreferences.getInstance();
value = pref2.getString("user_role");
final mimeTypeData =
lookupMimeType(image.path, headerBytes: [0xFF, 0xD8]).split('/');
// Intilize the multipart request
final imageUploadRequest = http.MultipartRequest('POST', apiUrl);
// Attach the file in the request
final file = await http.MultipartFile.fromPath('photo', image.path,
contentType: MediaType(mimeTypeData[0], mimeTypeData[1]));
// Explicitly pass the extension of the image with request body
// Since image_picker has some bugs due which it mixes up
// image extension with file name like this filenamejpge
// Which creates some problem at the server side to manage
// or verify the file extension
imageUploadRequest.files.add(file);
imageUploadRequest.fields['mobile'] = _mobileNo.text;
imageUploadRequest.headers.addAll({
'Content-Type': 'application/json',
'Authorization': Constants.authToken,
});
var response = await imageUploadRequest.send();
if (response.statusCode == 200) print('Done!');
final respStr = await response.stream.bytesToString();
return json.decode(respStr);
}
this an example of uploading files to your API with HTTP package
import 'package:http/http.dart' as http;
void uploadFiles(List<File> files) async {
final url = YOUR-API-LINK;
for (var file in files) {
// Create a multipart request
var request = http.MultipartRequest('POST', Uri.parse(url));
// Add the file to the request
request.files.add(http.MultipartFile.fromBytes(
'file',
file.readAsBytesSync(),
filename: file.path.split('/').last,
));
// Send the request
var response = await request.send();
// Check the status code
if (response.statusCode != 200) {
print('Failed to upload file');
}else{
print response.body;
}
}
}
for Dio use this
void uploadFiles(List<File> files) async {
final url = YOUR-API-LINK;
// Create a Dio client
var dio = Dio();
// Create a FormData object
var formData = FormData();
// Add the files to the FormData object
for (var file in files) {
formData.files.add(MapEntry(
'file',
await MultipartFile.fromFile(file.path, filename: file.path.split('/').last),
));
}
// Send the request
var response = await dio.post(url, data: formData);
// Check the status code
if (response.statusCode != 200) {
print('Failed to upload files');
}else {
print(response.data)
}
}
as you can see there not much difference between them in http you use MultipartRequest in dio you use FormData.

how to upload image to rest API in flutter through http post method?

I'm trying to upload an image through the flutter via post method. and I'm using image_picker for pick file from mobile but I can't able to upload
and I have tried to send the file like FormData that also doesn't work
Future<dynamic> uploadLicence(int id ,dynamic obj) async {
FormData formdata = new FormData(); // just like JS
formdata.add("image",obj);
final response = await post('Logistic/driver/LicenceImage?
driverId=$id',
formdata);
print(response);
// return null;
if (response.statusCode == 200) {
final result = json.decode(response.body);
return result;
} else {
return null;
}
}
after that, I just tried with this method but this also not working
Future<dynamic> uploadLicence(int id, File file) async {
final url = Uri.parse('$BASE_URL/Logistic/driver/LicenceImage?
driverId=$id');
final fileName = path.basename(file.path);
final bytes = await compute(compress, file.readAsBytesSync());
var request = http.MultipartRequest('POST', url)
..files.add(new http.MultipartFile.fromBytes(
'image',bytes,filename: fileName,);
var response = await request.send();
var decoded = await
response.stream.bytesToString().then(json.decode);
if (response.statusCode == HttpStatus.OK) {
print("image uploded $decoded");
} else {
print("image uplod failed ");
}
}
List<int> compress(List<int> bytes) {
var image = img.decodeImage(bytes);
var resize = img.copyResize(image);
return img.encodePng(resize, level: 1);
}
It's possible with MultipartRequest. Or you can use simply dio package. It's one command.
With http:
import 'package:http/http.dart' as http;
final Uri uri = Uri.parse(url);
final http.MultipartRequest request = http.MultipartRequest("POST", uri);
// Additional key-values here
request.fields['sample'] = variable;
// Adding the file, field is the key for file and file is the value
request.files.add(http.MultipartFile.fromBytes(
field, await file.readAsBytes(), filename: filename);
// progress track of uploading process
final http.StreamedResponse response = await request.send();
print('statusCode => ${response.statusCode}');
// checking response data
Map<String, dynamic> data;
await for (String s in response.stream.transform(utf8.decoder)) {
data = jsonDecode(s);
print('data: $data');
}
I user this code for my project i hope work for you
Upload(File imageFile) async {
var stream = new http.ByteStream(DelegatingStream.typed(imageFile.openRead()));
var length = await imageFile.length();
var uri = Uri.parse(uploadURL);
var request = new http.MultipartRequest("POST", uri);
var multipartFile = new http.MultipartFile('file', stream, length,
filename: basename(imageFile.path));
//contentType: new MediaType('image', 'png'));
request.files.add(multipartFile);
var response = await request.send();
print(response.statusCode);
response.stream.transform(utf8.decoder).listen((value) {
print(value);
});
}

Unhandled Exception: Content size below specified contentLength. 206 bytes written but expected 375482. #228

I'm trying to upload multiple files with multi_image_picker with the code below but I'm receiving the error in the title. I'm following the code in the document but I'm not sure if I'm doing it the right way.
Thanks in advance
Future<void> sendDataToServer() async {
// string to uri
Uri uri = Uri.parse(Settings.SERVER_URL + 'api/uploadproduct');
// create multipart request
MultipartRequest request = http.MultipartRequest("POST", uri);
request.fields['name'] = productName;
List.generate(images.length, (index) async {
Asset asset = images[index];
ByteData byteData = await asset.getByteData();
List<int> imageData = byteData.buffer.asUint8List();
MultipartFile multipartFile = MultipartFile.fromBytes(
'photos',
imageData,
filename: 'some-file-name.jpg',
contentType: MediaType("image", "jpg"),
);
// add file to multipart
request.files.add(multipartFile);
});
// send
var response = await request.send();
}
UPDATE AND SOULUTION
Thanks to #Richard Heap answer I could find a solution I'm adding the solution here because his answer is not the complete solution:
Future<void> sendDataToServer() async {
// string to uri
Uri uri = Uri.parse(Settings.SERVER_URL + 'api/uploadproduct');
// create multipart request
MultipartRequest request = http.MultipartRequest("POST", uri);
request.fields['name'] = productName;
for (var asset in images) { {
Asset asset = images[index];
ByteData byteData = await asset.getByteData();
List<int> imageData = byteData.buffer.asUint8List();
MultipartFile multipartFile = MultipartFile.fromBytes(
'photos[]', //this [] little change was needed to make it work
imageData,
filename: 'some-file-name.jpg',
contentType: MediaType("image", "jpg"),
);
// add file to multipart
request.files.add(multipartFile);
};
// send
var response = await request.send();
}
Change List.generate to:
var i = 0;
for (var asset in images) {
i++;
var multipartFile = MultipartFile.fromBytes(
'photo$i',
(await asset.getByteData()).buffer.asUint8List(),
filename: 'photo$i.jpg', // use the real name if available, or omit
contentType: MediaType('image', 'jpg'),
);
request.files.add(multipartFile);
}
This will result in numbered params photo1, photo2, etc.
Your trick of appending the [] might only work with a PHP-based server that understands that suffix to mean an array of parameters called photos. The individually numbered approach is more generic.
I had same issue
Files.forEach((val) async{
var MyFile = await http.MultipartFile.fromPath('MediaFiles[]',val);
Multi.files.add(
MyFile,
);
});
i change just "forEach" to "for" loop and its work for me.
Working code
for(var val in Files){
var MyFile = await http.MultipartFile.fromPath('MediaFiles[]',val);
Multi.files.add(
MyFile,
);
}