How to check firebase storage directory already exist - flutter

How can I check if the Firebase Storage directory already exists in Flutter?

There is no way to check if a "folder" exists in Cloud Storage.
This might sound strange, but you have to consider that folders don't actually exist in a bucket-based storage. Cloud Storage doesn't actually have any folders.
The files in the storage just have path metadata associated with them, so that we humans can think hierarchically like we do with folders.
If you want to know if a file exists (not a "folder"), then in your code you could await getMetadata(); on a StorageReference that refers to the file you're looking for.
A workaround could be to create a dummy file such as "readme.md" inside each folder; that would certify its existence. If you can't find such file, your folder (probably) doesn't exist. This implies you carefully add such "dummy" file every time you add a folder.

firebase.database().ref("path/node/").on('value', (snapshot) => { console.log(snapshot.exists()); });

The answer from #venir is useful in understanding what's going on but you can overcome the problem by using this approach.
You can check if a folder exists by checking whether its parent folder contains a folder named after the one you are looking for. Something like this (excuse the TypeScript):
const beforeLast = (str: string, strLast: string) => {
return str.substr(0, str.lastIndexOf(strLast))
}
const afterLast = (str: string, strLast: string) => {
return str.substr(str.lastIndexOf(strLast) + 1)
}
private doesStorageFolderExist(storagePath: string): Observable<any> {
const parentPath: string = beforeLast(storagePath, '/')
const folderName: string = afterLast(storagePath, '/')
const ref: AngularFireStorageReference = this.storage.ref(parentPath)
const listAll: Observable<ListResult> = ref.listAll()
return listAll.pipe(
map((listResult: ListResult) => {
const storagePathExists: boolean = listResult.prefixes.some((folderRef) => folderRef.name === folderName)
return { storagePath, storagePathExists }
})
)
}
Obviously, this only works if there is a parent folder, but often this will be the case. You have to not like Firebase very much for making things so hard!

Related

Flutter dart export hive saved data to file to retrieve later

