颤振:如果Firebase存储中的JSON文件更新,如何使用Firebase存储获取新数据

颤振:如果Firebase存储中的JSON文件更新,如何使用Firebase存储获取新数据,json,firebase,flutter,dart,firebase-storage,Json,Firebase,Flutter,Dart,Firebase Storage,我目前正在通过调用Firebase存储中的JSON文件来显示数据,但我不希望每次都下载JSON文件来显示数据=>我将检查Firebase存储中的JSON文件是否已更改: 如果更改=>将新的JSON文件下载到本地目录并显示它 否则=>在本地目录中显示旧JSON文件(第一次打开应用程序时将下载此旧JSON文件) 关于JSON文件 这是我将JSON上传到Firebase存储后的JSON链接: 据我所知,此链接由两部分组成: 第一部分:https://firebasestorage.google

我目前正在通过调用Firebase存储中的JSON文件来显示数据,但我不希望每次都下载JSON文件来显示数据=>我将检查Firebase存储中的JSON文件是否已更改:

  • 如果更改=>将新的JSON文件下载到本地目录并显示它
  • 否则=>在本地目录中显示旧JSON文件(第一次打开应用程序时将下载此旧JSON文件)

关于JSON文件 这是我将JSON上传到Firebase存储后的JSON链接:

据我所知,此链接由两部分组成:

第一部分
https://firebasestorage.googleapis.com/v0/b/tft-test-48c87.appspot.com/o/loadData.json

