is it possible to decrease the length of base64 string of type image in flutter? - flutter

I am sending an image as base64 string to API through post function and getting errors like;
The requested URL is over the maximum size allowed
statusCode: 414
414 Request-URI Too Large
The function I am using
getAndUploadProfilePicture() async {
final _picker = ImagePicker();
final XFile? pickedImage = await _picker.pickImage(
source: ImageSource.gallery,
imageQuality: 20,
);
if (pickedImage != null) {
file = File(pickedImage.path);
final bytes = File(pickedImage.path).readAsBytesSync();
String img64 = base64Encode(bytes);
try {
Dio dio = ApiServices().launch();
final response = await dio.post(EndPoints.uploadUserProfilePicture,
queryParameters: {"id": '24', "picture": img64, "type": 'guide'});
if (response.statusCode == 200) {
print('picture upload successfullyy');
print(response.statusMessage);
}
} catch (e, s) {
// ignore: avoid_print
print("DatabaseService getAndUploadProfilePicture() Exception: $e");
// ignore: avoid_print
print(s);
}
} else {}
}
The API call work fine when i pass a small dummy string instead of base64 image

You cannot decrease the length of a base64-encoded string for an image without first resizing or recompressing the image to be smaller or without corrupting the image by truncating its data. However, that's the wrong question to ask to fix your problem.
If you're making a request via an http POST operation, you shouldn't be passing the image data through queryParameters (which are encoded into the URL itself, hence the error about the URI being too long). You should be supplying it through dio.post's data argument instead. package:dio's README.md shows an example for using dio.post:
Performing a POST request:
response = await dio.post('/test', data: {'id': 12, 'name': 'wendu'});

Related

Send picture to server api with post request [FLUTTER]

I want to take picture, which should contain just a few letters, with my phone and then send it to a server where it will convert the picture to a text string.
My imported packages:
import 'dart:io';
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'package:image_picker/image_picker.dart';
I currently have this camera function:
// Camera implementation
File? _image;
final ImagePicker _picker = ImagePicker();
Future getImage() async {
final image = await _picker.pickImage(source: ImageSource.camera);
setState(() {
_image = File(image!.path);
});
}
And I use it in this button:
// Camera button
ElevatedButton.icon(
onPressed: getImage,
icon: const Icon(Icons.camera_alt_rounded),
label: const Text('Scan'),
style: ButtonStyle(
backgroundColor: MaterialStateProperty.all(Colors.green[500]),
textStyle: MaterialStateProperty.all(const TextStyle(fontSize: 26)),
)
)
I have tested to just send some data to jsonplaceholder and it works, but I can't get understand how to implement it to a picture that should be sent to my server.
// Send Data to the Server (TEST VERSION)
postDataTest() async{
try{
var response = await http.post(Uri.parse("https://jsonplaceholder.typicode.com/posts"),
body: {
"id": 1.toString(),
"name": "Hax",
}
);
print(response.body);
} catch(e){
print(e);
}
}
TLDR. I want to take a picture and send it to a server.
Use multipart
Upload(File img) async {
var uri = Uri.parse(uploadURL);
var request = new http.MultipartRequest("POST", uri);
request.files.add( new http.MultipartFile.fromBytes("file", img.readAsBytesSync(), filename: "Photo.jpg", contentType: new MediaType("image", "jpg")));
var response = await request.send();
print(response.statusCode);
response.stream.transform(utf8.decoder).listen((value) {
print(value);
});
}
Picture to text for archive this you need to convert image into base64. Check this link
However, it's generally a bad idea to store large blobs of binary data in your database. you will end up wasting bandwidth transmitting data.it also decrease mobile app performance while you read large blob data. you don't need as well as unnecessary encoding and decoding.
You can send picture to server using multipart api request.
So you can archive mutipart request with api in various packages
https - dart documentation
Dio
You can also check multipartRequest on Stackoverflow.
I managed to solve it with this function:
// Upload camera photo to server
Future uploadImage() async {
final uri = Uri.parse("url to the server");
var request = http.MultipartRequest('POST', uri);
var takenPicture = await http.MultipartFile.fromPath("image", _image!.path);
request.files.add(takenPicture);
var response = await request.send();
if(response.statusCode == 200){
print('Image uploaded!');
} else{
print('Image not uploaded');
}
}
file.path is path of image u can use file picker or image picker in flutter
baseimage = "";
if(file.path != '') {
List<int> imageBytes = file.readAsBytesSync();
baseimage = base64Encode(imageBytes);
}
send image as a string and decode base64 in your server
laravel exemple using spatie media
if (isset($request->image)) {
$fiche_client->addMediaFromBase64($request->image)
->usingFileName(Str::random(10).'.png')
->toMediaCollection('magasin');
}

Sending base64 image from flutter

