Deleting a folder from Firebase Cloud Storage in Flutter - firebase

I have a Flutter mobile app in which I am trying to delete a folder (and its contents) from Firebase Cloud Storage. My method is as follows:
deleteFromFirebaseStorage() async {
return await FirebaseStorage.instance.ref().child('Parent folder/Child folder').delete();
}
I expect Child folder and its contents to be deleted, but this exception is thrown:
Unhandled Exception: PlatformException(Error -13010, FIRStorageErrorDomain, Object Parent folder/Child folder does not exist.)
However I can clearly see that folder exists in Cloud Storage. How do I delete this folder?

MD. Saffan Alvy was right, but to completely delete all and not just one file
do this.
if you didn't know.
await await FirebaseStorage.instance.ref("users/${FirebaseAuth.instance.currentUser!.uid}/media").listAll().then((value) {
value.items.forEach((element) {
FirebaseStorage.instance.ref(element.fullPath).delete();
);
});

Cloud Storage doesn't actually have any folders. There are just paths that look like folders, to help you think about how your structure your data. Each object can just have a common prefix that describes its "virtual location" in the bucket.
There's no operations exposed by the Firebase SDKs that make it easy to delete all objects in one of these common prefixes. Your only real option is to list all files in a common prefix, iterate the results, and delete each object individually.
Unfortunately, the list files API has not made it to flutter yet, as discussed here. So you are kind of out of luck as far as an easy solution is concerned. See also: FirebaseStorage: How to Delete Directory
Your primary viable options are:
Keep track of each object in a database, then query the database to get the list of objects to delete.
Delete the objects on a backend using one of the server SDKs. For example: Delete folder in Google Cloud Storage using nodejs gcloud api

I built a script that deletes recursively all files inside a folder and all subfolders, here is:
import 'package:firebase_storage/firebase_storage.dart';
class FirebaseStorageApi {
static Future<void> deleteFolder({
required String path
}) async {
List<String> paths = [];
paths = await _deleteFolder(path, paths);
for (String path in paths) {
await FirebaseStorage.instance.ref().child(path).delete();
}
}
static Future<List<String>> _deleteFolder(String folder, List<String> paths) async {
ListResult list = await FirebaseStorage.instance.ref().child(folder).listAll();
List<Reference> items = list.items;
List<Reference> prefixes = list.prefixes;
for (Reference item in items) {
paths.add(item.fullPath);
}
for (Reference subfolder in prefixes) {
paths = await _deleteFolder(subfolder.fullPath, paths);
}
return paths;
}
}
Usage:
await FirebaseStorageApi.deleteFolder(path: "YOUR/FOLDER/PATH");

Currently, I'm working on a project that contains a single file inside every folder so I did this and it worked for me.
await FirebaseStorage.instance.ref("path/" + to + "/" + folder)
.listAll().then((value) {
FirebaseStorage.instance.ref(value.items.first.fullPath).delete();
});
This code deletes the file and folder as well. Since, there's no folder here, deleting the file deletes the folder or reference. You can use foreach loop or map to delete everything from the folder if you have multiple files.

Related

Xamarin.Forms - How to access /data/user/0/com.companyname.notes/files/.local/share/

I was following a small tutorial of Microsoft.
Which basically saves your text input onto the internal memory of your device.
String _filename = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "Notes.txt");
Results in: /data/user/0/com.companyname.notes/files/.local/share/Notes.txt for me.
Now, while everything works, I would like to see this Notes.txt file in the folder.
I have searched far and wide, but can't seem to find a way to locate this file on my device.
I can go to Android/data/com.companyname.notes/files but then I only see a ._override_ folder with the app project files in it, but without the Notes.txt
Any ideas?
Thanks
From your path:/data/user/0/com.companyname.notes/files/.local/share/Notes.txt, we can know that you want to access internal storage, but Internal storage refers to the non-volatile memory that Android allocates to the operating system, APKs, and for individual apps. This space is not accessible except by the operating system or apps. So you can not find this text file from internal storage.
If you want to see file, you can save this file in external storage
/storage/emulated/0/Android/data/com.companyname.app/files
More detailed info about internal storage, see:
https://learn.microsoft.com/en-us/xamarin/android/platform/files/
Update
If you want to save text file, you should declare one of the two permissions for external storage in the AndroidManifest.xml
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
Then the primary location for private external files is found by calling the method Android.Content.Context.GetExternalFilesDir(string type). This method will return a Java.IO.File object that represents the private external storage directory for the app. Passing null to this method will return the path to the user's storage directory for the application. As an example, for an application with the package name com.companyname.app, the "root" directory of the private external files would be:
/storage/emulated/0/Android/data/com.companyname.app/files/
In the Forms, you need to create new interface:
public interface IFileSystem
{
string GetExternalStorage();
}
Implement this interface in Android:
[assembly: Dependency(typeof(FileSystemImplementation))]
namespace FileApp.Droid
{
public class FileSystemImplementation : IFileSystem
{
public string GetExternalStorage()
{
Context context = Android.App.Application.Context;
var filePath = context.GetExternalFilesDir("");
return filePath.Path;
}
}
}
Now you can create text file and save text in this file:
private async void Btn1_Clicked(object sender, EventArgs e)
{
var folderPath = DependencyService.Get<IFileSystem>().GetExternalStorage();
var file = Path.Combine(folderPath, "count.txt");
using (var writer = File.CreateText(file))
{
await writer.WriteLineAsync("123456789000000000000000000000000000000000000");
}
}
I have made a sample:
https://github.com/CherryBu/FileApp
The exact path to the private external storage directory can vary from device to device and between versions of Android.
Open your File Manager App
Go to Android/data
Find the .com folder, in your case, com.companyname.notes
Follow the path until you find the file