I am developing a barcode app and save the data to hive. What I need to know is there a way to export the saved hive database to a backup file and be able to retrieve it for instance if the app crashed or your phone is lost. This is for blind accessibility. Want to export the data to a file that I can save to my pc to store and if something happens I do not have to scan all the products again to build the database. If hive can not do this can someone point me in a direction of which flutter dart database can do this. Thank you
Ok the answer did not work for me. Here is a copy of my model file
import 'package:hive/hive.dart';
part 'product.g.dart';
#HiveType(typeId: 0)
class Product extends HiveObject{
#HiveField(0)
String itemName;
#HiveField(1)
String barCode;
#HiveField(2)
String bcType;
Product(this.itemName, this.barCode, this.bcType);
}
Then I call my box like
var box = Hive.box('products');
How to encode this to json for saving?
I use the next
Future<File> _createBackupFile() async {
/// This example uses the OS temp directory
File backupFile = File('${Directory.systemTemp.path}/backup_barcode.json');
try {
/// barcodeBox is the [Box] object from the Hive package, usually exposed inside a [ValueListenableBuilder] or via [Hive.box()]
var barcodeBox = Hive.box<Product>('products');
backupFile = await backupFile.writeAsString(jsonEncode(barcodeBox.values));
return backupFile;
} catch (e) {
// TODO: handle exception
print(e);
}
}
There is not a "out-of-the-box" solution for that as far as I know. It depends a lot on your use case of how you want to do that (since there are many ways). For a complete example of how I did that for my app, you can take a look here:
https://github.com/Kounex/obs_blade/blob/master/lib/views/settings/logs/log_detail/log_detail.dart (I made use of the share package in order to easily export it - but that's not necessary)
Flutter also has its own documentation on reading and writing files (https://flutter.dev/docs/cookbook/persistence/reading-writing-files) - I will add some information to round it up:
Storage location
First of all we have to think about where to store the "backup file". Flutter exposes common paths on its own which you can make use of (additionally the path_provider package gives you more flexibility). If you want this backup file to be temporarily, you can for example use:
Directory.systemTemp;
The documentation states: "This is the directory provided by the operating system for creating temporary files and directories in." The OS will make sure to delete them in different occasions so you don't have to worry about it. You can also create additional directories inside this temp directory to make it more distinguishable, like:
Directory.systemTemp.createTemp('my_app');
IMPORTANT: this applies to non-sensitive data. If whatever you are processing contains sensitive data (like names, addresses etc.), you have to ensure data security / data privacy. In such cases I would make use of the path_provider package as mentioned earlier and create those files in the documents directory (getApplicationDocumentsDirectory()) and make sure they are deleted immediately after usage / export. Even encrypting the content may be a good idea - but I'm not diving into this here.
File mangagement
Once we know where to store the file, we just need to create them. Chapter 3 and 4 of the flutter documentation earlier exactly states how to do that, so I'm rather focusing on what to write.
A common and very convenient way to compose your data is JSON. Flutter also has documentation for that: https://flutter.dev/docs/development/data-and-backend/json
Since you are using Hive, you probably already have classes representing entries in your boxes and you could easily just add the toJson() function where you return a Map<String, dynamic> (as seen in the documentation) and you can use that to finally write the needed information into a file.
Based on your Hive class, this is how to adjust it in otder to serialize it correctly:
import 'package:hive/hive.dart';
part 'product.g.dart';
#HiveType(typeId: 0)
class Product extends HiveObject{
#HiveField(0)
String itemName;
#HiveField(1)
String barCode;
#HiveField(2)
String bcType;
Product(this.itemName, this.barCode, this.bcType);
/// This function will automatically be used by the [jsonEncode()] function internally
Map<String, dynamic> toJson() => {
'itemName': this.itemName,
'barCode': this.barCode,
'bcType': this.bcType,
}
}
A small example implementation could look like this:
Future<File?> _createBackupFile() async {
/// This example uses the OS temp directory
File backupFile = File('${Directory.systemTemp.path}/backup_barcode.json');
try {
/// barcodeBox is the [Box] object from the Hive package, usually exposed inside a [ValueListenableBuilder] or via [Hive.box()]
backupFile = await backupFile.writeAsString(jsonEncode(barcodeBox.values));
return backupFile;
} catch (e) {
// TODO: handle exception
}
}
This will save the JSON representation of your Hive box inside the temporary OS directory. You can swap the directory with whatever suits you best (on Android for example on the external storage for easier accessibility).
Now you have to think about how and when to trigger this. You can do this manually by triggering a button press for example or automatically after a certain action (like adding a new barcode) and choose a way that works for you to access the file. As stated earlier, saving the file somewhere easily accessible like the external storage on Android or making use of the share package are possible solutions.
Android Manifest should contain these:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<application
android:requestLegacyExternalStorage="true"
You will need this package and this package to proceed.
Now a method to backup the data to a desired location:
Future<void> createBackup() async {
if (Hive.box<Product>('products').isEmpty) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('No Products Stored.')),
);
return;
}
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Creating backup...')),
);
Map<String, dynamic> map = Hive.box<Product>('products')
.toMap()
.map((key, value) => MapEntry(key.toString(), value));
String json = jsonEncode(map);
await Permission.storage.request();
Directory dir = await _getDirectory();
String formattedDate = DateTime.now()
.toString()
.replaceAll('.', '-')
.replaceAll(' ', '-')
.replaceAll(':', '-');
String path = '${dir.path}$formattedDate.json';//Change .json to your desired file format(like .barbackup or .hive).
File backupFile = File(path);
await backupFile.writeAsString(json);
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Backup saved in folder Barcodes')),
);}
Future<Directory> _getDirectory() async {
const String pathExt = 'Barcodes/';//This is the name of the folder where the backup is stored
Directory newDirectory = Directory('/storage/emulated/0/' + pathExt);//Change this to any desired location where the folder will be created
if (await newDirectory.exists() == false) {
return newDirectory.create(recursive: true);
}
return newDirectory;
}
Finally, call this function using a button and it will save a backup with the current time as the name in JSON format.
createBackup()
After this to restore the data back to Hive,
Future<void> restoreBackup() async {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Restoring backup...')),
);
FilePickerResult? file = await FilePicker.platform.pickFiles(
type: FileType.any,
);
if (file != null) {
File files = File(file.files.single.path.toString());
Hive.box<Product>('products').clear();
Map<String, dynamic> map = jsonDecode(await files.readAsString());
for (var i = 0; i < map.length; i++) {
Product product = Product.fromJson(i.toString(), map);
Hive.box<Product>('products').add(product);
}
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Restored Successfully...')),
);
}
}
Finally, call this function using a button and it will open the file picker where you can select the backup file and it will remove the existing data and add every item from the backup in a loop.
restoreBackup()

