注册

跟我学企业级flutter项目:如何重新定制cached_network_image的缓存管理与Dio网络请求

前言


flutter中需要展示网络图片时候,不建议使用flutter原本Image.network(),建议最好还是采用cached_network_image这个三方库。那么我今天就按照它来展开说明,我再做企业级项目时如何重新定制cached_network_image。


由于我的项目网络请求采用Dio库,所以我希望我的图片库也采用Dio来网络请求,也是为了方便请求日志打印(在做APM监控时候可以看到网络请求状态,方便定位问题)。


前期准备


准备好mime_converter类,由于cached_network_image中的manager这个文件不是export的状态,那么我们需要准备好该类,以便我们自己实现网络请求修改。


实现mime_converter


创建mime_converter 类,代码如下:


import 'dart:io';

///将最常见的MIME类型转换为最期望的文件扩展名。
extension ContentTypeConverter on ContentType {
String get fileExtension => mimeTypes[mimeType] ?? '.$subType';
}

///MIME类型的来源:
/// https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types/Common_types
///2020年3月20日时更新
const mimeTypes = {
'application/vnd.android.package-archive': '.apk',
'application/epub+zip': '.epub',
'application/gzip': '.gz',
'application/java-archive': '.jar',
'application/json': '.json',
'application/ld+json': '.jsonld',
'application/msword': '.doc',
'application/octet-stream': '.bin',
'application/ogg': '.ogx',
'application/pdf': '.pdf',
'application/php': '.php',
'application/rtf': '.rtf',
'application/vnd.amazon.ebook': '.azw',
'application/vnd.apple.installer+xml': '.mpkg',
'application/vnd.mozilla.xul+xml': '.xul',
'application/vnd.ms-excel': '.xls',
'application/vnd.ms-fontobject': '.eot',
'application/vnd.ms-powerpoint': '.ppt',
'application/vnd.oasis.opendocument.presentation': '.odp',
'application/vnd.oasis.opendocument.spreadsheet': '.ods',
'application/vnd.oasis.opendocument.text': '.odt',
'application/vnd.openxmlformats-officedocument.presentationml.presentation':
'.pptx',
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet': '.xlsx',
'application/vnd.openxmlformats-officedocument.wordprocessingml.document':
'.docx',
'application/vnd.rar': '.rar',
'application/vnd.visio': '.vsd',
'application/x-7z-compressed': '.7z',
'application/x-abiword': '.abw',
'application/x-bzip': '.bz',
'application/x-bzip2': '.bz2',
'application/x-csh': '.csh',
'application/x-freearc': '.arc',
'application/x-sh': '.sh',
'application/x-shockwave-flash': '.swf',
'application/x-tar': '.tar',
'application/xhtml+xml': '.xhtml',
'application/xml': '.xml',
'application/zip': '.zip',
'audio/3gpp': '.3gp',
'audio/3gpp2': '.3g2',
'audio/aac': '.aac',
'audio/x-aac': '.aac',
'audio/midi audio/x-midi': '.midi',
'audio/mpeg': '.mp3',
'audio/ogg': '.oga',
'audio/opus': '.opus',
'audio/wav': '.wav',
'audio/webm': '.weba',
'font/otf': '.otf',
'font/ttf': '.ttf',
'font/woff': '.woff',
'font/woff2': '.woff2',
'image/bmp': '.bmp',
'image/gif': '.gif',
'image/jpeg': '.jpg',
'image/png': '.png',
'image/svg+xml': '.svg',
'image/tiff': '.tiff',
'image/vnd.microsoft.icon': '.ico',
'image/webp': '.webp',
'text/calendar': '.ics',
'text/css': '.css',
'text/csv': '.csv',
'text/html': '.html',
'text/javascript': '.js',
'text/plain': '.txt',
'text/xml': '.xml',
'video/3gpp': '.3gp',
'video/3gpp2': '.3g2',
'video/mp2t': '.ts',
'video/mpeg': '.mpeg',
'video/ogg': '.ogv',
'video/webm': '.webm',
'video/x-msvideo': '.avi',
'video/quicktime': '.mov'
};

实现FileServiceResponse


FileServiceResponse是数据处理的关键,那么我们来实现该类



class DioGetResponse implements FileServiceResponse {
DioGetResponse(this._response);

final DateTime _receivedTime = clock.now();

final Response<ResponseBody> _response;

@override
int get statusCode => _response.statusCode!;


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

@override
int? get contentLength => _getContentLength();

int _getContentLength() {
try {
return int.parse(
_header(HttpHeaders.contentLengthHeader) ?? '-1');
} catch (e) {
return -1;
}
}

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


@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;
}
}

实现FileService


实现FileService 参数为dio


class DioHttpFileService extends FileService {
final Dio _dio;

DioHttpFileService(this._dio);

@override
Future<FileServiceResponse> get(String url, {Map<String, String>? headers}) async {
Options options = Options(headers: headers ?? {}, responseType: ResponseType.stream);
Response<ResponseBody> httpResponse = await _dio.get<ResponseBody>(url, options: options);
return DioGetResponse(httpResponse);
}
}

制定框架缓存管理器


我在项目中,设定了缓存配置最多缓存 100 个文件,并且每个文件只应缓存 7天,如果需要使用日志拦截器的话,就在拦截器中增加日志拦截:


class LibCacheManager {
static const key = 'libCacheKey';

///缓存配置 {最多缓存 100 个文件,并且每个文件只应缓存 7天}
static CacheManager instance = CacheManager(
Config(
key,
stalePeriod: const Duration(days: 7),
maxNrOfCacheObjects: 100,
fileService : DioHttpFileService(Dio()))
),
);

}

项目中使用


使用如下


CachedNetworkImage(imageUrl: "https://t8.baidu.com/it/u=3845489932,4046458829&fm=74&app=80&size=f256,256&n=0&f=JPEG&fmt=auto?sec=1654102800&t=f6de842e1e7086ffc73536795d37fd2c",
cacheManager: LibCacheManager.instance,
width: 100,
height: 100,
placeholder: (context, url) => ImgPlaceHolder(),
errorWidget: (context, url, error) => ImgError(),
);

如上便是 如何重新定制cached_network_image的缓存管理与Dio网络请求

0 个评论

要回复文章请先登录注册