flutter_cached_network_image icon indicating copy to clipboard operation
flutter_cached_network_image copied to clipboard

The libarary is not respecting the cache-control headers

Open moham96 opened this issue 3 years ago • 1 comments

🐛 Bug Report

Expected behavior

on the server side I'm sending these headers along with the image file:

{
'cache-control': 'no-cache,max-age=1',
'etag': hash,
'age': '1',
'content-type': 'image/jpeg'
}

so what I expect is that each time I call CachedNetworkImageProvider the app would send a get request with an if-none-match to check if the ETag of the cached file was changed, but I'm not seeing this request on the server side, note that if I use the flutter_cache_manager package directly(through getSingleFile) with Image.memory then it works as expected, somehow it just breaks when using the flutter_cached_network_image package.

Reproduction steps

  • Start a backend server that sends the following cache control HTTP header with 'no-cache' which should force the cache manager to validate the cached file each time it is requested:
import 'dart:io';
import 'dart:typed_data';
import "package:path/path.dart" show dirname, join;
import 'package:shelf/shelf.dart' as shelf;
import 'package:shelf/shelf_io.dart' as io;
import 'package:crypto/crypto.dart' as crypto;
import 'package:shelf_router/shelf_router.dart' as shelf_router;

shelf_router.Router router = shelf_router.Router();
late HttpServer server;
String generateMd5(Uint8List data) {
  crypto.Hash md5 = crypto.md5;

  crypto.Digest digest = md5.convert(data);
  return digest.toString();
}

void main(List<String> args) async {
  server = await io.serve(router, InternetAddress.anyIPv4, 8081);
  router.get('/avatar/<key>', avatarCallback);
}

avatarCallback(shelf.Request request, String key) async {
  print("A request was made");
  print(request.headers);
  Uint8List avatar =
      File(join(dirname(Platform.script.path), 'avatar.png')).readAsBytesSync();
  String hash = generateMd5(avatar);
  Map<String, Object> responseHeaders = {
    'cache-control': 'no-cache,max-age=1',
    'etag': hash,
    'age': '1',
    'content-type': 'image/png'
  };
  if (request.headers.containsKey('if-none-match')) {
    print('found if-none-match');
    print('request hash: ${request.headers['if-none-match']}');
    print("local hash:  ${hash}");
    if (request.headers['if-none-match'] == hash) {
      return shelf.Response(304, headers: responseHeaders);
    }
  }
  return shelf.Response.ok(avatar, headers: responseHeaders);
}

I have added a print statement in a place where it is called each time a request is made to the file .

  • below is a an example flutter app to show the problem:
import 'package:flutter/material.dart';
import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter_cache_manager/flutter_cache_manager.dart';

BaseCacheManager def = DefaultCacheManager();

void main() async {
  WidgetsFlutterBinding.ensureInitialized();

  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      theme: ThemeData.dark(),
      home: HomePage(),
    );
  }
}

class HomePage extends StatefulWidget {
  HomePage({
    Key? key,
  }) : super(key: key);

  @override
  State<HomePage> createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  String imageUrl = 'http://192.168.88.32:8081/avatar/0';
  List<Widget> imagesChildren = [];

  @override
  void initState() {
    // TODO: implement initState
    super.initState();
  }

  @override
  void dispose() {
    // TODO: implement dispose
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    print('rebuilding');
    print(imagesChildren.length);
    return Scaffold(
      body: SafeArea(
        child: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.start,
            children: [
              TextButton(
                onPressed: (() async {
                  Widget wid;
                  wid = Image.memory(
                    await def
                        .getSingleFile(imageUrl)
                        .then((value) => value.readAsBytesSync()),
                    width: 50,
                    height: 50,
                  );

                  setState(() {
                    imagesChildren.add(wid);
                  });
                }),
                child: Text('add using cache manager'),
              ),
              TextButton(
                onPressed: (() {
                  Widget wid;
                  wid = Image(
                    width: 50,
                    height: 50,
                    image: CachedNetworkImageProvider(imageUrl),
                  );
                  setState(() {
                    imagesChildren.add(wid);
                  });
                }),
                child: const Text('add using CachedNetworkImageProvider'),
              ),
              ListView(
                scrollDirection: Axis.vertical,
                shrinkWrap: true,
                children: [...imagesChildren],
              ),
            ],
          ),
        ),
      ),
    );
  }
}

  • start the server and the flutter app, notice that when you click ' add using cache manager' multiple times, it works as expected(the app sends a request with the cached file's hash to check if the resource was updated, while if you click on the 'add using CachedNetworkImageProvider' multiple times, it only request the file in the first time, then it always shows the cached file and doesn't check the server for updates.

Configuration

Version: 1.x

Platform:

  • [ x] :iphone: iOS
  • [x ] :robot: Android

moham96 avatar Aug 20 '22 10:08 moham96

Not a single reaction since August 2022? I have the same problem: When I update the image behind the same url, the server (firebase storage) sends a changed etag and last-modified header, but flutter_cached_network_image will not refetch the image. So if the image (profile image) is being used before stalePeriod exceeds, the image will never be updated.

justChris avatar Jan 04 '25 07:01 justChris