Firebase _upload writes random downloadURL - firebase

Below is a simple firebase image uploader. The problem is that it sometimes uses another image's downloadURL as the value when it writes to Firestore. It uploads my image to cloud storage without a problem but then when it goes to write the location to firestore, it often uses the URL of another image. The full code is below but I have omitted the UI. How do I ensure that it writes the correct URL to firestore?
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
import 'dart:io';
import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_storage/firebase_storage.dart';
import 'package:path/path.dart' as path;
import 'package:image_picker/image_picker.dart';
class ImagePicky2 extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
// Remove the debug banner
debugShowCheckedModeBanner: false,
theme: ThemeData(primarySwatch: Colors.green),
home: HomePage(),
);
}
}
class HomePage extends StatefulWidget {
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
FirebaseStorage storage = FirebaseStorage.instance;
double? lat, lng;
File? file;
String? name, detail, pathImage, dateString;
// Select an image from the gallery or take a picture with the camera
// Then upload to Firebase Storage
Future<XFile?> _upload(String inputSource) async {
FirebaseAuth auth = FirebaseAuth.instance;
User firebaseUser = auth.currentUser!;
final picker = ImagePicker();
try {
final pickedImage = await picker.pickImage(
source: inputSource == 'camera'
? ImageSource.camera
: ImageSource.gallery,
imageQuality: 25,
maxWidth: 1920);
final String fileName = path.basename(pickedImage!.path);
File imageFile = File(pickedImage.path);
try {
// Uploading the selected image with some custom meta data
await storage.ref(fileName).putFile(
imageFile,
SettableMetadata(customMetadata: {
'uploaded_by': firebaseUser.displayName!,
'description': 'Some description...'
}));
// Refresh the UI
setState(() {});
} on FirebaseException catch (error) {
print(error);
}
} catch (err) {
print(err);
}
photoUploadFirestoreDetails();
}
// Retriew the uploaded images
// This function is called when the app launches for the first time or when an image is uploaded or deleted
Future<List<Map<String, dynamic>>> _loadImages() async {
FirebaseAuth auth = FirebaseAuth.instance;
User firebaseUser = auth.currentUser!;
List<Map<String, dynamic>> files = [];
final ListResult result = await storage.ref().list();
final List<Reference> allFiles = result.items;
await Future.forEach<Reference>(allFiles, (file) async {
final String fileUrl = await file.getDownloadURL();
pathImage = await file.getDownloadURL();
final FullMetadata fileMeta = await file.getMetadata();
files.add({
"url": fileUrl,
"path": file.fullPath,
"uploaded_by": fileMeta.customMetadata?['uploaded_by'] ?? firebaseUser.displayName,
"description":
fileMeta.customMetadata?['description'] ?? 'No description'
});
});
return files;
}
Future<Null> photoUploadFirestoreDetails() async {
Firebase.initializeApp();
Map<String, dynamic> map = Map();
map['PathImage'] = pathImage;
FirebaseFirestore firestore = FirebaseFirestore.instance;
CollectionReference collectionReference =
firestore.collection('MarkerCollect');
await collectionReference.doc().set(map).then((
value) {
});
}
}```

The code is uploading random download urls to Firestore because you're getting the image path from the _loadImages method which loads up the files on storage instead of using the download url of the just uploaded file.
This is the problematic code:
Future<Null> photoUploadFirestoreDetails() async {
...
map['PathImage'] = pathImage;
...
}
Solution:
You can fix this by retrieving the download url just after the upload and passing it to the photoUploadFirestoreDetails method to be used in the Firestore upload.
You should also put the photoUploadFirestoreDetails in the try-catch.
Checkout the updated code below:
// _upload method
Future<XFile?> _upload(String inputSource) async {
FirebaseAuth auth = FirebaseAuth.instance;
User firebaseUser = auth.currentUser!;
final picker = ImagePicker();
try {
final pickedImage = await picker.pickImage(
source: inputSource == 'camera'
? ImageSource.camera
: ImageSource.gallery,
imageQuality: 25,
maxWidth: 1920);
final String fileName = path.basename(pickedImage!.path);
File imageFile = File(pickedImage.path);
try {
// Uploading the selected image with some custom meta data
final Reference storageReference = storage.ref(fileName);
await storageReference.putFile(
imageFile,
SettableMetadata(customMetadata: {
'uploaded_by': firebaseUser.displayName!,
'description': 'Some description...'
}));
final String downloadUrl = await storageReference.getDownloadURL();
// Refresh the UI
setState(() {});
await photoUploadFirestoreDetails(downloadUrl: downloadUrl);
} on FirebaseException catch (error) {
print(error);
}
} catch (err) {
print(err);
}
}
// photoUploadFirestoreDetails method
Future<Null> photoUploadFirestoreDetails({#required String downloadUrl}) async {
Firebase.initializeApp();
Map<String, dynamic> map = Map();
map['PathImage'] = downloadUrl;
FirebaseFirestore firestore = FirebaseFirestore.instance;
CollectionReference collectionReference =
firestore.collection('MarkerCollect');
var value = await collectionReference.doc().set(map);
}

Try this function to upload image to fire-storage and get Url
Future<String?> uploadAndGetUrl(File file) async {
try {
final Reference ref = FirebaseStorage.instance
.ref()
.child('profilePhoto')
.child(DateTime.now().microsecondsSinceEpoch.toString());
UploadTask uploadTask = ref.putFile(file);
await uploadTask.whenComplete(() {});
String url = await ref.getDownloadURL();
return url;
} catch (e) {
print('Firebase Storage Error is : $e');
return null;
}
}
OR you can just upload an image and get the image URL later.
Your upload image function looks okay. the name should be unique. otherwise, it returns a different image url.
Future<String> getUrl(String imageName) async {
try {
Reference storageRef = FirebaseStorage.instance.ref().child('profilePhoto/$logo');
String url = await storageRef.getDownloadURL();
return url;
} catch (e) {
return null;
}
}

Related

Is there any way to save flutter_tts file to firebase storage?

I am working on a flutter project in which user is supposed to create some scripts and by typing them in text and then flutter_tts library is supposed to convert them to audio file which works fine for that time being but I want to save that file into firebase storage for later user. I have tried the following code but it just saves blank audio file in the firebase storage. Any kind of help will be appreciated.
The code I have tried is:
final FlutterTts _flutterTts = FlutterTts();
late var fileName;
/// creation of audio script
Future createAudioScript(
String name,
String script,
String firebasepath,
) async {
await _flutterTts.setLanguage("en-US");
await _flutterTts.setSpeechRate(1.0);
await _flutterTts.setVolume(1.0);
await _flutterTts.setPitch(1.0);
await _flutterTts.setVoice(
{"name": "en-us-x-tpf-local", "locale": "en-US"},
);
await _flutterTts.speak(script);
fileName = GetPlatform.isAndroid ? '$name.wav' : '$name.caf';
print('FileName: $fileName');
var directoryPath =
"${(await getApplicationDocumentsDirectory()).path}/audio/";
var directory = Directory(directoryPath);
if (!await directory.exists()) {
await directory.create();
print('[INFO] Created the directory');
}
var path =
"${(await getApplicationDocumentsDirectory()).path}/audio/$fileName";
print('[INFO] path: $path');
var file = File(path);
if (!await file.exists()) {
await file.create();
print('[INFO] Created the file');
}
await _flutterTts.synthesizeToFile(script, fileName).then((value) async {
if (value == 1) {
print('generated');
var file = File(
'/storage/emulated/0/Android/data/com.solution.thriving/files/$fileName',
);
print(file);
moveFile(file, path, '$firebasepath/$fileName').then((value) {
print('move file: $value');
_app.link.value = value;
print('link: ${_app.link.value}');
});
}
});
}
/// move file from temporary to local storage and save to firebase
Future<String> moveFile(
File sourceFile,
String newPath,
String firebasePath,
) async {
String audioLink = '';
print('moved');
await sourceFile.copy(newPath).then((value) async {
print('value: $value');
await appStorage.uploadAudio(value, fileName, firebasePath).then((audio) {
print(audio);
audioLink = audio;
return audioLink;
});
}).whenComplete(() async {
customToast(message: 'Audio has been generated successfully.');
});
return audioLink;
}
After spending whole day and with the help of a friend, I finally managed to figure out the issue which was being caused because I was using synthesizeToFile() and speak() functions at the same time, which I managed to resolved the issue by changing my code to the following code snippet.
final FlutterTts _flutterTts = FlutterTts();
late var fileName;
/// converting text to speech
Future createAudioScript(
String name,
String script,
String firebasepath,
) async {
await _flutterTts.setLanguage("en-US");
await _flutterTts.setSpeechRate(1.0);
await _flutterTts.setVolume(1.0);
await _flutterTts.setPitch(1.0);
await _flutterTts.setVoice(
{"name": "en-us-x-tpf-local", "locale": "en-US"},
);
if (GetPlatform.isIOS) _flutterTts.setSharedInstance(true);
// await _flutterTts.speak(script);
fileName = GetPlatform.isAndroid ? '$name.wav' : '$name.caf';
log('FileName: $fileName');
await _flutterTts.synthesizeToFile(script, fileName).then((value) async {
if (value == 1) {
log('Value $value');
log('generated');
}
});
final externalDirectory = await getExternalStorageDirectory();
var path = '${externalDirectory!.path}/$fileName';
log(path);
saveToFirebase(path, fileName, firebasPath: '$firebasepath/$name')
.then((value) => {log('Received Audio Link: $value')});
}
/// saving converted audio file to firebase
Future<String> saveToFirebase(String path, String name,
{required String firebasPath}) async {
final firebaseStorage = FirebaseStorage.instance;
SettableMetadata metadata = SettableMetadata(
contentType: 'audio/mpeg',
customMetadata: <String, String>{
'userid': _app.userid.value,
'name': _app.name.value,
'filename': name,
},
);
var snapshot = await firebaseStorage
.ref()
.child(firebasPath)
.putFile(File(path), metadata);
var downloadUrl = await snapshot.ref.getDownloadURL();
print(downloadUrl + " saved url");
return downloadUrl;
}

My Pdf document is not downloading or opening where as image file is opening after download in flutter

My Pdf document is not downloading or opening where as image file is opening after download in flutter.
I have use file picker to pick image files and pdf files and send them to firebase storage.
My image file is downloading and opening perfectly fine , but unable to download PDF file after sending it to firebase storage.
May be it is downloading, but unable to view when clicked on download button.
Future<void> downloadFile(StorageReference ref) async {
final String url = await ref.getDownloadURL();
final http.Response downloadData = await http.get(url);
final Directory systemTempDir = Directory.systemTemp;
final File tempFile = File('${systemTempDir.path}/tmp.jpg');
if (tempFile.existsSync()) {
await tempFile.delete();
}
await tempFile.create();
final StorageFileDownloadTask task = ref.writeToFile(tempFile);
final int byteCount = (await task.future).totalByteCount;
var bodyBytes = downloadData.bodyBytes;
final String name = await ref.getName();
final String path = await ref.getPath();
print(
'Success!\nDownloaded $name \nUrl: $url'
'\npath: $path \nBytes Count :: $byteCount',
);
_scaffoldKey.currentState.showSnackBar(
SnackBar(
backgroundColor: Colors.white,
content: Image.memory(
bodyBytes,
fit: BoxFit.fill,
),
),
);
}
}``
Please suggest me if any changes needed in the above code or any where
Flutter supports opening Images by default, to open pdf's you need to use pdf plugin (dependencies). https://codecarbon.com/top-pdf-widgets-for-flutter/ go through this link, I think 7. pdf_viewer_plugin suits you for this app.
Dependencies:
flutter_full_pdf_viewer: ^1.0.6 # MIT Licence
Create a new file display-pdf.dart and past the below code there.
import 'package:flutter/material.dart';
import 'package:flutter_full_pdf_viewer/full_pdf_viewer_scaffold.dart';
class DisplayPDF extends StatelessWidget {
final String pdfPath;
final String title;
DisplayPDF(this.pdfPath, this.title);
#override
Widget build(BuildContext context) {
return PDFViewerScaffold(
appBar: AppBar(
title: Text(title),
centerTitle: true,
),
path: pdfPath);
}
}
Create a file pdf-utilities.dart and past the below code there.
import 'dart:async';
import 'dart:typed_data';
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:path_provider/path_provider.dart';
class PdfUtilities {
final String pdfPath;
final BuildContext context;
PdfUtilities(this.pdfPath, this.context);
Future<String> prepareTestPdf() async {
final ByteData bytes = await DefaultAssetBundle.of(context).load(pdfPath);
final Uint8List list = bytes.buffer.asUint8List();
final tempDir = await getTemporaryDirectory();
final tempDocumentPath = '${tempDir.path}/$pdfPath';
final file = await File(tempDocumentPath).create(recursive: true);
file.writeAsBytesSync(list);
return tempDocumentPath;
}
}
Replace Future<void> downloadFile(StorageReference ref) async with this code:
Future<void> downloadFile(StorageReference ref) async {
final String url = await ref.getDownloadURL();
final http.Response downloadFile = await http.get(url);
final Directory systemTempDir = Directory.systemTemp;
final File tempFile = File('${systemTempDir.path}/tmp.pdf');
if (tempFile.existsSync()) {
await tempFile.delete();
}
await tempFile.create();
final StorageFileDownloadTask task = ref.writeToFile(tempFile);
final int byteCount = (await task.future).totalByteCount;
var bodyBytes = downloadFile.bodyBytes;
final String name = await ref.getName();
final String path = await ref.getPath();
print(
'Success!\nDownloaded $name \nUrl: $url'
'\npath: $path \nBytes Count :: $byteCount',
);
final String title = 'Displaying PDF';
PdfUtilities pdf = new PdfUtilities(path, context);
pdf.prepareTestPdf().then(writeCounter(await bodyBytes),
(path) {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => DisplayPDF(path, title),
),
);
},
);
}
Future<File> writeCounter(Uint8List stream) async {
final file = await _localFile;
// Write the file
return file.writeAsBytes(stream);
}

Flutter passing Firestore UID into repository

For my project, I am using firestore to get the data per user. Therefore I created the following structure in Firestore.
The data is stored in:
Collection: users > Document: UID (variable) > Collection: reminders
In order to get the data from Firestore I need to pass the UID into the repository. My reminder repository doesn't have this data standard therefor I get it via a call to Firebase.
Let me show the code to make it more clear:
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:diabetes/src/model/models.dart';
import 'package:diabetes/src/entities/entities.dart';
import 'package:diabetes/src/repositories/repositories.dart';
class FirebaseRemindersRepository implements RemindersRepository {
final reminderCollection = Firestore.instance.collection("users");
#override
Future<void> addNewReminder(Reminder reminder) async {
try {
final user = await FirebaseAuth.instance.currentUser();
final uid = user.uid;
return reminderCollection
.document(uid)
.collection("reminders")
.add(reminder.toEntity().toDocument());
} catch (error) {
print(error);
}
}
#override
Future<void> deleteReminder(Reminder reminder) async {
try {
final user = await FirebaseAuth.instance.currentUser();
final uid = user.uid;
return reminderCollection
.document(uid)
.collection("reminders")
.document(reminder.id)
.delete();
} catch (error) {
print(error);
}
}
#override
Stream<List<Reminder>> reminders() {
return reminderCollection.snapshots().map((snapshot) {
return snapshot.documents
.map((doc) => Reminder.fromEntity(ReminderEntity.fromSnapshot(doc)))
.toList();
});
}
#override
Future<void> updateReminder(Reminder update) async {
try {
final user = await FirebaseAuth.instance.currentUser();
final uid = user.uid;
return reminderCollection
.document(uid)
.collection("reminders")
.document(update.id)
.updateData(update.toEntity().toDocument());
} catch (error) {
print(error);
}
}
}
As you can see this makes me repeat this part a lot:
final user = await FirebaseAuth.instance.currentUser();
final uid = user.uid;
Also, I am not able to do the same here:
Stream<List<Reminder>> reminders() {

Firebase downloadUrl and Access token conflict

I am working on a simple app using Flutter to retrieve an image I manually uploaded to my firebase storage and display it on the screen. The upload is always successful, but I cannot get the image because the "dowloadURL" is showing "access token" instead.
Am i missing something somewhere? Someone please help me?
enter image description here
For uploading the file and directly get the image downloadUrl after, I've made some static functions which I call on my page like this:
StorageUploadTask imageUploadTask = await ImageProcessProvider.setImageToStorage(_destinationImage, generatedRandomId);
String imageDownloadUrl = await ImageProcessProvider.getImageDownloadUrlAfterUploading(imageUploadTask);
Static functions (including how I upload: 'setImageToStorage'):
import 'dart:async';
import 'dart:io';
import 'package:firebase_storage/firebase_storage.dart';
import 'package:image_picker_modern/image_picker_modern.dart';
class ImageProcessProvider {
static Future<String> getImageDownloadUrl(String imageId) async {
try {
final String downloadUrl = await FirebaseStorage.instance.ref().child(imageId + '.jpg').getDownloadURL();
return downloadUrl;
} catch (error) {
print('getImageDownloadUrl: $error');
throw error;
}
}
static Future<String> getImageDownloadUrlAfterUploading(StorageUploadTask uploadTask) async {
try {
final StorageTaskSnapshot url = (await uploadTask.onComplete);
final String downloadUrl = (await url.ref.getDownloadURL());
return downloadUrl;
} catch (error) {
print('getImageDownloadUrlAfterUploading: $error');
throw error;
}
}
static Future<StorageUploadTask> setImageToStorage(File image, String imageId) async {
try {
final String fileName = imageId + '.jpg';
final StorageReference storageRef = FirebaseStorage.instance.ref().child(fileName);
final StorageUploadTask uploadTask = storageRef.putFile(
File(image.path),
StorageMetadata(
contentType: 'image/jpeg',
),
);
return uploadTask;
} catch (error) {
print('setImageToStorage: $error');
throw error;
}
}
}
Hope this will help you.

How to store image in fire base and store url in firestore

i want to send coupon card to fire store that contain ( name - address - coupon ) and i want to make user set an specific image for every single card
that's my FireStoreService file
class FireStoreService {
FireStoreService._internal();
static final FireStoreService firestoreService = FireStoreService._internal();
Firestore db = Firestore.instance ;
factory FireStoreService() {
return firestoreService;
}
Stream<List<Coupon>> getCoupon() {
return db.collection('coupon').snapshots().map(
(snapshot) => snapshot.documents.map(
(doc) => Coupon.fromMap(doc.data, doc.documentID),
).toList(),
);
}
Future<void> addCoupon(Coupon coupon) {
return db.collection('coupon').add(coupon.toMap());
}
Future<void> deleteCoupon(String id) {
return db.collection('coupon').document(id).delete();
}
Future<void> updateCoupon(Coupon coupon) {
return db.collection('coupon').document(coupon.id).updateData(coupon.toMap());
}
}
and this is Coupon Model file
class Coupon {
final String id;
final String storeName;
final String storeLink;
final String couponCode;
Coupon(
{this.id, this.storeName, this.storeLink, this.couponCode});
Coupon.fromMap(Map<String, dynamic> data, String id)
: storeName = data["storeName"],
storeLink = data['storeLink'],
couponCode = data["couponCode"],
id = id;
Map<String, dynamic> toMap() {
return {
"storeName": storeName,
'storeLink': storeLink,
'couponCode': couponCode,
};
}
}
and this is Image Picker code and it's work fine and picked up the image
Future getImage() async {
try {
File image = await ImagePicker.pickImage(source: ImageSource.gallery);
setState(() {
image = image;
});
} catch (e) {
print(e);
}
}
any help ?
This is a function that asks for an imageFile. If you run your code (getImage function): pass the image variable to the uploadImage function.
String myUrl = await uploadImage(file);
Then you can use setData or updateData to put the url in the database.
Firestore.instance.collection('books').document()
.setData({ 'title': 'title', 'url': '$myUrl' })
final StorageReference storageRef = FirebaseStorage.instance.ref();
Future<String> uploadImage(imageFile) async {
StorageUploadTask uploadTask =
storageRef.child("myPath&Name.jpg").putFile(imageFile);
StorageTaskSnapshot storageSnap = await uploadTask.onComplete;
String downloadURL = await storageSnap.ref.getDownloadURL();
return downloadURL;
}

Resources