最后一部分
?alt=media&token=
+
2e3d416-62dc-4137-93a3-59ade95ac38f
(它是第一部分中字符串“downloadTokens”的值

在链接的第一部分中,有关于JSON文件的所有信息,特别是我认为字符串“updated”的值可以用作下载文件的条件

例如,“更新”:“2020-08-04T14:30:10.920Z”,

每次我上传一个与旧JSON文件同名的新JSON文件时,更新的字符串的值都会改变,但链接下载不会改变


台阶 因此,我想做以下工作:

  • 创建文件以将字符串“更新”存储在本地目录中(例如“更新”:null),并在下载到本地目录后将JSON文件存储在哪里
  • 开放应用程序
  • 选中链接第一部分中的字符串“已更新”:
    • 案例A:如果第一部分中字符串的值“已更新”
      =本地目录中字符串“已更新”的值

      • 步骤1:将JSON文件(通过链接:
        First part
        +
        ?alt=media&token=
        +
        downloadtokes
        )下载到本地目录(如果旧的JSON文件已经存在,它将被替换)
      • 步骤2:用Firebase存储中字符串“updated”的值覆盖本地目录中字符串“updated”的值
      • 步骤3:访问本地目录中的JSON文件以显示数据
    • 案例B:如果第一部分中字符串“更新”的值
      =
      本地目录中字符串“更新”的值
      =>不做任何操作,只需访问本地目录中的JSON文件即可显示数据


    我知道一篇文章有很多问题,我是一个代码新手,如果我把它分成几篇文章,那么我很难把它们结合起来。所以我希望答案有完整的代码,这将是伟大的。谢谢这是主文件:

    import 'package:ask/model/load_data_model.dart';
    import 'package:flutter/material.dart';
    import 'package:http/http.dart' as http;
    
    class LoadDataPage extends StatefulWidget {
      @override
      _LoadDataPageState createState() => _LoadDataPageState();
    }
    
    class DataServices {
      static const String url = 'https://firebasestorage.googleapis.com/v0/b/tft-test-48c87.appspot.com/o/loadData.json?alt=media&token=92e3d416-62dc-4137-93a3-59ade95ac38f';
    
      static Future<List<Data>> getData() async {
        try {
          final response = await http.get(url);
          if (200 == response.statusCode) {
            final List<Data> data = dataFromJson(response.body);
            return data;
          } else {
            return List<Data>();
          }
        } catch (e) {
          return List<Data>();
        }
      }
    }
    
    class _LoadDataPageState extends State<LoadDataPage> {
      @override
      Widget build(BuildContext context) {
        return Scaffold(
            appBar: AppBar(title: Text('Load Data')),
            body: FutureBuilder(
                future: DataServices.getData(),
                builder: (BuildContext context, AsyncSnapshot snapshot) {
                  List<Widget> children;
                  List<Data> _data = snapshot.data;
                  if (snapshot.hasData) {
                    return ListView.builder(
                      itemCount: _data.length,
                      itemBuilder: (context, index) {
                        return Column(
                          children: [Text(_data[index].data)],
                        );
                      },
                    );
                  } else {
                    children = <Widget>[SizedBox(child: CircularProgressIndicator(), width: 60, height: 60), const Padding(padding: EdgeInsets.only(top: 16), child: Text('Loading...'))];
                  }
                  return Center(child: Column(mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, children: children));
                }));
      }
    }
    
    
    import'package:ask/model/load_data_model.dart';
    进口“包装:颤振/材料.省道”;
    将“package:http/http.dart”导入为http;
    类LoadDataPage扩展StatefulWidget{
    @凌驾
    _LoadDataPageState createState()=>\u LoadDataPageState();
    }
    类数据服务{
    静态常量字符串url=https://firebasestorage.googleapis.com/v0/b/tft-test-48c87.appspot.com/o/loadData.json?alt=media&token=92e3d416-62dc-4137-93a3-59ade95ac38f';
    静态未来getData()异步{
    试一试{
    最终响应=等待http.get(url);
    if(200==响应.状态码){
    最终列表数据=dataFromJson(response.body);
    返回数据;
    }否则{
    返回列表();
    }
    }捕获(e){
    返回列表();
    }
    }
    }
    类_LoadDataPageState扩展状态{
    @凌驾
    小部件构建(构建上下文){
    返回脚手架(
    appBar:appBar(标题:文本(“加载数据”)),
    正文:未来建设者(
    future:DataServices.getData(),
    生成器:(BuildContext上下文,异步快照){
    列出儿童名单;
    列表_data=snapshot.data;
    if(snapshot.hasData){
    返回ListView.builder(
    itemCount:_data.length,
    itemBuilder:(上下文,索引){
    返回列(
    子项:[Text(_data[index].data)],
    );
    },
    );
    }否则{
    children=[SizedBox(child:CircularProgressIndicator(),宽度:60,高度:60),常量填充(Padding:EdgeInsets.only(top:16),child:Text('Loading…))];
    }
    返回中心(子项:列(mainAxisAlignment:mainAxisAlignment.Center,crossAxisAlignment:crossAxisAlignment.Center,子项:子项));
    }));
    }
    }
    

    另一个步骤 EdwynZN的回答对我来说非常有效,但是,我编辑了这篇文章,添加了一个案例,我认为这将使加载页面尽快完成,所以请再次帮助我:

    打开页面=>
    readFile
    compareLastUpdate
    >
    \u lastUpdateDB
    &
    \u createFile

    • 案例A:应用程序第一次打开=>
      readFile
      :false>
      \u lastUpdateDB
      &
      \u createFile
      readFile
    • 案例B:不是第一次打开应用程序:
      • 数据仍然立即从旧的JSON加载,同时在后台运行:
        compareLastUpdate
        • 如果更新时间相同=>则不执行任何操作
        • 如果更新时间不同=>
          \u lastUpdateDB
          &
          \u createFile
    p/S:使用此流程,第二次打开页面时,将显示新数据,对吗?但是我想知道,如果在新的JSON文件之后使用
    StatefulWidget
    =>是
    import 'package:shared_preferences/shared_preferences.dart';
    import 'package:path_provider/path_provider.dart';
    import 'dart:convert';
    
    
    /// Move them outside of the class as Top Level functions
    List<Data> readFile(File file) {
      try{
        String data = file.readAsStringSync();
        return dataFromJson(data);
      } catch(e){
        print(e.toString());
        return List<Data>(); // or return an empty list, up to you
      }
    }
    
    // No need of encoder now because response body is already a String
    void writeFile(Map<String, dynamic> arg) =>
      arg['file']?.writeAsStringSync(arg['data'], flush: true);
    
    class DataServices {
    
      DateTime dateApi;
    
      static const String url = 'https://firebasestorage.googleapis.com/v0/b/tft-test-48c87.appspot.com/o/loadData.json?alt=media&token=92e3d416-62dc-4137-93a3-59ade95ac38f';
      static const String urlUpdate = 'https://firebasestorage.googleapis.com/v0/b/tft-test-48c87.appspot.com/o/loadData.json';
    
      Future<List<Data>> getData() async {
        bool update = await compareLastUpdate;
        if(update) { // that means the update times are the same, so retrieving form json file is better than doing http request
           final file  = await _createFile();
           if(await file.exists()) return await compute(readFile, file);
           else return null; //or an empty List
           // If it doesn't exists (probably first time running the app)
           // then retrieve an empty list, null or check how to fill the list from somewhere else
        }
        try {
          final response = await http.get(url);
          final SharedPreferences preferences = await SharedPreferences.getInstance();
          if (200 == response.statusCode) {
            final String utfData = utf8.decode(response.bodyBytes); //just decode it yourself instead of using response.body which uses [latin1] by default
            final List<Data> data = await compute(dataFromJson, utfData);
            final file  = await _createFile();
            Map<String, dynamic> args = {
              'file': file,
              'data': utfData
              //'data': response.body // pass the return body instead of the data
            };
            await compute(writeFile, args);
            await preferences.setString('updateDate', dateApi.toString()); //Save the new date
            return data;
          } else {
            return List<Data>();
          }
        } catch (e) {
          return List<Data>();
        }
      }
    
     File _createFile() async{
       Directory tempDir = await getTemporaryDirectory(); // or check for a cache dir also
       return File('${tempDir.path}/Data.json');
     }
    
    
    Future<bool> get compareLastUpdate async{
      final dateCache = await _lastUpdateDB;
      dateApi = await _lastUpdateApi;
    
      if(dateCache == null) return false;    
      return dateApi?.isAtSameMomentAs(dateCache) ?? false; // or just isAfter()
      // If dateApi is null (an error conection or some throw) just return false or throw an error and 
      // catch it somewhere else (and give info to the user why it couldn't update)
    }
    
    Future<DateTime> get _lastUpdateApi async{
      try {
         final response = await http.get(urlUpdate);
         DateTime dateTime;
         if (200 == response.statusCode) {
           final data = jsonDecode(response.body));
           dateTime = DateTime.tryParse(data['updated'] ?? '');
         } 
         return dateTime;
       } catch (e) {
         return null;
       }
    }
    
      Future<DateTime> get _lastUpdateDB async{
        final SharedPreferences preferences = await SharedPreferences.getInstance();
        return DateTime.tryParse(preferences.getString('updateDate') ?? ''); // Or if it's null use an old date
        // The first time the app opens there is no updateDate value, so it returns null, if that
        // happens replace it by an old date, one you know your api will be always newer,
        // Ex: 1999-08-06 02:07:53.973 Your Api/App didn't even exist back then
        // Or just use an empty String so the tryParser returns null
      }
    }
    
    class _LoadDataPageState extends State<LoadDataPage> {
      final DataServices services = DataServices();
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
            appBar: AppBar(title: Text('Load Data')),
            body: FutureBuilder(
                future: services.getData(),
                builder: (BuildContext context, AsyncSnapshot snapshot) {
                  List<Widget> children;
                  List<Data> _data = snapshot.data;
                  if (snapshot.hasData) {
                    return ListView.builder(
                      itemCount: _data.length,
                      itemBuilder: (context, index) {
                        return Column(
                          children: [Text(_data[index].data)],
                        );
                      },
                    );
                  } else {
                    children = <Widget>[SizedBox(child: CircularProgressIndicator(), width: 60, height: 60), const Padding(padding: EdgeInsets.only(top: 16), child: Text('Loading...'))];
                  }
                  return Center(child: Column(mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, children: children));
                }));
      }
    }