Obtaining the URL to an Already Stored File in Firebase Storage through Flutter

I'm currently trying to access the URL to a file from Firebase Storage without logging into the Firebase Console and grabbing the URL by hand. I'm developing an app that will be able to obtain that URL and store it as a string.
I've been hitting a lot of walls when doing this through Flutter and I've tried searching SO for a means of obtaining the URL programmatically.
So far, I've tried one method that looks like it works, but it isn't actually performing the task that I had intended for it to do.
void getDownloadUrl(String audioName) {
FirebaseStorage firebaseStorage = FirebaseStorage.instance;
StorageReference songRef = firebaseStorage.ref().child('$audioName');
songRef.getMetadata().then(
(_) => print('successfully accessed firebase storage child'),
onError: (_) =>
print("Wasn't able to access metadata from " + songRef.path));
String audioString;
songRef
.getDownloadURL()
.then((_) => audioString = songRef.getDownloadURL().toString());
String audioPath;
if (firebaseStorage != null) {
audioPath = firebaseStorage.ref().child('$audioName').path;
} else {
print('songNameString is null. Check $audioPath');
}
}
This is one method I've tried and it is still unsuccessful.
Can someone please explain the best practice for accessing Firebase Storage, search through the folder, get a hold of a file and access the required URL that can be stored for later use as that is the main goal I have for this section?
I have also opened permissions for read/write access to Firebase Storage.
Just to be clear - I'm not uploading a file through the app. So I don't think I'll be able to call the event that returns a URL upon upload.
Try This :
printPath() async {
StorageReference ref =
FirebaseStorage.instance.ref().child('example/audio abc.mp3');
String url = (await ref.getDownloadURL()).toString();
print('Urlllllllllllllll $url');
}
Above code working fine .See below Screeenshot for refrence ;
Click Here to see the Image

How to get the dimensions of an image from firebase storage in firebase functions?

I have an app having firebase back-end. and when i made i didn't thought about the dimensions of images as if they ll be useful in future so i just kept the images as they are and kept their URLs in firestore.
But now i m in need of dimensions of images before showing them to user so i have thought of making a function that i ll execute only once in order to set the files with their dimension in firestore and i ll also add some client side code in order to get the dimensions before uploading them.
So i have tried almost everything to get the file dimensions in functions but couldn't do it.
sample[abstract code]
this code works in node.js but fails in firebase functions
const fs = require('fs')
const request = require('request')
import sizeOf from 'image-size'
const FIRE = 'https://firebasestorage.googleapis.com/file....'
const FILE = 'file.jpg';
request.head(FIRE, (err, res, body) => {
request(FIRE)
.pipe(fs.createWriteStream(FILE))
.on('close', () => {
sizeOf(FILE, (err1, dimensions) => {
const result = {
"width": dimensions.width,
"height": dimensions.height
}
console.log(dimensions.width, dimensions.height);
fs.unlinkSync(FILE);
response.setHeader('Content-Type', 'application/json');
const responseData = {
'Error': false,
'Message': "result : " + result
};
response.send(JSON.stringify(responseData));
})
})
})
help me if someone knows something about this!
and moreover also tell me about how firebase keeps images, i mean in what manner ? whenever i open the url it doesnt show me the image instead it just downloads the image unline other urls on random websites.
I have got a trick to do it. It is quite prone to error, but will work for sure :
get all the urls of images using an api and do the stuff locally using node.js and post the result to another api, which will then feed the data to firestore ?
Your code is trying to write to:
const FILE = 'file.jpg';
Which is a file in the same directory as where your index.js is stored. This is (as the error message says) a read-only directory in the Cloud Functions container. If you want to write any files, they must be in /tmp (also known as tempfs). See Write temporary files from Google Cloud Function

