Baseflow / flutter_cache_manager

Generic cache manager for flutter
https://baseflow.com
MIT License
739 stars 427 forks source link

When retrieving the cache data few of the special characters are getting corrupted #394

Open Vashista-Puram opened 1 year ago

Vashista-Puram commented 1 year ago

🐛 Bug Report

When retrieving the cache data few of the special characters are getting corrupted

Example char ==== `

Expected behavior

The data should not be replaced with junk data

Reproduction steps

insert some string containing ` into the cache and then read it back

Configuration

Version: 3.3.0

Platform: Android and iOS

Example code -

Converting Uint8List to Uint32List may fix the issue

class AppCacheManager {
  static const _CACHE_KEY = 'AppCacheKey';

  /// Maximum number of Cache files saved. If it exceeds the count, files which haven't been used for the longest time will be deleted
  static const _MAX_NO_OF_CACHE_OBJECT = 50;

  /// Time Duration for a which a file is considered Stale. After this duration file will be deleted.
  static const _STALE_PERIOD = const Duration(days: 7);

  CacheManager? _cacheManager;
  static AppCacheManager? _instance;

  AppCacheManager._() {
    _cacheManager = (isNativeApp()
        ? CacheManager(Config(_CACHE_KEY, stalePeriod: _STALE_PERIOD, maxNrOfCacheObjects: _MAX_NO_OF_CACHE_OBJECT))
        : null);
  }

  factory AppCacheManager() {
    _instance ??= AppCacheManager._();
    return _instance!;
  }

  /// This method is responsible to return http.StreamedResponse
  /// return null in case (file doesn't exist or file validity is expired)
  Future<http.StreamedResponse?> getStreamedResponse(String key, http.BaseRequest request,
      {bool ignoreMemCache = false}) async {
    if (isNativeApp()) {
      var cachedFileInfo = await getFileInfoFromCache(key, ignoreMemCache: ignoreMemCache);
      if (cachedFileInfo?.file == null) return Future.value(null);
      Stream stream = cachedFileInfo!.file.openRead();
      return http.StreamedResponse(
        stream as Stream<List<int>>,
        HttpStatus.ok,
        request: request,
      );
    }
    return Future.value(null);
  }

  /// This method is responsible to return FileInfo and will also remove the file in case file validity is expired
  /// return null in case (Platform is Web, file doesn't exist, file validity is expired)
  Future<FileInfo?> getFileInfoFromCache(String key, {bool ignoreMemCache = false}) async {
    /// No FileInfo will be returned in case of Web
    if (isNativeApp()) {
      var cachedFileInfo = await _cacheManager?.getFileFromCache(key, ignoreMemCache: ignoreMemCache);

      /// return null value when cache not found
      if (cachedFileInfo?.file == null) {
        return Future.value(null);
      }
      bool isValidityExpired = _isFileValidityExpired(cachedFileInfo!);

      /// remove cached file if validity is expired
      if (isValidityExpired) {
        _cacheManager?.removeFile(key);
        return Future.value(null);
      }
      return Future.value(cachedFileInfo);
    }
    return Future.value(null);
  }

  /// method to check file validity
  bool _isFileValidityExpired(FileInfo cachedFileInfo) {
    return DateTime.now().isAfter(cachedFileInfo.validTill);
  }

  /// This method is responsible for saving input stream into cache
  /// Web Platform not Supported, hence will return null
  Future? cacheFile(String url, Stream stream, int validTimeInMilliSeconds) async {
    if (isNativeApp()) {
      Uint8List bytes = await _toByteStream(stream as Stream<List<int>>).toBytes();

      /// Saving the response to File
      await _cacheManager?.putFile(
        url,
        bytes,
        maxAge: Duration(milliseconds: validTimeInMilliSeconds),
      );

      /// Finished saving the response to file
    }
    return null;
  }

  http.ByteStream _toByteStream(Stream<List<int>> stream) {
    if (stream is http.ByteStream) return stream;
    return http.ByteStream(stream);
  }

  Future<void> clearCache() {
    return _cacheManager?.emptyCache() ?? Future.value(null);
  }
}