Firebase Storage upload permissions issue using Admin SDK [duplicate]

I've started working with firebase storage and firebase functions recently. Right now I've been developing file upload from functions to storage .
I've got it working (upload is done and file appears on the storage section), yet, the image, stays like this forever (loading forever on the right side):
I though that it was an error from my code. Yet, if I open Google Cloud Platform - Storage, the image appears and I can open it and preview it.
In firebase storage, if I open the image (select on it and click open), it returns the following url: https://console.firebase.google.com/u/0/undefined
What may I been doing wrong? Here's the code I'm using:
function uploadImage() {
const newImageData = "data:image/png;base64,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"
var mimeTypes = require('mimetypes');
var image = newImageData,
mimeType = image.match(/data:([a-zA-Z0-9]+\/[a-zA-Z0-9-.+]+).*,.*/)![1],
fileName = 'test.' + mimeTypes.detectExtension(mimeType),
base64EncodedImageString = image.replace(/^data:image\/\w+;base64,/, ''),
imageBuffer = new Buffer(base64EncodedImageString, 'base64');
// Instantiate the GCP Storage instance
const { Storage } = require('#google-cloud/storage');
const googleCloudStorage = new Storage(firebaseSettings);
const bucket = googleCloudStorage.bucket('projectID.appspot.com');
var file = bucket.file(fileName);
return file.save(imageBuffer, {
metadata: { contentType: mimeType, cacheControl: "public, max-age=300" },
public: true,
validation: 'md5'
}, function (error: any) {
if (error) {
throw 'error';
}
return "https://storage.googleapis.com/share-expanses-dcc9f.appspot.com/" + fileName;
});
}
Thanks for the help
Haven't been able to test the solution given by Firebase, but here's the transcript of the response:
The problem that you are facing could be because of two reasons. The
first one is how you are uploading the files, via the Firebase
Console, using any Admin SDK, or via the gsutil command. If using the
Admin SDK option, the problem is a known issue where the required
metadata doesn’t exist, fortunately there is a workaround, you can try
this script to solve this issue.
Now, the second one is related to the network if you are using
comcast, please, try on a different network to see if this issue is
related to that.
When you save an image to firebase, you need to provide an access token in metadata : firebaseStorageDownloadTokens. It has to be an uuid.
More info can be found here : https://www.sentinelstand.com/article/guide-to-firebase-storage-download-urls-tokens
const { v4: uuid } = require("uuid")
function uploadImage() {
const newImageData = "data:image/png;base64,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"
var mimeTypes = require('mimetypes');
var image = newImageData,
mimeType = image.match(/data:([a-zA-Z0-9]+\/[a-zA-Z0-9-.+]+).*,.*/)![1],
fileName = 'test.' + mimeTypes.detectExtension(mimeType),
base64EncodedImageString = image.replace(/^data:image\/\w+;base64,/, ''),
imageBuffer = new Buffer(base64EncodedImageString, 'base64');
// Instantiate the GCP Storage instance
const { Storage } = require('#google-cloud/storage');
const googleCloudStorage = new Storage(firebaseSettings);
const bucket = googleCloudStorage.bucket('projectID.appspot.com');
var file = bucket.file(fileName);
return file.save(imageBuffer, {
metadata: {
contentType: mimeType,
cacheControl: "public,
max-age=300",
// THIS IS THE LINE YOU NEED TO ADD
firebaseStorageDownloadTokens: uuid(),
},
public: true,
validation: 'md5'
}, function (error: any) {
if (error) {
throw 'error';
}
return "https://storage.googleapis.com/share-expanses-dcc9f.appspot.com/" + fileName;
});
}
After that you'll need to click on "Create access token"
#jean-smaug answer is almost complete. Based on the page he linked (https://www.sentinelstand.com/article/guide-to-firebase-storage-download-urls-tokens), the only missing thing is to wrap the firebaseStorageDownloadTokens property inside a metadata object. I've just tested it and it's working fine 👌 No need to create access token afterwards.
In my case I added metadata while uploading and it loading as it showed in image but when I'm refresh page after 3 min I found that it upload correctly , so as Cafn explain if it not matter of metadata you should wait until it loaded
$uploadedObject=$bucket->upload($imageFile, [
'name' => 'Image_Name',
"metadata" => [ "contentType"=> 'image/png'],
]);