File download from API to Meteor server and upload to S3

I am sending a request from my Meteor server to download a file via an API. I then want to upload that file to S3. I keep getting the following "NoSuchKey: The specified key does not exist." I initially thought it was maybe a problem with my AcessKey/SecretKey form AWS but after googling this for a while the only examples I could find of other people getting this error is when trying to download a file from S3.
Setting up cfs:s3
var imageStore = new FS.Store.S3("images", {
accessKeyId: "MyAcessKeyId", //required if environment variables are not set
secretAccessKey: "MySecretAcessKey", //required if environment variables are not set
bucket: "BucketName", //required
});
Images = new FS.Collection("images", {
stores: [imageStore]
});
Start file transfer from API and upload to S3
client.get_result(id, Meteor.bindEnvironment(function(err, result){ //result is the download stream and id specifies which file to download.
if (err !== null){
return;
}
var file = new FS.File(result);
Images.insert(file, function (err, fileObj) {
if (err){
console.log(err);
}
});
}));
Note: I was getting the following error so I added Meteor.bindEnvironment.
"Meteor code must always run within a Fiber. Try wrapping callbacks that you pass to non-Meteor libraries with Meteor.bindEnvironment."
Node.js example from API Documentation
client.get_result(id, function(err, result){
if (err != null) {
return;
}
file.writeFile(path.join('public', path.join('results', filename)), result, 'binary');
});
What ended up fixing the problem for me was moving part of the setup to the lib folder. Although I tried several different ways I was unable to get it to execute entirely on the server. It looks like the documentation was updated recently which states everything a bit more clearly. If you follow this setup it should eliminate the error. See the section titled Client, Server, and S3 credentials
https://github.com/CollectionFS/Meteor-CollectionFS/tree/master/packages/s3
Note: Make sure not to place you secret key is not in you lib folder as this is accessible from the client.

How to protect a file directory and only allow authenticated users to access the files?

how do I restrict a folder, so only those who logged in into my Meteor app can download files?
I looked into multiple ways of doing this, but the main problem is that I can't access ( I get null.) with:
Meteor.user() or this.userId()
I tried:
__meteor_bootstrap__.app
.use(connect.query())
.use(function(req, res, next) {
Fiber(function () {
// USER HERE?
}).run();
});
or
__meteor_bootstrap__.app.stack.unshift({
route: "/protected/secret_document.doc", // only users can download this
handle: function(req, res) { Fiber(function() {
// CHECK USER HERE ?
// IF NOT LOGGED IN:
res.writeHead(403, {'Content-Type': 'text/html'});
var content = '<html><body>403 Forbidden</body></html>';
res.end(content, 'utf-8');
}).run() }
});
You could try storing the files in mongodb, which would mean that they would then be hooked into your collection system and be queryable on the client and server. Then, just publish the relevant data to the client for specific users, or use Meteor.methods to expose information that way.
Example:
Assuming files are stored in MongoDB, let's first publish them to the client:
Meteor.publish("files", function(folder) {
if (!this.userId) return;
// the userHasAccessToFolder method checks whether
// this user is allowed to see files in this folder
if (userHasAccessToFolder(this.userId, folder))
// if so, return the files for that folder
// (filter the results however you need to)
return Files.find({folder: folder});
});
Then on the client, we autosubscribe to the published channel so that whenever it changes, it gets refreshed:
Meteor.startup(function() {
Meteor.autosubscribe(function() {
// send the current folder to the server,
// which will return the files in the folder
// only if the current user is allowed to see it
Meteor.subscribe("files", Session.get("currentFolder"));
});
});
NB. I haven't tested above code so consider it pseudocode, but it should point you in the general direction for solving this problem. The hard part is storing the files in mongodb!
i'd be more concerned as to why Meteor.user() isn't working.
a few questions:
are you on meteor 0.5.0?
have you added accounts-base to your meteor project?
have you used one of meteor's login systems (accounts-password, accounts-facebook, etc)? (optional - accounts-ui for ease of use?)
have you still got autopublish on? or have you set up publishing / subscription properly?
Meteor.user() should be the current user, and Meteor.users should be a Meteor Collection of all previous logged in users.

Resources