如何使用 Flutter 列出我的 Firebase 存储中的所有路径和目录?

How do I list all the paths and directories in my Firebase Storage with Flutter?

我正在使用 Flutter,在我的 Firebase 存储中我有一个复杂的文件和目录架构。我想列出 Firebase 存储中包含的所有文件(包括它们的路径),以便下载它们并将它们存储在设备的本地存储中。

我知道如何列出一个文件夹中的所有文件,但是如何在不知道这些文件夹名称的情况下同时列出所有文件夹中的文件?

例如,我的架构可能如下所示:

MainFolder
  -- Folder2
    -- Folder3
      -- file.json
    -- Folder4
      -- file2.json
AnotherMainFolder
  -- file3.json

我的 Firebase 存储中的结构是动态的(我不知道其中的名称和目录数量)。

到目前为止,我已经成功编写了以下代码:

class DownloadFirebaseApi {
  static Future<List<FirebaseFile>> listAll() async {
    // here, I should specify a path `ref(path)`
    // however I don't want to because
    // I want all the folders and all the files
    // no matter in which directory they are
    final ref = FirebaseStorage.instance.ref();
    final result = await ref.listAll();
    final urls = await _getDownloadLinks(result.items);
    return urls
        .asMap()
        .map((index, url) {
          final ref = result.items[index];
          final name = ref.name;
          final file = FirebaseFile(name: name, url: url, ref: ref);
          return MapEntry(index, file);
        })
        .values
        .toList();
  }

  static Future<List<String>> _getDownloadLinks(List<Reference> refs) async {
    return Future.wait(refs.map((ref) {
      var url = ref.getDownloadURL();
      return ref.getDownloadURL();
    }).toList());
  }
}

listAll()是否有我不知道的递归方法?

当您调用 listAll 时,结果有两个列表:

  1. result.items,其中包含根目录中的所有单个文件。
  2. result.prefixes,其中包含所有目录(在 Cloud Storage 中通常称为“前缀”)。

您还需要遍历 result.prefixes,然后依次调用 listAll 以获得完整的递归文件列表。

另请参阅 listing files and directories 上的 FlutterFire 文档。

对于那些有兴趣知道我是如何编写代码的人(感谢 Frank van Puffelen 的回答)

import 'package:firebase_storage/firebase_storage.dart';

class FirebaseFile {
  final Reference ref;
  final String url;
  final String name;

  FirebaseFile({
    required this.ref,
    required this.url,
    required this.name,
  });

  @override
  String toString() {
    return 'file "$name"';
  }
}

class DownloadFirebaseApi {
  /// Transform the items (the files) contained in the [result] (a folder).
  /// The items are transformed into a list of [FirebaseFile] objects.
  static Future<List<FirebaseFile>> _getFilesFrom(ListResult result) async {
    List<String> urls = await _getDownloadLinks(result.items);
    return urls
        .asMap()
        .map((index, url) {
          final ref = result.items[index];
          final name = ref.name;
          final file = FirebaseFile(name: name, url: url, ref: ref);
          return MapEntry(index, file);
        })
        .values
        .toList();
  }

  /// Explores all the nested directories and
  /// returns a list of all the files as [FirebaseFile] objects.
  static Future<List<FirebaseFile>> _exploreDirectories(
    ListResult result,
  ) async {
    List<FirebaseFile> files = [];
    if (result.prefixes.isEmpty) {
      if (result.items.isEmpty) {
        return [];
      } else {
        return await _getFilesFrom(result);
      }
    } else {
      for (Reference prefix in result.prefixes) {
        ListResult nestedResult = await prefix.listAll();
        if (nestedResult.items.isNotEmpty) {
          files.addAll(await _getFilesFrom(nestedResult));
        }
        if (nestedResult.prefixes.isNotEmpty) {
          files.addAll(await _exploreDirectories(nestedResult));
        }
      }
    }
    return files;
  }

  /// Returns a list of all the files as [FirebaseFile] objects.
  static Future<List<FirebaseFile>> listAll() async {
    final ref = FirebaseStorage.instance.ref();
    List<FirebaseFile> files = [];
    ListResult result = await ref.listAll();
    if (result.prefixes.isNotEmpty) {
      files = await _exploreDirectories(result);
    } else if (result.items.isNotEmpty) {
      files = await _getFilesFrom(result);
    } else {
      return [];
    }

    return files;
  }

  /// Gets the download URL of every file with their [refs].
  static Future<List<String>> _getDownloadLinks(List<Reference> refs) async {
    return Future.wait(refs.map((ref) => ref.getDownloadURL()).toList());
  }
}