如何在 Firebase 应用程序中获取云存储中所有文件的列表?
How to get a list of all files in Cloud Storage in a Firebase app?
我正在上传图片,一切正常,但我有 100 张图片,我想在我的 View
中显示所有图片,因为我在文件夹,我找不到任何 API 适合这项工作。
因为 JavaScript release 6.1, iOS release 6.4, and Android release version 18.1 的 Firebase SDK 都有列出文件的方法。
documentation is a bit sparse so far, so I recommend checking out 了解详情。
上一个答案,因为这种方法有时仍然有用:
目前在 Firebase SDK 中没有 API 调用来从应用程序中列出云存储文件夹中的所有文件。如果您需要这样的功能,您应该将文件的元数据(例如下载 URL)存储在可以列出它们的地方。 Firebase Realtime Database and Cloud Firestore 非常适合这一点,并且还可以让您轻松地与他人共享 URL。
您可以在我们的 FriendlyPix sample app. The relevant code for the web version is here 中找到一个很好的(但有点复杂)示例,但也有 iOS 和 Android 的版本。
一种解决方法是创建一个内部没有任何内容的文件(即 list.txt),在此文件中,您可以使用所有列表设置自定义元数据(即 Map)该文件的 URL.
因此,如果您需要下载 fodler 中的所有文件,您首先下载 list.txt 文件的元数据,然后遍历自定义数据并下载所有地图中带有 URL 的文件。
由于没有列出任何语言,我将在 Swift 中回答这个问题。我们强烈建议同时使用 Firebase 存储和 Firebase 实时数据库来完成下载列表:
已分享:
// Firebase services
var database: FIRDatabase!
var storage: FIRStorage!
...
// Initialize Database, Auth, Storage
database = FIRDatabase.database()
storage = FIRStorage.storage()
...
// Initialize an array for your pictures
var picArray: [UIImage]()
上传:
let fileData = NSData() // get data...
let storageRef = storage.reference().child("myFiles/myFile")
storageRef.putData(fileData).observeStatus(.Success) { (snapshot) in
// When the image has successfully uploaded, we get it's download URL
let downloadURL = snapshot.metadata?.downloadURL()?.absoluteString
// Write the download URL to the Realtime Database
let dbRef = database.reference().child("myFiles/myFile")
dbRef.setValue(downloadURL)
}
下载:
let dbRef = database.reference().child("myFiles")
dbRef.observeEventType(.ChildAdded, withBlock: { (snapshot) in
// Get download URL from snapshot
let downloadURL = snapshot.value() as! String
// Create a storage reference from the URL
let storageRef = storage.referenceFromURL(downloadURL)
// Download the data, assuming a max size of 1MB (you can change this as necessary)
storageRef.dataWithMaxSize(1 * 1024 * 1024) { (data, error) -> Void in
// Create a UIImage, add it to the array
let pic = UIImage(data: data)
picArray.append(pic)
})
})
有关详细信息,请参阅 Zero to App: Develop with Firebase, and it's associated source code,了解如何执行此操作的实际示例。
我在做项目的时候也遇到了这个问题。我真的希望他们提供一个 end api 方法。不管怎样,我就是这样做的:
当您将图像上传到 Firebase 存储时,创建一个对象并将该对象同时传递给 Firebase 数据库。此对象包含图像的下载 URI。
trailsRef.putFile(file).addOnSuccessListener(new OnSuccessListener<UploadTask.TaskSnapshot>() {
@Override
public void onSuccess(UploadTask.TaskSnapshot taskSnapshot) {
Uri downloadUri = taskSnapshot.getDownloadUrl();
DatabaseReference myRef = database.getReference().child("trails").child(trail.getUnique_id()).push();
Image img = new Image(trail.getUnique_id(), downloadUri.toString());
myRef.setValue(img);
}
});
稍后当您想要从文件夹下载图像时,只需遍历该文件夹下的文件即可。此文件夹与 Firebase 存储中的 "folder" 同名,但您可以随意命名。我把它们放在单独的线程中。
@Override
protected List<Image> doInBackground(Trail... params) {
String trialId = params[0].getUnique_id();
mDatabase = FirebaseDatabase.getInstance().getReference();
mDatabase.child("trails").child(trialId).addValueEventListener(new ValueEventListener() {
@Override
public void onDataChange(DataSnapshot dataSnapshot) {
images = new ArrayList<>();
Iterator<DataSnapshot> iter = dataSnapshot.getChildren().iterator();
while (iter.hasNext()) {
Image img = iter.next().getValue(Image.class);
images.add(img);
}
isFinished = true;
}
@Override
public void onCancelled(DatabaseError databaseError) {
}
});
现在我有了一个对象列表,其中包含每个图像的 URI,我可以对它们做任何我想做的事情。为了将它们加载到 imageView 中,我创建了另一个线程。
@Override
protected List<Bitmap> doInBackground(List<Image>... params) {
List<Bitmap> bitmaps = new ArrayList<>();
for (int i = 0; i < params[0].size(); i++) {
try {
URL url = new URL(params[0].get(i).getImgUrl());
Bitmap bmp = BitmapFactory.decodeStream(url.openConnection().getInputStream());
bitmaps.add(bmp);
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
return bitmaps;
}
这个 returns 位图列表,当它完成时,我只需将它们附加到主 activity 中的 ImageView。下面的方法是 @Override 因为我创建了接口并在其他线程中监听完成。
@Override
public void processFinishForBitmap(List<Bitmap> bitmaps) {
List<ImageView> imageViews = new ArrayList<>();
View v;
for (int i = 0; i < bitmaps.size(); i++) {
v = mInflater.inflate(R.layout.gallery_item, mGallery, false);
imageViews.add((ImageView) v.findViewById(R.id.id_index_gallery_item_image));
imageViews.get(i).setImageBitmap(bitmaps.get(i));
mGallery.addView(v);
}
}
请注意,我必须先等待 List Image 返回,然后调用线程处理 List Bitmap。在本例中,Image 包含 URI。
@Override
public void processFinish(List<Image> results) {
Log.e(TAG, "get back " + results.size());
LoadImageFromUrlTask loadImageFromUrlTask = new LoadImageFromUrlTask();
loadImageFromUrlTask.delegate = this;
loadImageFromUrlTask.execute(results);
}
希望有人觉得它有用。以后也可以作为自己的公会线。
用 JS 做到这一点
您可以将它们直接附加到您的 div 容器中,也可以将它们推送到一个数组中。下面向您展示了如何将它们附加到您的 div.
1) 当您将图像存储在存储器中时,使用以下结构在您的 firebase 数据库中创建对图像的引用
/images/(imageName){
description: "" ,
imageSrc : (imageSource)
}
2) 当您加载文档时,使用以下代码从数据库中提取所有图像源 URL 而不是存储
$(document).ready(function(){
var query = firebase.database().ref('images/').orderByKey();
query.once("value").then(function(snapshot){
snapshot.forEach(function(childSnapshot){
var imageName = childSnapshot.key;
var childData = childSnapshot.val();
var imageSource = childData.url;
$('#imageGallery').append("<div><img src='"+imageSource+"'/></div>");
})
})
});
您可以使用以下代码。在这里,我将图像上传到 firebase 存储,然后我将图像下载 url 存储到 firebase 数据库。
//getting the storage reference
StorageReference sRef = storageReference.child(Constants.STORAGE_PATH_UPLOADS + System.currentTimeMillis() + "." + getFileExtension(filePath));
//adding the file to reference
sRef.putFile(filePath)
.addOnSuccessListener(new OnSuccessListener<UploadTask.TaskSnapshot>() {
@Override
public void onSuccess(UploadTask.TaskSnapshot taskSnapshot) {
//dismissing the progress dialog
progressDialog.dismiss();
//displaying success toast
Toast.makeText(getApplicationContext(), "File Uploaded ", Toast.LENGTH_LONG).show();
//creating the upload object to store uploaded image details
Upload upload = new Upload(editTextName.getText().toString().trim(), taskSnapshot.getDownloadUrl().toString());
//adding an upload to firebase database
String uploadId = mDatabase.push().getKey();
mDatabase.child(uploadId).setValue(upload);
}
})
.addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(@NonNull Exception exception) {
progressDialog.dismiss();
Toast.makeText(getApplicationContext(), exception.getMessage(), Toast.LENGTH_LONG).show();
}
})
.addOnProgressListener(new OnProgressListener<UploadTask.TaskSnapshot>() {
@Override
public void onProgress(UploadTask.TaskSnapshot taskSnapshot) {
//displaying the upload progress
double progress = (100.0 * taskSnapshot.getBytesTransferred()) / taskSnapshot.getTotalByteCount();
progressDialog.setMessage("Uploaded " + ((int) progress) + "%...");
}
});
现在要获取存储在 firebase 数据库中的所有图像,您可以使用
//adding an event listener to fetch values
mDatabase.addValueEventListener(new ValueEventListener() {
@Override
public void onDataChange(DataSnapshot snapshot) {
//dismissing the progress dialog
progressDialog.dismiss();
//iterating through all the values in database
for (DataSnapshot postSnapshot : snapshot.getChildren()) {
Upload upload = postSnapshot.getValue(Upload.class);
uploads.add(upload);
}
//creating adapter
adapter = new MyAdapter(getApplicationContext(), uploads);
//adding adapter to recyclerview
recyclerView.setAdapter(adapter);
}
@Override
public void onCancelled(DatabaseError databaseError) {
progressDialog.dismiss();
}
});
更多详情可以看我的postFirebase Storage Example.
自 2017 年 3 月以来: 随着 Firebase Cloud Functions 的加入,以及 Firebase 与 Google Cloud 的更深入集成,现在这成为可能。
借助 Cloud Functions,您可以使用 Google Cloud Node 包在 Cloud Storage 上执行 epic 操作。下面的示例将所有文件 URL 从 Cloud Storage 获取到一个数组中。每次将内容保存到 google 云存储时都会触发此功能。
注1:这是一个计算量相当大的操作,因为它必须循环遍历存储桶/文件夹中的所有文件。
注释 2:我只是作为一个例子写的,没有对 promises 等做太多细节。只是提供一个想法。
const functions = require('firebase-functions');
const gcs = require('@google-cloud/storage')();
// let's trigger this function with a file upload to google cloud storage
exports.fileUploaded = functions.storage.object().onChange(event => {
const object = event.data; // the object that was just uploaded
const bucket = gcs.bucket(object.bucket);
const signedUrlConfig = { action: 'read', expires: '03-17-2025' }; // this is a signed url configuration object
var fileURLs = []; // array to hold all file urls
// this is just for the sake of this example. Ideally you should get the path from the object that is uploaded :)
const folderPath = "a/path/you/want/its/folder/size/calculated";
bucket.getFiles({ prefix: folderPath }, function(err, files) {
// files = array of file objects
// not the contents of these files, we're not downloading the files.
files.forEach(function(file) {
file.getSignedUrl(signedUrlConfig, function(err, fileURL) {
console.log(fileURL);
fileURLs.push(fileURL);
});
});
});
});
我希望这会给你一个大概的想法。如需更好的云函数示例,请查看 Google's Github repo full of Cloud Functions samples for Firebase. Also check out their Google Cloud Node API Documentation
使用 Cloud Function 跟踪每个上传的图像并将其存储在数据库中的另一种将图像添加到数据库的方法。
exports.fileUploaded = functions.storage.object().onChange(event => {
const object = event.data; // the object that was just uploaded
const contentType = event.data.contentType; // This is the image Mimme type\
// Exit if this is triggered on a file that is not an image.
if (!contentType.startsWith('image/')) {
console.log('This is not an image.');
return null;
}
// Get the Signed URLs for the thumbnail and original image.
const config = {
action: 'read',
expires: '03-01-2500'
};
const bucket = gcs.bucket(event.data.bucket);
const filePath = event.data.name;
const file = bucket.file(filePath);
file.getSignedUrl(config, function(err, fileURL) {
console.log(fileURL);
admin.database().ref('images').push({
src: fileURL
});
});
});
完整代码在这里:
https://gist.github.com/bossly/fb03686f2cb1699c2717a0359880cf84
所以我有一个项目需要从 firebase 存储下载资产,所以我不得不自己解决这个问题。方法如下:
1- 首先,制作一个模型数据,例如 class Choice{}
,其中 class 定义了一个名为 image Name 的字符串变量,因此它会像那样
class Choice {
.....
String imageName;
}
2- 从 database/firebase 数据库中,将图像名称硬编码到对象中,因此如果您有名为 Apple.png 的图像名称,请将对象创建为
Choice myChoice = new Choice(...,....,"Apple.png");
3- 现在,为您的 firebase 存储中的资产获取 link,类似于
gs://your-project-name.appspot.com/
like this one
4- 最后,初始化您的 firebase 存储引用并开始通过这样的循环获取文件
storageRef = storage.getReferenceFromUrl(firebaseRefURL).child(imagePath);
File localFile = File.createTempFile("images", "png");
storageRef.getFile(localFile).addOnSuccessListener(new OnSuccessListener<FileDownloadTask.TaskSnapshot>() {
@Override
public void onSuccess(FileDownloadTask.TaskSnapshot taskSnapshot) {
//Dismiss Progress Dialog\
}
5- 就是这样
对于node js,我使用了这段代码
const Storage = require('@google-cloud/storage');
const storage = new Storage({projectId: 'PROJECT_ID', keyFilename: 'D:\keyFileName.json'});
const bucket = storage.bucket('project.appspot.com'); //gs://project.appspot.com
bucket.getFiles().then(results => {
const files = results[0];
console.log('Total files:', files.length);
files.forEach(file => {
file.download({destination: `D:\${file}`}).catch(error => console.log('Error: ', error))
});
}).catch(err => {
console.error('ERROR:', err);
});
Android 最好的做法是使用 FirebaseUI 和 Glide。
您需要将其添加到您的 gradle/app 上才能获取图书馆。请注意,它上面已经有 Glide 了!
implementation 'com.firebaseui:firebase-ui-storage:4.1.0'
然后在您的代码中使用
// Reference to an image file in Cloud Storage
StorageReference storageReference = FirebaseStorage.getInstance().getReference();
// ImageView in your Activity
ImageView imageView = findViewById(R.id.imageView);
// Download directly from StorageReference using Glide
// (See MyAppGlideModule for Loader registration)
GlideApp.with(this /* context */)
.load(storageReference)
.into(imageView);
实际上这是可能的,但只有 Google Cloud API instead one from Firebase. It's because a Firebase Storage is a Google Cloud Storage Bucket 可以通过 Google 云 API 轻松实现,但是您需要使用 OAuth 进行身份验证而不是 Firebase 的身份验证。
#In Python
import firebase_admin
from firebase_admin import credentials
from firebase_admin import storage
import datetime
import urllib.request
def image_download(url, name_img) :
urllib.request.urlretrieve(url, name_img)
cred = credentials.Certificate("credentials.json")
# Initialize the app with a service account, granting admin privileges
app = firebase_admin.initialize_app(cred, {
'storageBucket': 'YOURSTORAGEBUCKETNAME.appspot.com',
})
url_img = "gs://YOURSTORAGEBUCKETNAME.appspot.com/"
bucket_1 = storage.bucket(app=app)
image_urls = []
for blob in bucket_1.list_blobs():
name = str(blob.name)
#print(name)
blob_img = bucket_1.blob(name)
X_url = blob_img.generate_signed_url(datetime.timedelta(seconds = 300), method='GET')
#print(X_url)
image_urls.append(X_url)
PATH = ['Where you want to save the image']
for path in PATH:
i = 1
for url in image_urls:
name_img = str(path + "image"+str(i)+".jpg")
image_download(url, name_img)
i+=1
我遇到了同样的问题,我的更复杂。
管理员会将音频和 pdf 文件上传到存储中:
audios/season1, season2.../class1, class 2/.mp3 文件
书籍/.pdf 文件
Android应用程序需要获取子文件夹和文件列表。
解决方案是在存储上捕获上传事件并使用云函数在 firestore 上创建相同的结构。
第 1 步:在 firestore
上手动创建 'storage' 集合和 'audios/books' 文档
第 2 步:设置云功能
可能需要大约 15 分钟:https://www.youtube.com/watch?v=DYfP-UIKxH0&list=PLl-K7zZEsYLkPZHe41m4jfAxUi0JjLgSM&index=1
第 3 步:使用云函数捕获上传事件
import * as functions from 'firebase-functions';
import * as admin from 'firebase-admin';
admin.initializeApp(functions.config().firebase);
const path = require('path');
export const onFileUpload = functions.storage.object().onFinalize(async (object) => {
let filePath = object.name; // File path in the bucket.
const contentType = object.contentType; // File content type.
const metageneration = object.metageneration; // Number of times metadata has been generated. New objects have a value of 1.
if (metageneration !== "1") return;
// Get the file name.
const fileName = path.basename(filePath);
filePath = filePath.substring(0, filePath.length - 1);
console.log('contentType ' + contentType);
console.log('fileName ' + fileName);
console.log('filePath ' + filePath);
console.log('path.dirname(filePath) ' + path.dirname(filePath));
filePath = path.dirname(filePath);
const pathArray = filePath.split("/");
let ref = '';
for (const item of pathArray) {
if (ref.length === 0) {
ref = item;
}
else {
ref = ref.concat('/sub/').concat(item);
}
}
ref = 'storage/'.concat(ref).concat('/sub')
admin.firestore().collection(ref).doc(fileName).create({})
.then(result => {console.log('onFileUpload:updated')})
.catch(error => {
console.log(error);
});
});
第 4 步:使用 firestore
在 Android 应用程序上检索 folders/files 的列表
private static final String STORAGE_DOC = "storage/";
public static void getMediaCollection(String path, OnCompleteListener onCompleteListener) {
String[] pathArray = path.split("/");
String doc = null;
for (String item : pathArray) {
if (TextUtils.isEmpty(doc)) doc = STORAGE_DOC.concat(item);
else doc = doc.concat("/sub/").concat(item);
}
doc = doc.concat("/sub");
getFirestore().collection(doc).get().addOnCompleteListener(onCompleteListener);
}
第 5 步:下载 url
public static void downloadMediaFile(String path, OnCompleteListener<Uri> onCompleteListener) {
getStorage().getReference().child(path).getDownloadUrl().addOnCompleteListener(onCompleteListener);
}
备注
我们必须将“子”集合放入每个项目,因为 firestore 不支持检索集合列表。
我花了 3 天的时间才找到解决方案,希望最多能花你 3 个小时。
自 2019 年 5 月起,version 6.1.0 的 Firebase SDK for Cloud Storage 现在支持列出存储桶中的所有对象。您只需要在 Reference
:
中调用 listAll()
// Since you mentioned your images are in a folder,
// we'll create a Reference to that folder:
var storageRef = firebase.storage().ref("your_folder");
// Now we get the references of these images
storageRef.listAll().then(function(result) {
result.items.forEach(function(imageRef) {
// And finally display them
displayImage(imageRef);
});
}).catch(function(error) {
// Handle any errors
});
function displayImage(imageRef) {
imageRef.getDownloadURL().then(function(url) {
// TODO: Display the image on the UI
}).catch(function(error) {
// Handle any errors
});
}
请注意,为了使用此功能,您必须opt-in到version 2 of Security Rules,这可以通过rules_version = '2';
来完成您的安全规则的第一行:
rules_version = '2';
service firebase.storage {
match /b/{bucket}/o {
match /{allPaths=**} {
我建议查看 docs 以供进一步参考。
此外,根据 setup,在第 5 步,Node.js
不允许此脚本,因为 require("firebase/app");
不会 return firebase.storage()
作为一个功能。这只能使用 import * as firebase from 'firebase/app';
.
来实现
扩展 Rosário Pereira Fernandes 的 答案,JavaScript 解决方案:
- 在你的机器上安装 firebase
npm install -g firebase-tools
- 在 firebase init 上将
JavaScript
设置为默认语言
- 在创建项目的根目录下执行 npm installs
npm install --save firebase
npm install @google-cloud/storage
npm install @google-cloud/firestore
... <any other dependency needed>
- 在项目中添加非默认依赖项,例如
"firebase": "^6.3.3",
"@google-cloud/storage": "^3.0.3"
functions/package.json
{
"name": "functions",
"description": "Cloud Functions for Firebase",
"scripts": {
"lint": "eslint .",
"serve": "firebase serve --only functions",
"shell": "firebase functions:shell",
"start": "npm run shell",
"deploy": "firebase deploy --only functions",
"logs": "firebase functions:log"
},
"engines": {
"node": "10"
},
"dependencies": {
"@google-cloud/storage": "^3.0.3",
"firebase": "^6.3.3",
"firebase-admin": "^8.0.0",
"firebase-functions": "^3.1.0"
},
"devDependencies": {
"eslint": "^5.12.0",
"eslint-plugin-promise": "^4.0.1",
"firebase-functions-test": "^0.1.6"
},
"private": true
}
- 创建某种
listAll
函数
index.js
var serviceAccount = require("./key.json");
const functions = require('firebase-functions');
const images = require('./images.js');
var admin = require("firebase-admin");
admin.initializeApp({
credential: admin.credential.cert(serviceAccount),
databaseURL: "https://<my_project>.firebaseio.com"
});
const bucket = admin.storage().bucket('<my_bucket>.appspot.com')
exports.getImages = functions.https.onRequest((request, response) => {
images.getImages(bucket)
.then(urls => response.status(200).send({ data: { urls } }))
.catch(err => console.error(err));
})
images.js
module.exports = {
getImages
}
const query = {
directory: 'images'
};
function getImages(bucket) {
return bucket.getFiles(query)
.then(response => getUrls(response))
.catch(err => console.error(err));
}
function getUrls(response) {
const promises = []
response.forEach( files => {
files.forEach (file => {
promises.push(getSignedUrl(file));
});
});
return Promise.all(promises).then(result => getParsedUrls(result));
}
function getSignedUrl(file) {
return file.getSignedUrl({
action: 'read',
expires: '09-01-2019'
})
}
function getParsedUrls(result) {
return JSON.stringify(result.map(mediaLink => createMedia(mediaLink)));
}
function createMedia(mediaLink) {
const reference = {};
reference.mediaLink = mediaLink[0];
return reference;
}
- 执行
firebase deploy
上传你的云函数
- Call 您的应用中的自定义功能
build.gradle
dependencies {
...
implementation 'com.google.firebase:firebase-functions:18.1.0'
...
}
kotlin class
private val functions = FirebaseFunctions.getInstance()
val cloudFunction = functions.getHttpsCallable("getImages")
cloudFunction.call().addOnSuccessListener {...}
关于此功能的进一步开发,我运行 提出了一些可能发现的问题。
您可以通过listAll() 方法列出firebase 存储目录中的文件。
要使用此方法,必须实施此版本的 firebase 存储。
'com.google.firebase:firebase-storage:18.1.1'
https://firebase.google.com/docs/storage/android/list-files
请记住将安全规则升级到版本 2。
我正在使用 AngularFire
并使用以下命令获取所有 downloadURL
getPhotos(id: string): Observable<string[]> {
const ref = this.storage.ref(`photos/${id}`)
return ref.listAll().pipe(switchMap(list => {
const calls: Promise<string>[] = [];
list.items.forEach(item => calls.push(item.getDownloadURL()))
return Promise.all(calls)
}));
}
结合这个 post 和 的一些答案,经过一些个人研究,对于带有打字稿的 NodeJS,我设法通过使用 firebase-admin
:
import * as admin from 'firebase-admin';
const getFileNames = () => {
admin.storage().bucket().getFiles(autoPaginate: false).then(([files]: any) => {
const fileNames = files.map((file: any) => file.name);
return fileNames;
})
}
就我而言,我还需要从 firebase 存储中获取特定文件夹中的所有文件。根据 google storage 文件夹不存在,而是一种命名约定。无论如何,我通过在 getFiles
函数调用中添加 { prefix: ${folderName}, autoPaginate: false }
来设法做到这一点(没有将每个文件的完整路径保存到数据库中)所以:
...
const getFileNames = (folderName: string) => {
admin.storage().bucket().getFiles({ prefix: `${folderName}`, autoPaginate: false })
.then(([files]: any) => {
...
在Swift
public func downloadData() async {
let imagesRef = storage.child("pictures/")
do {
let storageReference = try await storage.root().child("pictures").listAll()
print("storageReference: \(storageReference.items)")
} catch {
print(error)
}
}
输出
[
gs://<your_app_name>.appspot.com/pictures/IMG_1243.JPG,
gs://<your_app_name>.appspot.com/pictures/IMG_1244.JPG,
gs://<your_app_name>.appspot.com/pictures/IMG_1245.JPG,
gs://<your_app_name>.appspot.com/pictures/IMG_1246.JPG
]
我正在上传图片,一切正常,但我有 100 张图片,我想在我的 View
中显示所有图片,因为我在文件夹,我找不到任何 API 适合这项工作。
因为 JavaScript release 6.1, iOS release 6.4, and Android release version 18.1 的 Firebase SDK 都有列出文件的方法。
documentation is a bit sparse so far, so I recommend checking out
上一个答案,因为这种方法有时仍然有用:
目前在 Firebase SDK 中没有 API 调用来从应用程序中列出云存储文件夹中的所有文件。如果您需要这样的功能,您应该将文件的元数据(例如下载 URL)存储在可以列出它们的地方。 Firebase Realtime Database and Cloud Firestore 非常适合这一点,并且还可以让您轻松地与他人共享 URL。
您可以在我们的 FriendlyPix sample app. The relevant code for the web version is here 中找到一个很好的(但有点复杂)示例,但也有 iOS 和 Android 的版本。
一种解决方法是创建一个内部没有任何内容的文件(即 list.txt),在此文件中,您可以使用所有列表设置自定义元数据(即 Map
因此,如果您需要下载 fodler 中的所有文件,您首先下载 list.txt 文件的元数据,然后遍历自定义数据并下载所有地图中带有 URL 的文件。
由于没有列出任何语言,我将在 Swift 中回答这个问题。我们强烈建议同时使用 Firebase 存储和 Firebase 实时数据库来完成下载列表:
已分享:
// Firebase services
var database: FIRDatabase!
var storage: FIRStorage!
...
// Initialize Database, Auth, Storage
database = FIRDatabase.database()
storage = FIRStorage.storage()
...
// Initialize an array for your pictures
var picArray: [UIImage]()
上传:
let fileData = NSData() // get data...
let storageRef = storage.reference().child("myFiles/myFile")
storageRef.putData(fileData).observeStatus(.Success) { (snapshot) in
// When the image has successfully uploaded, we get it's download URL
let downloadURL = snapshot.metadata?.downloadURL()?.absoluteString
// Write the download URL to the Realtime Database
let dbRef = database.reference().child("myFiles/myFile")
dbRef.setValue(downloadURL)
}
下载:
let dbRef = database.reference().child("myFiles")
dbRef.observeEventType(.ChildAdded, withBlock: { (snapshot) in
// Get download URL from snapshot
let downloadURL = snapshot.value() as! String
// Create a storage reference from the URL
let storageRef = storage.referenceFromURL(downloadURL)
// Download the data, assuming a max size of 1MB (you can change this as necessary)
storageRef.dataWithMaxSize(1 * 1024 * 1024) { (data, error) -> Void in
// Create a UIImage, add it to the array
let pic = UIImage(data: data)
picArray.append(pic)
})
})
有关详细信息,请参阅 Zero to App: Develop with Firebase, and it's associated source code,了解如何执行此操作的实际示例。
我在做项目的时候也遇到了这个问题。我真的希望他们提供一个 end api 方法。不管怎样,我就是这样做的: 当您将图像上传到 Firebase 存储时,创建一个对象并将该对象同时传递给 Firebase 数据库。此对象包含图像的下载 URI。
trailsRef.putFile(file).addOnSuccessListener(new OnSuccessListener<UploadTask.TaskSnapshot>() {
@Override
public void onSuccess(UploadTask.TaskSnapshot taskSnapshot) {
Uri downloadUri = taskSnapshot.getDownloadUrl();
DatabaseReference myRef = database.getReference().child("trails").child(trail.getUnique_id()).push();
Image img = new Image(trail.getUnique_id(), downloadUri.toString());
myRef.setValue(img);
}
});
稍后当您想要从文件夹下载图像时,只需遍历该文件夹下的文件即可。此文件夹与 Firebase 存储中的 "folder" 同名,但您可以随意命名。我把它们放在单独的线程中。
@Override
protected List<Image> doInBackground(Trail... params) {
String trialId = params[0].getUnique_id();
mDatabase = FirebaseDatabase.getInstance().getReference();
mDatabase.child("trails").child(trialId).addValueEventListener(new ValueEventListener() {
@Override
public void onDataChange(DataSnapshot dataSnapshot) {
images = new ArrayList<>();
Iterator<DataSnapshot> iter = dataSnapshot.getChildren().iterator();
while (iter.hasNext()) {
Image img = iter.next().getValue(Image.class);
images.add(img);
}
isFinished = true;
}
@Override
public void onCancelled(DatabaseError databaseError) {
}
});
现在我有了一个对象列表,其中包含每个图像的 URI,我可以对它们做任何我想做的事情。为了将它们加载到 imageView 中,我创建了另一个线程。
@Override
protected List<Bitmap> doInBackground(List<Image>... params) {
List<Bitmap> bitmaps = new ArrayList<>();
for (int i = 0; i < params[0].size(); i++) {
try {
URL url = new URL(params[0].get(i).getImgUrl());
Bitmap bmp = BitmapFactory.decodeStream(url.openConnection().getInputStream());
bitmaps.add(bmp);
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
return bitmaps;
}
这个 returns 位图列表,当它完成时,我只需将它们附加到主 activity 中的 ImageView。下面的方法是 @Override 因为我创建了接口并在其他线程中监听完成。
@Override
public void processFinishForBitmap(List<Bitmap> bitmaps) {
List<ImageView> imageViews = new ArrayList<>();
View v;
for (int i = 0; i < bitmaps.size(); i++) {
v = mInflater.inflate(R.layout.gallery_item, mGallery, false);
imageViews.add((ImageView) v.findViewById(R.id.id_index_gallery_item_image));
imageViews.get(i).setImageBitmap(bitmaps.get(i));
mGallery.addView(v);
}
}
请注意,我必须先等待 List Image 返回,然后调用线程处理 List Bitmap。在本例中,Image 包含 URI。
@Override
public void processFinish(List<Image> results) {
Log.e(TAG, "get back " + results.size());
LoadImageFromUrlTask loadImageFromUrlTask = new LoadImageFromUrlTask();
loadImageFromUrlTask.delegate = this;
loadImageFromUrlTask.execute(results);
}
希望有人觉得它有用。以后也可以作为自己的公会线。
用 JS 做到这一点
您可以将它们直接附加到您的 div 容器中,也可以将它们推送到一个数组中。下面向您展示了如何将它们附加到您的 div.
1) 当您将图像存储在存储器中时,使用以下结构在您的 firebase 数据库中创建对图像的引用
/images/(imageName){
description: "" ,
imageSrc : (imageSource)
}
2) 当您加载文档时,使用以下代码从数据库中提取所有图像源 URL 而不是存储
$(document).ready(function(){
var query = firebase.database().ref('images/').orderByKey();
query.once("value").then(function(snapshot){
snapshot.forEach(function(childSnapshot){
var imageName = childSnapshot.key;
var childData = childSnapshot.val();
var imageSource = childData.url;
$('#imageGallery').append("<div><img src='"+imageSource+"'/></div>");
})
})
});
您可以使用以下代码。在这里,我将图像上传到 firebase 存储,然后我将图像下载 url 存储到 firebase 数据库。
//getting the storage reference
StorageReference sRef = storageReference.child(Constants.STORAGE_PATH_UPLOADS + System.currentTimeMillis() + "." + getFileExtension(filePath));
//adding the file to reference
sRef.putFile(filePath)
.addOnSuccessListener(new OnSuccessListener<UploadTask.TaskSnapshot>() {
@Override
public void onSuccess(UploadTask.TaskSnapshot taskSnapshot) {
//dismissing the progress dialog
progressDialog.dismiss();
//displaying success toast
Toast.makeText(getApplicationContext(), "File Uploaded ", Toast.LENGTH_LONG).show();
//creating the upload object to store uploaded image details
Upload upload = new Upload(editTextName.getText().toString().trim(), taskSnapshot.getDownloadUrl().toString());
//adding an upload to firebase database
String uploadId = mDatabase.push().getKey();
mDatabase.child(uploadId).setValue(upload);
}
})
.addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(@NonNull Exception exception) {
progressDialog.dismiss();
Toast.makeText(getApplicationContext(), exception.getMessage(), Toast.LENGTH_LONG).show();
}
})
.addOnProgressListener(new OnProgressListener<UploadTask.TaskSnapshot>() {
@Override
public void onProgress(UploadTask.TaskSnapshot taskSnapshot) {
//displaying the upload progress
double progress = (100.0 * taskSnapshot.getBytesTransferred()) / taskSnapshot.getTotalByteCount();
progressDialog.setMessage("Uploaded " + ((int) progress) + "%...");
}
});
现在要获取存储在 firebase 数据库中的所有图像,您可以使用
//adding an event listener to fetch values
mDatabase.addValueEventListener(new ValueEventListener() {
@Override
public void onDataChange(DataSnapshot snapshot) {
//dismissing the progress dialog
progressDialog.dismiss();
//iterating through all the values in database
for (DataSnapshot postSnapshot : snapshot.getChildren()) {
Upload upload = postSnapshot.getValue(Upload.class);
uploads.add(upload);
}
//creating adapter
adapter = new MyAdapter(getApplicationContext(), uploads);
//adding adapter to recyclerview
recyclerView.setAdapter(adapter);
}
@Override
public void onCancelled(DatabaseError databaseError) {
progressDialog.dismiss();
}
});
更多详情可以看我的postFirebase Storage Example.
自 2017 年 3 月以来: 随着 Firebase Cloud Functions 的加入,以及 Firebase 与 Google Cloud 的更深入集成,现在这成为可能。
借助 Cloud Functions,您可以使用 Google Cloud Node 包在 Cloud Storage 上执行 epic 操作。下面的示例将所有文件 URL 从 Cloud Storage 获取到一个数组中。每次将内容保存到 google 云存储时都会触发此功能。
注1:这是一个计算量相当大的操作,因为它必须循环遍历存储桶/文件夹中的所有文件。
注释 2:我只是作为一个例子写的,没有对 promises 等做太多细节。只是提供一个想法。
const functions = require('firebase-functions');
const gcs = require('@google-cloud/storage')();
// let's trigger this function with a file upload to google cloud storage
exports.fileUploaded = functions.storage.object().onChange(event => {
const object = event.data; // the object that was just uploaded
const bucket = gcs.bucket(object.bucket);
const signedUrlConfig = { action: 'read', expires: '03-17-2025' }; // this is a signed url configuration object
var fileURLs = []; // array to hold all file urls
// this is just for the sake of this example. Ideally you should get the path from the object that is uploaded :)
const folderPath = "a/path/you/want/its/folder/size/calculated";
bucket.getFiles({ prefix: folderPath }, function(err, files) {
// files = array of file objects
// not the contents of these files, we're not downloading the files.
files.forEach(function(file) {
file.getSignedUrl(signedUrlConfig, function(err, fileURL) {
console.log(fileURL);
fileURLs.push(fileURL);
});
});
});
});
我希望这会给你一个大概的想法。如需更好的云函数示例,请查看 Google's Github repo full of Cloud Functions samples for Firebase. Also check out their Google Cloud Node API Documentation
使用 Cloud Function 跟踪每个上传的图像并将其存储在数据库中的另一种将图像添加到数据库的方法。
exports.fileUploaded = functions.storage.object().onChange(event => {
const object = event.data; // the object that was just uploaded
const contentType = event.data.contentType; // This is the image Mimme type\
// Exit if this is triggered on a file that is not an image.
if (!contentType.startsWith('image/')) {
console.log('This is not an image.');
return null;
}
// Get the Signed URLs for the thumbnail and original image.
const config = {
action: 'read',
expires: '03-01-2500'
};
const bucket = gcs.bucket(event.data.bucket);
const filePath = event.data.name;
const file = bucket.file(filePath);
file.getSignedUrl(config, function(err, fileURL) {
console.log(fileURL);
admin.database().ref('images').push({
src: fileURL
});
});
});
完整代码在这里: https://gist.github.com/bossly/fb03686f2cb1699c2717a0359880cf84
所以我有一个项目需要从 firebase 存储下载资产,所以我不得不自己解决这个问题。方法如下:
1- 首先,制作一个模型数据,例如 class Choice{}
,其中 class 定义了一个名为 image Name 的字符串变量,因此它会像那样
class Choice {
.....
String imageName;
}
2- 从 database/firebase 数据库中,将图像名称硬编码到对象中,因此如果您有名为 Apple.png 的图像名称,请将对象创建为
Choice myChoice = new Choice(...,....,"Apple.png");
3- 现在,为您的 firebase 存储中的资产获取 link,类似于
gs://your-project-name.appspot.com/
like this one
4- 最后,初始化您的 firebase 存储引用并开始通过这样的循环获取文件
storageRef = storage.getReferenceFromUrl(firebaseRefURL).child(imagePath);
File localFile = File.createTempFile("images", "png");
storageRef.getFile(localFile).addOnSuccessListener(new OnSuccessListener<FileDownloadTask.TaskSnapshot>() {
@Override
public void onSuccess(FileDownloadTask.TaskSnapshot taskSnapshot) {
//Dismiss Progress Dialog\
}
5- 就是这样
对于node js,我使用了这段代码
const Storage = require('@google-cloud/storage');
const storage = new Storage({projectId: 'PROJECT_ID', keyFilename: 'D:\keyFileName.json'});
const bucket = storage.bucket('project.appspot.com'); //gs://project.appspot.com
bucket.getFiles().then(results => {
const files = results[0];
console.log('Total files:', files.length);
files.forEach(file => {
file.download({destination: `D:\${file}`}).catch(error => console.log('Error: ', error))
});
}).catch(err => {
console.error('ERROR:', err);
});
Android 最好的做法是使用 FirebaseUI 和 Glide。
您需要将其添加到您的 gradle/app 上才能获取图书馆。请注意,它上面已经有 Glide 了!
implementation 'com.firebaseui:firebase-ui-storage:4.1.0'
然后在您的代码中使用
// Reference to an image file in Cloud Storage
StorageReference storageReference = FirebaseStorage.getInstance().getReference();
// ImageView in your Activity
ImageView imageView = findViewById(R.id.imageView);
// Download directly from StorageReference using Glide
// (See MyAppGlideModule for Loader registration)
GlideApp.with(this /* context */)
.load(storageReference)
.into(imageView);
实际上这是可能的,但只有 Google Cloud API instead one from Firebase. It's because a Firebase Storage is a Google Cloud Storage Bucket 可以通过 Google 云 API 轻松实现,但是您需要使用 OAuth 进行身份验证而不是 Firebase 的身份验证。
#In Python
import firebase_admin
from firebase_admin import credentials
from firebase_admin import storage
import datetime
import urllib.request
def image_download(url, name_img) :
urllib.request.urlretrieve(url, name_img)
cred = credentials.Certificate("credentials.json")
# Initialize the app with a service account, granting admin privileges
app = firebase_admin.initialize_app(cred, {
'storageBucket': 'YOURSTORAGEBUCKETNAME.appspot.com',
})
url_img = "gs://YOURSTORAGEBUCKETNAME.appspot.com/"
bucket_1 = storage.bucket(app=app)
image_urls = []
for blob in bucket_1.list_blobs():
name = str(blob.name)
#print(name)
blob_img = bucket_1.blob(name)
X_url = blob_img.generate_signed_url(datetime.timedelta(seconds = 300), method='GET')
#print(X_url)
image_urls.append(X_url)
PATH = ['Where you want to save the image']
for path in PATH:
i = 1
for url in image_urls:
name_img = str(path + "image"+str(i)+".jpg")
image_download(url, name_img)
i+=1
我遇到了同样的问题,我的更复杂。
管理员会将音频和 pdf 文件上传到存储中:
audios/season1, season2.../class1, class 2/.mp3 文件
书籍/.pdf 文件
Android应用程序需要获取子文件夹和文件列表。
解决方案是在存储上捕获上传事件并使用云函数在 firestore 上创建相同的结构。
第 1 步:在 firestore
上手动创建 'storage' 集合和 'audios/books' 文档第 2 步:设置云功能
可能需要大约 15 分钟:https://www.youtube.com/watch?v=DYfP-UIKxH0&list=PLl-K7zZEsYLkPZHe41m4jfAxUi0JjLgSM&index=1
第 3 步:使用云函数捕获上传事件
import * as functions from 'firebase-functions';
import * as admin from 'firebase-admin';
admin.initializeApp(functions.config().firebase);
const path = require('path');
export const onFileUpload = functions.storage.object().onFinalize(async (object) => {
let filePath = object.name; // File path in the bucket.
const contentType = object.contentType; // File content type.
const metageneration = object.metageneration; // Number of times metadata has been generated. New objects have a value of 1.
if (metageneration !== "1") return;
// Get the file name.
const fileName = path.basename(filePath);
filePath = filePath.substring(0, filePath.length - 1);
console.log('contentType ' + contentType);
console.log('fileName ' + fileName);
console.log('filePath ' + filePath);
console.log('path.dirname(filePath) ' + path.dirname(filePath));
filePath = path.dirname(filePath);
const pathArray = filePath.split("/");
let ref = '';
for (const item of pathArray) {
if (ref.length === 0) {
ref = item;
}
else {
ref = ref.concat('/sub/').concat(item);
}
}
ref = 'storage/'.concat(ref).concat('/sub')
admin.firestore().collection(ref).doc(fileName).create({})
.then(result => {console.log('onFileUpload:updated')})
.catch(error => {
console.log(error);
});
});
第 4 步:使用 firestore
在 Android 应用程序上检索 folders/files 的列表private static final String STORAGE_DOC = "storage/";
public static void getMediaCollection(String path, OnCompleteListener onCompleteListener) {
String[] pathArray = path.split("/");
String doc = null;
for (String item : pathArray) {
if (TextUtils.isEmpty(doc)) doc = STORAGE_DOC.concat(item);
else doc = doc.concat("/sub/").concat(item);
}
doc = doc.concat("/sub");
getFirestore().collection(doc).get().addOnCompleteListener(onCompleteListener);
}
第 5 步:下载 url
public static void downloadMediaFile(String path, OnCompleteListener<Uri> onCompleteListener) {
getStorage().getReference().child(path).getDownloadUrl().addOnCompleteListener(onCompleteListener);
}
备注
我们必须将“子”集合放入每个项目,因为 firestore 不支持检索集合列表。
我花了 3 天的时间才找到解决方案,希望最多能花你 3 个小时。
自 2019 年 5 月起,version 6.1.0 的 Firebase SDK for Cloud Storage 现在支持列出存储桶中的所有对象。您只需要在 Reference
:
listAll()
// Since you mentioned your images are in a folder,
// we'll create a Reference to that folder:
var storageRef = firebase.storage().ref("your_folder");
// Now we get the references of these images
storageRef.listAll().then(function(result) {
result.items.forEach(function(imageRef) {
// And finally display them
displayImage(imageRef);
});
}).catch(function(error) {
// Handle any errors
});
function displayImage(imageRef) {
imageRef.getDownloadURL().then(function(url) {
// TODO: Display the image on the UI
}).catch(function(error) {
// Handle any errors
});
}
请注意,为了使用此功能,您必须opt-in到version 2 of Security Rules,这可以通过rules_version = '2';
来完成您的安全规则的第一行:
rules_version = '2';
service firebase.storage {
match /b/{bucket}/o {
match /{allPaths=**} {
我建议查看 docs 以供进一步参考。
此外,根据 setup,在第 5 步,Node.js
不允许此脚本,因为 require("firebase/app");
不会 return firebase.storage()
作为一个功能。这只能使用 import * as firebase from 'firebase/app';
.
扩展 Rosário Pereira Fernandes 的 答案,JavaScript 解决方案:
- 在你的机器上安装 firebase
npm install -g firebase-tools
- 在 firebase init 上将
JavaScript
设置为默认语言 - 在创建项目的根目录下执行 npm installs
npm install --save firebase
npm install @google-cloud/storage
npm install @google-cloud/firestore
... <any other dependency needed>
- 在项目中添加非默认依赖项,例如
"firebase": "^6.3.3",
"@google-cloud/storage": "^3.0.3"
functions/package.json
{
"name": "functions",
"description": "Cloud Functions for Firebase",
"scripts": {
"lint": "eslint .",
"serve": "firebase serve --only functions",
"shell": "firebase functions:shell",
"start": "npm run shell",
"deploy": "firebase deploy --only functions",
"logs": "firebase functions:log"
},
"engines": {
"node": "10"
},
"dependencies": {
"@google-cloud/storage": "^3.0.3",
"firebase": "^6.3.3",
"firebase-admin": "^8.0.0",
"firebase-functions": "^3.1.0"
},
"devDependencies": {
"eslint": "^5.12.0",
"eslint-plugin-promise": "^4.0.1",
"firebase-functions-test": "^0.1.6"
},
"private": true
}
- 创建某种
listAll
函数
index.js
var serviceAccount = require("./key.json");
const functions = require('firebase-functions');
const images = require('./images.js');
var admin = require("firebase-admin");
admin.initializeApp({
credential: admin.credential.cert(serviceAccount),
databaseURL: "https://<my_project>.firebaseio.com"
});
const bucket = admin.storage().bucket('<my_bucket>.appspot.com')
exports.getImages = functions.https.onRequest((request, response) => {
images.getImages(bucket)
.then(urls => response.status(200).send({ data: { urls } }))
.catch(err => console.error(err));
})
images.js
module.exports = {
getImages
}
const query = {
directory: 'images'
};
function getImages(bucket) {
return bucket.getFiles(query)
.then(response => getUrls(response))
.catch(err => console.error(err));
}
function getUrls(response) {
const promises = []
response.forEach( files => {
files.forEach (file => {
promises.push(getSignedUrl(file));
});
});
return Promise.all(promises).then(result => getParsedUrls(result));
}
function getSignedUrl(file) {
return file.getSignedUrl({
action: 'read',
expires: '09-01-2019'
})
}
function getParsedUrls(result) {
return JSON.stringify(result.map(mediaLink => createMedia(mediaLink)));
}
function createMedia(mediaLink) {
const reference = {};
reference.mediaLink = mediaLink[0];
return reference;
}
- 执行
firebase deploy
上传你的云函数 - Call 您的应用中的自定义功能
build.gradle
dependencies {
...
implementation 'com.google.firebase:firebase-functions:18.1.0'
...
}
kotlin class
private val functions = FirebaseFunctions.getInstance()
val cloudFunction = functions.getHttpsCallable("getImages")
cloudFunction.call().addOnSuccessListener {...}
关于此功能的进一步开发,我运行 提出了一些可能发现的问题
您可以通过listAll() 方法列出firebase 存储目录中的文件。 要使用此方法,必须实施此版本的 firebase 存储。 'com.google.firebase:firebase-storage:18.1.1'
https://firebase.google.com/docs/storage/android/list-files
请记住将安全规则升级到版本 2。
我正在使用 AngularFire
并使用以下命令获取所有 downloadURL
getPhotos(id: string): Observable<string[]> {
const ref = this.storage.ref(`photos/${id}`)
return ref.listAll().pipe(switchMap(list => {
const calls: Promise<string>[] = [];
list.items.forEach(item => calls.push(item.getDownloadURL()))
return Promise.all(calls)
}));
}
结合这个 post 和 firebase-admin
:
import * as admin from 'firebase-admin';
const getFileNames = () => {
admin.storage().bucket().getFiles(autoPaginate: false).then(([files]: any) => {
const fileNames = files.map((file: any) => file.name);
return fileNames;
})
}
就我而言,我还需要从 firebase 存储中获取特定文件夹中的所有文件。根据 google storage 文件夹不存在,而是一种命名约定。无论如何,我通过在 getFiles
函数调用中添加 { prefix: ${folderName}, autoPaginate: false }
来设法做到这一点(没有将每个文件的完整路径保存到数据库中)所以:
...
const getFileNames = (folderName: string) => {
admin.storage().bucket().getFiles({ prefix: `${folderName}`, autoPaginate: false })
.then(([files]: any) => {
...
在Swift
public func downloadData() async {
let imagesRef = storage.child("pictures/")
do {
let storageReference = try await storage.root().child("pictures").listAll()
print("storageReference: \(storageReference.items)")
} catch {
print(error)
}
}
输出
[
gs://<your_app_name>.appspot.com/pictures/IMG_1243.JPG,
gs://<your_app_name>.appspot.com/pictures/IMG_1244.JPG,
gs://<your_app_name>.appspot.com/pictures/IMG_1245.JPG,
gs://<your_app_name>.appspot.com/pictures/IMG_1246.JPG
]