flutter_cache_manager本地缓存之Config、FileService、FileServiceResponse源码解析(三)

303 阅读5分钟

Config抽象类

/// Config file for the CacheManager.
/// [cacheKey] is used for the folder to store files and for the database
/// file name.
/// [stalePeriod] is the time duration in which a cache object is
/// considered 'stale'. When a file is cached but not being used for a
/// certain time the file will be deleted.
/// [maxNrOfCacheObjects] defines how large the cache is allowed to be. If
/// there are more files the files that haven't been used for the longest
/// time will be removed.
/// [repo] is the [CacheInfoRepository] which stores the cache metadata. On
/// Android, iOS and macOS this defaults to [CacheObjectProvider], a
/// sqflite implementation due to legacy. On web this defaults to
/// [NonStoringObjectProvider]. On the other platforms this defaults to
/// [JsonCacheInfoRepository].
/// The [fileSystem] defines where the cached files are stored and the
/// [fileService] defines where files are fetched, for example online.
factory Config(
  String cacheKey, {
  Duration stalePeriod,
  int maxNrOfCacheObjects,
  CacheInfoRepository repo,
  FileSystem fileSystem,
  FileService fileService,
}) = impl.Config;

String get cacheKey;
Duration get stalePeriod;
int get maxNrOfCacheObjects;
CacheInfoRepository get repo;
FileSystem get fileSystem;
FileService get fileService;

Config是一个抽象类,用于定义CacheManager的配置。以下是Config的一些属性和构造函数参数的简要解释:

  • cacheKey: 用于存储文件和数据库文件名的文件夹的名称。
  • stalePeriod: 缓存对象被视为“陈旧”的时间持续时间。如果文件被缓存但一段时间内未被使用,则会被删除。
  • maxNrOfCacheObjects: 允许缓存的文件的最大数量。如果文件数超过此限制,则将删除最长时间未被使用的文件。
  • repo: CacheInfoRepository,用于存储缓存元数据。在Android、iOS和macOS上,默认为CacheObjectProvider,它是一个sqflite实现。在web上,默认为NonStoringObjectProvider。在其他平台上,默认为JsonCacheInfoRepository
  • fileSystem: 定义缓存文件存储位置的FileSystem对象。
  • fileService: 定义文件获取位置的FileService对象。

通过设置这些属性和参数,可以自定义缓存管理器的行为和性能。

Config实现类

class Config implements def.Config {
  Config(
    this.cacheKey, {
    Duration? stalePeriod,
    int? maxNrOfCacheObjects,
    CacheInfoRepository? repo,
    FileSystem? fileSystem,
    FileService? fileService,
  })  : stalePeriod = stalePeriod ?? const Duration(days: 30),
        maxNrOfCacheObjects = maxNrOfCacheObjects ?? 200,
        repo = repo ?? _createRepo(cacheKey),
        fileSystem = fileSystem ?? IOFileSystem(cacheKey),
        fileService = fileService ?? HttpFileService();

  @override
  final CacheInfoRepository repo;

  @override
  final FileSystem fileSystem;

  @override
  final String cacheKey;

  @override
  final Duration stalePeriod;

  @override
  final int maxNrOfCacheObjects;

  @override
  final FileService fileService;

  static CacheInfoRepository _createRepo(String key) {
    if (Platform.isAndroid || Platform.isIOS || Platform.isMacOS) {
      return CacheObjectProvider(databaseName: key);
    }
    return JsonCacheInfoRepository(databaseName: key);
  }
}

这是一个实现了抽象类Config的类Config。在构造函数中,它接受以下参数:

  • cacheKey:用于存储文件夹和数据库文件名称的键。
  • stalePeriod(可选):定义缓存对象被视为“陈旧”的时间段。当文件被缓存但一段时间没有被使用时,该文件将被删除。默认为30天。
  • maxNrOfCacheObjects(可选):定义缓存允许的最大大小。如果有更多的文件,那些最长时间没有使用的文件将被删除。默认值为200。
  • repo(可选):用于存储缓存元数据的CacheInfoRepository。在Android、iOS和macOS上,这默认为CacheObjectProvider,这是由于其遗留问题使用了sqflite实现。在Web上,这默认为NonStoringObjectProvider。在其他平台上,这默认为JsonCacheInfoRepository
  • fileSystem(可选):定义缓存文件存储的位置。默认情况下,它使用IOFileSystem,其中cacheKey是文件夹名称。
  • fileService(可选):定义文件获取的位置(例如在线)。默认情况下,它使用HttpFileService

此外,该类还包含一个静态私有方法_createRepo,用于创建缓存信息存储库。如果在Android、iOS和macOS上,则使用CacheObjectProvider。否则,使用JsonCacheInfoRepository

FileService抽象类

/// Defines the interface for a file service.
/// Most common file service will be an [HttpFileService], however one can
/// also make something more specialized. For example you could fetch files
/// from other apps or from local storage.
abstract class FileService {
  int concurrentFetches = 10;
  Future<FileServiceResponse> get(String url, {Map<String, String>? headers});
}

