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

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

Related

Image is null after its picked by using image_picker package and saved on local memory (flutter)

Description:
I'm using (image_picker: ^0.8.5+3) package to pick the images from gallery and camera and then upload them on backend server (django by using django rest framework)
Issue:
Image is getting null outside picker function, even though state has been set by using setSatet(() {}) method
Failed Solutions:
I tried to save the image locally before uploading the image on the backend, But image still shown null once its used or referenced outside picker function
Note: backend API's works fine and i can upload images by using postman
picker function:
// Get image from gallery and store it locally
Future<File?> _getFromGallery() async {
PickedFile? image = await ImagePicker.platform.pickImage(source: ImageSource.gallery);
if (image == null) {
return null;
}
final File file = File(image.path);
final Directory directory = await getApplicationDocumentsDirectory();
final imagepath = directory.path;
final String fileName = path.basename(image.path);
File newImage = await file.copy('$imagepath/$fileName');
setState(() {
_imagelocal = newImage;
print('image.path1');
print(_imagelocal!.path);
});
}
Uploading image function:
_uploadimage() async {
Map<String,String> header = {
"Content-Type":"application/octet-stream)"
};
print("uploaded image invoked");
var request = http.MultipartRequest('POST', Uri.parse('http://192.168.1.15:8000/api/uploadimage'));
// request.files.add(await http.MultipartFile('image',http.ByteStream(image.openRead()).cast(),await image.length(),filename:image.name, contentType: MediaType('image', 'jpg')));
request.files.add(await http.MultipartFile('LoadModelImage',http.ByteStream(_imagelocal!.openRead()).cast(),await _imagelocal!.length(),));
request.headers.addAll(header);
//for completeing the request
var response =await request.send();
//for getting and decoding the response into json format
var responsed = await http.Response.fromStream(response);
final responseData = json.decode(responsed.body);
if (response.statusCode==201) {
print("SUCCESS");
print(responseData);
}
else {
print("ERROR");
}
}

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

Upload image flutter web with dio formdata

I am trying using file_picker and dio packages to upload files as form data.
This is for flutter web and it seems MultipartFile.fromFile is not accepted.
What I tried is the following:
if (result != null) {
for (var file in result.files) {
final formData = FormData.fromMap({
...someOtherData,
'file': File(file.name), // <------ I guess this is where the issue is, I also tried file instead of File(file.name)
});
dio.post(
url,
data: formData,
);
}
}
Ok, I found it, leaving here for someone having the same problem
if (result != null) {
for (var file in result.files) {
final formData = FormData.fromMap({
...someOtherData,
'file': MultipartFile.fromBytes(file.bytes as List<int>)
});
dio.post(
url,
data: formData,
);
}
}
If anyone is still wondering how to get it working on both mobile and web (This is using the image_picker's PickedFile as the image variable type) :
FormData body;
final bytes = await image.readAsBytes();
final MultipartFile file = MultipartFile.fromBytes(bytes, filename: "picture");
MapEntry<String, MultipartFile> imageEntry = MapEntry("image", file);
body.files.add(imageEntry);
**
The catch is that the filename is required on web and is automatically assigned on mobile.**

Flutter web - Uploading image to firebase storage

I am using the firebase_storage: ^8.0.6 package on flutter web. I want to upload image to firebase storage that I get using FilePicker package.
The problem is that the new package uses the putFile() method to upload files. But File from dart:io doesn't work on flutter web and it also doesn't accept the File object from dart:html.
I can upload image as Blob using the putBlob() method but then it doesn't upload it as image type but it's type is application/octet-stream. I don't want to upload the image file as a blob.
Future<String> uploadImage(PlatformFile file) async {
try {
TaskSnapshot upload = await FirebaseStorage.instance
.ref(
'events/${file.name}-${DateTime.now().toIso8601String()}.${file.extension}')
.putBlob(Blob(file.bytes));
String url = await upload.ref.getDownloadURL();
return url;
} catch (e) {
print('error in uploading image for : ${e.toString()}');
return ';
}
}
How to fix this issue?
You can use the putData() method to send the image and set it's metadata as a image.
Future<String> uploadImage(PlatformFile file) async {
try {
TaskSnapshot upload = await FirebaseStorage.instance
.ref(
'events/${file.path}-${DateTime.now().toIso8601String()}.${file.extension}')
.putData(
file.bytes,
SettableMetadata(contentType: 'image/${file.extension}'),
);
String url = await upload.ref.getDownloadURL();
return url;
} catch (e) {
print('error in uploading image for : ${e.toString()}');
return '';
}
}
putData() method takes Uint8List by default.
Uploading images using TaskSnapshot is not working on my flutter web project.
I used firebase_storage: ^8.1.3 .
Following code is working for my web project.
String nameImage = DateTime.now().millisecondsSinceEpoch.toString();
Reference _reference = FirebaseStorage.instance
.ref()
.child('images/$nameImage.png}');
await _reference
.putData(
await image.readAsBytes(),
SettableMetadata(contentType: 'image/jpeg'),
)
.whenComplete(() async {
await _reference.getDownloadURL().then((value) {
user.profilePictureURL = value;
FireStoreUtils.firestore
.collection(USERS)
.doc(user.userID)
.update({'profilePictureURL': user.profilePictureURL});
});
});
You can still use .putFile when you use the File.fromUri() constructor and get the Uri from the PlatformFile object using Uri.dataFromBytes and passing the bytes to it.
The code below contains changes that should remove the error:
TaskSnapshot upload = await FirebaseStorage.instance
.ref(
'events/${file.name}-${DateTime.now().toIso8601String()}.${file.extension}')
.putFile(File.fromUri(Uri.dataFromBytes(file.bytes.toList())));

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