llfbandit / dio_cache_interceptor

Dio HTTP cache interceptor with multiple stores respecting HTTP directives (ETag, Last-Modified, Cache-Control) with options.
https://pub.dev/packages/dio_cache_interceptor
120 stars 70 forks source link

Getting empty data from cache #136

Closed realchandan closed 1 year ago

realchandan commented 1 year ago

Hi @llfbandit ,

I'm facing a bug where I am getting empty data/no data (not null though) from the dio response.data field.

I'm trying to fetch gifs from giphy and cache them for later use.

I'm attaching two screenshots, In the first one you can see the gif loads properly when it hits the actual giphy servers and fetches the data, but in the other screenshot, you can see the debugger and data field of response is an empty array.

I'm attaching minimum reproducible code below -

<uses-permission android:name="android.permission.INTERNET"/>
dependencies:
  flutter:
    sdk: flutter
  cupertino_icons: ^1.0.2
  dio_cache_interceptor: ^3.4.4
  dio_cache_interceptor_file_store: ^1.2.2
  dio: ^5.3.3
  path_provider: ^2.1.1
import 'dart:io';
import 'dart:typed_data';

import 'package:dio/dio.dart';
import 'package:dio_cache_interceptor/dio_cache_interceptor.dart';
import 'package:dio_cache_interceptor_file_store/dio_cache_interceptor_file_store.dart';
import 'package:flutter/material.dart';
import 'package:path_provider/path_provider.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  static Dio? cacheDio;

  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
        title: 'Flutter Demo',
        theme: ThemeData(
          colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
          useMaterial3: true,
        ),
        debugShowCheckedModeBanner: false,
        home: Scaffold(
          body: SafeArea(
              child: Center(
                  child: FutureBuilder<Uint8List?>(
            future: safelyGetGifs("5dYbT8yNjS23ZLWzhd"),
            builder: (context2, snapshot) {
              if (snapshot.hasData && snapshot.data != null) {
                return Image.memory(snapshot.data!, width: 200, height: 200);
              }

              return Container();
            },
          ))),
        ));
  }

  Future<Dio> cachedHttpClient() async {
    if (MyApp.cacheDio == null) {
      MyApp.cacheDio = Dio(
        BaseOptions(
          validateStatus: (_) => true,
        ),
      );

      Directory directory = await getApplicationDocumentsDirectory();

      final cacheOptions = CacheOptions(
        store: FileCacheStore("${directory.path}/networkcache"),
        policy: CachePolicy.request,
        hitCacheOnErrorExcept: [401, 403],
        maxStale: const Duration(days: 30),
      );

      MyApp.cacheDio!.interceptors
          .add(DioCacheInterceptor(options: cacheOptions));

      MyApp.cacheDio!.options.responseType = ResponseType.bytes;
    }

    return MyApp.cacheDio!;
  }

  Future<Uint8List?> safelyGetGifs(String? giphyId) async {
    if (giphyId == null) return null;

    String url = "https://media1.giphy.com/media/$giphyId/200.gif";

    var response = await sendGetRequestCacheClient(url);

    if (response.statusCode! >= 200 && response.statusCode! < 400) {
      try {
        // Bug
        if (response.data is List && response.data.isNotEmpty) {
          return Uint8List.fromList(response.data);
        }
      } catch (_) {}
    }

    return null;
  }

  Future<Response> sendGetRequestCacheClient(String url) async {
    var t = await cachedHttpClient();
    return t.get(url);
  }
}

I am waiting to hear from you! Thank You!

Screenshot1 Screenshot2

llfbandit commented 1 year ago

I tried to reproduce this with this URL: https://media1.giphy.com/media/sG8gmIFd6G7xHdQE9y/200.gif

In the response, we get: cache-control: no-store, private, max-age=0, no-cache, must-revalidate, s-maxage=0

This is why the cache store is empty.

If you want to force cache, you must use the dedicated policies and handle it by yourself.

realchandan commented 1 year ago

It works fine, when using CachePolicy.forceCache

I can't believe I have spend so many hours on this (reading the source code and debugging the code)

I have ended up using cached_network_image thus It's no longer an issue for me!

Thank You! @llfbandit