定义了一个文件服务的接口。

最常见的文件服务将是一个 [HttpFileService],但你也可以创建更专门的服务。例如,你可以从其他应用程序或本地存储中获取文件。

HttpFileService(FileService的实现子类)

/// [HttpFileService] is the most common file service and the default for
/// [WebHelper]. One can easily adapt it to use dio or any other http client.
class HttpFileService extends FileService {
  final http.Client _httpClient;

  HttpFileService({http.Client? httpClient})
      : _httpClient = httpClient ?? http.Client();

  @override
  Future<FileServiceResponse> get(String url,
      {Map<String, String>? headers}) async {
    final req = http.Request('GET', Uri.parse(url));
    if (headers != null) {
      req.headers.addAll(headers);
    }
    final httpResponse = await _httpClient.send(req);

    return HttpGetResponse(httpResponse);
  }
}

以上代码定义了一个HttpFileService类,它是实现FileService接口的具体实现。这个类主要用于从网络上获取文件,它使用http包提供的Client来发送HTTP请求,获取指定URL的文件。

在类的构造函数中,我们可以传入一个可选的httpClient参数来指定要使用的http客户端。如果没有提供,它将创建一个默认的http.Client()来执行请求。

HttpFileService实现了FileService中定义的get方法,它使用http包的Request类来构建HTTP请求,并通过httpClient.send方法发送请求。返回的响应将被包装在HttpGetResponse对象中,并作为Future<FileServiceResponse>返回。

FileServiceResponse抽象类

/// Defines the interface for a get result of a [FileService].
abstract class FileServiceResponse {
  /// [content] is a stream of bytes
  Stream<List<int>> get content;

  /// [contentLength] is the total size of the content.
  /// If the size is not known beforehand contentLength is null.
  int? get contentLength;

  /// [statusCode] is expected to conform to an http status code.
  int get statusCode;

  /// Defines till when the cache should be assumed to be valid.
  DateTime get validTill;

  /// [eTag] is used when asking to update the cache
  String? get eTag;

  /// Used to save the file on the storage, includes a dot. For example '.jpeg'
  String get fileExtension;
}

定义了 [FileService] 获取文件的结果接口。

[content] 是一个字节流。 Stream<List> get content;

[contentLength] 是内容的总大小。 如果大小事先未知,则 contentLength 为 null。

int? get contentLength;

[statusCode] 应符合 HTTP 状态代码的规范。

int get statusCode;

定义了缓存应被认为是有效的截止时间。

DateTime get validTill;

[eTag] 用于在请求更新缓存时使用。

String? get eTag;

用于在存储上保存文件,包括点号。例如 '.jpeg'

String get fileExtension;

HttpGetResponse(FileServiceResponse类的实现)

/// Basic implementation of a [FileServiceResponse] for http requests.
class HttpGetResponse implements FileServiceResponse {
  HttpGetResponse(this._response);

  final DateTime _receivedTime = clock.now();

  final http.StreamedResponse _response;

  @override
  int get statusCode => _response.statusCode;

  String? _header(String name) {
    return _response.headers[name];
  }

  @override
  Stream<List<int>> get content => _response.stream;

  @override
  int? get contentLength => _response.contentLength;

  @override
  DateTime get validTill {
    // Without a cache-control header we keep the file for a week
    var ageDuration = const Duration(days: 7);
    final controlHeader = _header(HttpHeaders.cacheControlHeader);
    if (controlHeader != null) {
      final controlSettings = controlHeader.split(',');
      for (final setting in controlSettings) {
        final sanitizedSetting = setting.trim().toLowerCase();
        if (sanitizedSetting == 'no-cache') {
          ageDuration = const Duration();
        }
        if (sanitizedSetting.startsWith('max-age=')) {
          var validSeconds = int.tryParse(sanitizedSetting.split('=')[1]) ?? 0;
          if (validSeconds > 0) {
            ageDuration = Duration(seconds: validSeconds);
          }
        }
      }
    }

    return _receivedTime.add(ageDuration);
  }

  @override
  String? get eTag => _header(HttpHeaders.etagHeader);

  @override
  String get fileExtension {
    var fileExtension = '';
    final contentTypeHeader = _header(HttpHeaders.contentTypeHeader);
    if (contentTypeHeader != null) {
      final contentType = ContentType.parse(contentTypeHeader);
      fileExtension = contentType.fileExtension;
    }
    return fileExtension;
  }
}

HttpGetResponseFileServiceResponse 接口的基本实现,用于处理 HTTP 请求的响应。

它实现了 FileServiceResponse 接口,并提供了实现。

具体来说,它包含了一个私有的 _response 属性,该属性包含了 http.StreamedResponse 对象,以及一个私有的 _receivedTime 属性,表示响应的接收时间。

除此之外,它还实现了接口中定义的方法,包括 statusCodecontentcontentLengthvalidTilleTagfileExtension

其中,validTill 方法会解析 Cache-Control 头,并根据其中的指令计算出缓存的有效期,如果缺失 Cache-Control 头,则默认缓存有效期为一周。