How to upload to a specific folder in the Google Drive GoogleAPIs v3 in Dart?

I'm want to upload images to a specific folder.
This is the function i'm using right now to upload my files, it uploads them the to main folder:
import 'package:googleapis/drive/v3.dart' as driveV3;
Future upload(File file) async {
var client = await getHttpClient();
var drive = driveV3.DriveApi(client);
var response = await drive.files.create(
driveV3.File()..name = p.basename(file.absolute.path),
uploadMedia: driveV3.Media(file.openRead(), file.lengthSync())
);
}
What i want is a way to upload ALWAYS to a folder that can be created at the first time or something like that. what do i need to modify in the above function so i can specify a folder name EX: MyFolder and always uploads to it?
You are looking for the parents of the File class
This property takes a list of String. These strings are the folders you want to upload the file to. As it seems, a file can be uploaded in multiple folders.
So your code should look something like this:
Future upload(File file) async {
var client = await getHttpClient();
var drive = driveV3.DriveApi(client);
file.parents = ["folder_id"];
var response = await drive.files.create(
driveV3.File()..name = p.basename(file.absolute.path),
uploadMedia: driveV3.Media(file.openRead(), file.lengthSync())
);
}
On the documentation I quote:
The IDs of the parent folders which contain the file. If not specified
as part of a create request, the file will be placed directly in the
user's My Drive folder. If not specified as part of a copy request,
the file will inherit any discoverable parents of the source file.
Update requests must use the addParents and removeParents parameters
to modify the parents list.

how to stream data from cloud functions to cloud storage