I'm facing a strange issue lately. I have to send a base64 encoded image from flutter to a remote API. The problem is that I convert the image using below code:
Future getProfileImage() async {
final _pickedFile =
await _imagePicker.getImage(source: ImageSource.gallery);
_profileImage = await File(_pickedFile!.path);
print("${_pickedFile.toString()}");
//print("${_pickedFile.path}");
List<int> _profileImageBytes = await _profileImage!.readAsBytesSync();
_profileImageBase64 = await base64Encode(_profileImageBytes);
print("$_profileImageBase64");
}
But when I try to send using following code:
Future updateProfile() async {
print("$_profileImageBase64");
String pre = "data:image/jpg;base64,";
String imageString = '';
print("$imageString");
if (_profileImageBase64 == null) {
imageString = '';
} else {
imageString = "$pre$_profileImageBase64";
}
String apiUrl = "http://162.0.236.163:3000/api/users/profile-update";
Map<String, String> header = {
'Content-Type': 'application/x-www-form-urlencoded',
'Authorization': "Bearer ${widget.user.token}"
};
print(
"Bearer ${widget.user.token}, ${_firstName!.controller!.text}, ${_lastName!.controller!.text}");
//print("$imageString");
//log(imageString);
Map<String, String> body = {
'firstName': _firstName!.controller!.text,
'lastName': _lastName!.controller!.text,
'image': imageString
};
print("${body["image"]}");
http.Response reponse =
await http.post(Uri.parse(apiUrl), body: body, headers: header);
//print("${jsonDecode(reponse.body)}");
var data = jsonDecode(reponse.body) as Map;
print("$data");
if (data["status"] == 1) {
widget.user.first = _firstName!.controller!.text;
widget.user.last = _lastName!.controller!.text;
SharedPreferences prefs = await SharedPreferences.getInstance();
prefs.setString('firstName', widget.user.first.toString());
prefs.setString('lastName', widget.user.last.toString());
}
setState(() {});
}
it fails. Strange thing is that when I print the string at the start of above method, it shows different value, but when I print body["image"] it is slightly different string. Moreover, what is surprising is that when I copy any of these Strings from console, and hardcode them for image String, the code is successful. I can't figure out why I cannot successfully send the image using a String variable, which effectively has same code. Can anyone help please?
Edit: I have just realized that the string may not be printed in console completely. When I check the length of the String it is almost 314000 characters for base64 (220kb file). But in console few thousands appear. The one from console can be successfully
sent, but full string fails. Can this be due to limitation on server end
Well, if there isn't a problem with authorization, and shorter strings are successfully saved, then it sounds like some form of data validation is performed on the server-side (API) that is limiting either that field or the entire message body.
If the data is being stored in a database, make sure the image field is large enough to hold that many characters.
One suggestion to try is to reduce the size of the image before you convert it to base64. There are a few optional arguments on the getImage method in the 'image_picker' package that allows you to specify a maxHeight (width would be proportional) and image quality.
import 'dart:async';
import 'dart:io' as Io;
import 'dart:convert';
import 'package:image_picker/image_picker.dart';
final pickedFile = await picker.getImage(
source: ImageSource.gallery,
maxHeight: 150,
imageQuality: 90,
);
final bytes = await pickedFile.readAsBytes();
final b64img = base64.encode(bytes);

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"
}));

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

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;

How to get a Uint8List from a Network image by url in Flutter?

I have the network url of image and I need to get Uint8List. How can I convert it?
I check answers in like question, but those ways don't work.
How to get a Flutter Uint8List from a Network Image?
Try this:
Uint8List bytes = (await NetworkAssetBundle(Uri.parse(url)).load(url))
.buffer
.asUint8List();
Uint8List yourVar;
final DecoderCallback callback = (Uint8List bytes, {int cacheWidth, int cacheHeight}) {
yourVar = bytes.buffer.asUint8List();
return instantiateImageCodec(bytes, targetWidth: cacheWidth, targetHeight: cacheHeight);
};
ImageProvider provider = NetworkImage(yourImageUrl);
provider.obtainKey(createLocalImageConfiguration(context)).then((key) {
provider.load(key, callback);
});
this did the trick for me:
import 'dart:typed_data';
import 'package:flutter/services.dart';
//Get the image from the URL and then convert it to Uint8List
Uint8List bytes = (await NetworkAssetBundle(Uri.parse('https://some_image_url.png'))
.load('https://some_image_url.png'))
.buffer
.asUint8List();
This works on me (using flutter web) with a library file_saver.
Uri uri = Uri.parse(url);
Uint8List bytes = await readBytes(uri);
await FileSaver.instance.saveFile(filename, bytes, 'jpg',
mimeType: MimeType.JPEG); // specify your vars
I'm having the same problem in Flutter web, I had to use the extended_image library, and I found inside your example that has a method that allows you to convert an ImageProvider to Bytes.
https://github.com/fluttercandies/extended_image/blob/master/example/lib/pages/simple/image_editor_demo.dart.
/// it may be failed, due to Cross-domain
Future<Uint8List> _loadNetwork(ExtendedNetworkImageProvider key) async {
try {
final Response response = await HttpClientHelper.get(Uri.parse(key.url),
headers: key.headers,
timeLimit: key.timeLimit,
timeRetry: key.timeRetry,
retries: key.retries,
cancelToken: key.cancelToken);
return response.bodyBytes;
} on OperationCanceledError catch (_) {
print('User cancel request ${key.url}.');
return Future<Uint8List>.error(
StateError('User cancel request ${key.url}.'));
} catch (e) {
return Future<Uint8List>.error(StateError('failed load ${key.url}. \n $e'));
}
}