I'm trying to create a cloud function which saves some data (documents from firestore) to cloud storage.
Wrote some cloud functions before, but kind of new to cloud storage, buckets etc.
From what I've read I have to "stream" this data to a bucket.
I'd love to see a short snippet that does just that :)
For you to achieve that, it should not be something very complicated, so I hope I can help you.
To perform this, I will follow the example explained in the article Backup Firestore data to storage bucket on a schedule in GCP - which you can follow completely, in case you are interested - focusings in the upload from Firestore to Cloud Storage. I will explain which parts to use and how to use them, to achieve your agoal
Once you created your Cloud Storage bucket - it should have Multi-regional and Nearline configured in the settings - you need to use the below code as indicated after them.
index.js file:
const firestore = require('#google-cloud/firestore');
const client = new firestore.v1.FirestoreAdminClient();
// Replace BUCKET_NAME
const bucket = 'gs://<bucket-id>'
exports.scheduledFirestoreBackup = (event, context) => {
const databaseName = client.databasePath(
process.env.GCLOUD_PROJECT,
'(default)'
);
return client
.exportDocuments({
name: databaseName,
outputUriPrefix: bucket,
// Leave collectionIds empty to export all collections
// or define a list of collection IDs:
// collectionIds: ['users', 'posts']
collectionIds: [],
})
.then(responses => {
const response = responses[0];
console.log(`Operation Name: ${response['name']}`);
return response;
})
.catch(err => {
console.error(err);
});
};
package.json file:
{
"dependencies": {
"#google-cloud/firestore": "^1.3.0"
}
}
These files should be created, with the configuration in the Cloud Function as following: an unique name; Cloud Sub/Pub as trigger; topic name similar or equal to initiateFirestoreBackup; using Node.js, source will be the above files written and function to execute scheduledFirestoreBackup.
The above codes should be enough for you to export from your Firestore to your Cloud Storage, due to the fact that it will be getting all your collections - or you can define specifics - and sending to the bucket you already created.
Besides that, in case you want more information on uploading files to Cloud Storage using Cloud Functions, you can check here as well: Uploading files from Firebase Cloud Functions to Cloud Storage
Let me know if the information helped you!
Thanks to #gso_gabriel I was able to create a partial backup for my documents.
To anyone who's interested, here's a simplified version of my code:
const bucketName = 'myproject.appspot.com';
exports.backup = functions.https.onCall(async (data, context) => {
const userDoc = await admin.firestore().collection('users').doc('abc123').get();
await writeFile('temp', 'user.json', JSON.stringify(userDoc.data()));
}
async function writeFile(dirName, fileName, content) {
var bucket = admin.storage().bucket(bucketName);
const destFilename = dirName + '/' + fileName;
const file = bucket.file(destFilename);
const options = {
destination: destFilename,
metadata: { contentType: "application/json" }
};
await bucket.file(destFilename).save(content, options);
}

PlayFramework 2.3.x: Access public folder using URL with Play and Scala

I am uploading a videos and images using web-service and save the images in our application. When i save the files, the files are save on root of application folder. I want to access those images and videos with localhost url, like: I upload the file and save under app-root/upload/image.jpg. In my route mapping file, i declare routing as below:
GET /uploads/ staticDir:/upload
As define in Play Documentation. But still getting an compile time error: Controller method call expected. I want to access image like this http://localhost:9999/uploads/image.jpg
Well... One way of doing this is by adding following routes,
GET /uploads/*file controllers.Assets.at(path="/uploads", file)
But, it will interfere with the reverse-routing of already existing route which is,
GET /assets/*file controllers.Assets.at(path="/public", file)
And then you will have to use your these two assets routes as - #route.Assets.at("public", filename) and #route.Assets.at("uploads", filename) which means all your templates which use you public assets route as - #route.Assets.at(filename) will have to be changed. Which can be a hassle in an existing big project.
You can avoid this by using following method,
Create another controller as,
package controllers
object FileServer extends Controller {
def serveUploadedFiles1 = controllers.Assets.at( dicrectoryPath, file, false )
// Or... following is same as above
def serveUploadedFiles2( file: String ) = Action.async {
implicit request => {
val dicrectoryPath = "/uploads"
controllers.Assets.at( dicrectoryPath, file, false ).apply( request )
}
}
}
The above should have worked... but seems like play does a lot of meta-data checking on the requested "Assets" which somehow results in empty results for all /uploads/filename requests. I tried to look into the play-source code to check, but it seems like it may take sometime to figure it out.
So I think we can make do with following simpler method ( It can be refined further in so many ways.).
object FileServer extends Controller {
import play.api.http.ContentTypes
import play.api.libs.MimeTypes
import play.api.libs.iteratee.Enumerator
import play.api.libs.concurrent.Execution.Implicits.defaultContext
def serveUploadedFiles(file: String) = Action { implicit request =>
val fileResUri = "uploads/"+file
val mimeType: String = MimeTypes.forFileName( fileResUri ).fold(ContentTypes.BINARY)(addCharsetIfNeeded)
val serveFile = new java.io.File(fileResUri)
if( serveFile.exists() ){
val fileContent: Enumerator[Array[Byte]] = Enumerator.fromFile( serveFile )
//Ok.sendFile(serveFile).as( mimeType )
val response = Result(
ResponseHeader(
OK,
Map(
CONTENT_LENGTH -> serveFile.length.toString,
CONTENT_TYPE -> mimeType
)
),
fileContent
)
response
}
else {
NotFound
}
}
def addCharsetIfNeeded(mimeType: String): String =
if (MimeTypes.isText(mimeType)) s"$mimeType; charset=$defaultCharSet" else mimeType
lazy val defaultCharSet = config(_.getString("default.charset")).getOrElse("utf-8")
def config[T](lookup: Configuration => Option[T]): Option[T] = for {
app <- Play.maybeApplication
value <- lookup(app.configuration)
} yield value
}
But this method will cause some troubles in case of packaged-build deployments.
Which means, using the Play's Asset thing would be wiser choice. So looking again, the controllers.Assets.at which is actually controllers.Assets.assetAt uses this method at one place,
def resource(name: String): Option[URL] = for {
app <- Play.maybeApplication
resource <- app.resource(name)
} yield resource
Which means, it tries to locate the resource in the directories which are part of application's classpath and our uploads folder sure is not one of them. So... we can make play's Assets.at thingy work by adding uploads to classpath.
But... thinking again... If I recall all folders in the classpath are supposed to be packaged in the package to be deployed in-case of packaged-build deployments. And uploaded things will be created by the users, which means they should not be a part of package. Which again means... we should not be trying to access our uploaded things using Play's Assets.at thingy.
So... I think we are better off using our own simpler rudimentary implementation of serveUploadedFiles.
Now add a route in route file as,
GET /uploads/*file controllers.FileServer.serveUploadedFiles( file:String )
Also... Keep in mind that you should not be thinking of using play to serve your uploaded assets. Please use nginx or something similar.