Flutter 实现图片缓存到本地

网上可能有不少实现的插件,有些动不动就上千行代码, 其实很简单 只须要在源码的基础上加一个本地缓存就行,git

毕竟源码是最可靠的 github

https://github.com/dikeboy/flutter-cache-image-local缓存

Flutter  自带的有2个 图片的Widget ,Image 和FadeInImage, 从字面上就能理解 FadeIn是多了个预加载的图,网络

FadeInImage 有两个 方法 ,memoryNetwork 和assertNetwork  预加载图来源于二进制 或者 assert,若是是动态生成的 就须要用memoryNetworkasync

网络图片是经过NetworkImage来实现加载, ide

文件  assert  memory  networkImage 都是继承自Image_provider,fetch

Image_provider自己已经带了一个内存缓存,大概就相似安卓的Lrucacheui

 

 ImageStream resolve(ImageConfiguration configuration) { assert(configuration != null); final ImageStream stream = ImageStream(); T obtainedKey; obtainKey(configuration).then<void>((T key) { obtainedKey = key; stream.setCompleter(PaintingBinding.instance.imageCache.putIfAbsent(key, () => load(key)));

 

这里的imageCache就是Flutter 自带的缓存this

 

咱们要实现缓存到本地   确定须要 在内存缓存以后    网络请求以前url


先来看下NetworkImage的源码

class NetworkImage extends ImageProvider<NetworkImage> { /// Creates an object that fetches the image at the given URL. ///
  /// The arguments must not be null.
  const NetworkImage(this.url, { this.scale = 1.0 , this.headers }) : assert(url != null), assert(scale != null); /// The URL from which the image will be fetched.
 final String url; /// The scale to place in the [ImageInfo] object of the image.
  final double scale; /// The HTTP headers that will be used with [HttpClient.get] to fetch image from network.
  final Map<String, String> headers; @override Future<NetworkImage> obtainKey(ImageConfiguration configuration) { return SynchronousFuture<NetworkImage>(this); } @override ImageStreamCompleter load(NetworkImage key) { return MultiFrameImageStreamCompleter( codec: _loadAsync(key), //这个就是主要的图片网络加载方法 , 咱们主要是须要修改这个方法 scale: key.scale, informationCollector: (StringBuffer information) { information.writeln('Image provider: $this'); information.write('Image key: $key'); } ); } static final HttpClient _httpClient = HttpClient(); 方法很简单,拿回数据 转换为二进制返回/ Future<ui.Codec> _loadAsync(NetworkImage key) async { assert(key == this); final Uri resolved = Uri.base.resolve(key.url); final HttpClientRequest request = await _httpClient.getUrl(resolved); headers?.forEach((String name, String value) { request.headers.add(name, value); }); final HttpClientResponse response = await request.close(); if (response.statusCode != HttpStatus.ok) throw Exception('HTTP request failed, statusCode: ${response?.statusCode}, $resolved'); final Uint8List bytes = await consolidateHttpClientResponseBytes(response); if (bytes.lengthInBytes == 0) throw Exception('NetworkImage is an empty file: $resolved'); return await PaintingBinding.instance.instantiateImageCodec(bytes); } @override bool operator ==(dynamic other) { if (other.runtimeType != runtimeType) return false; final NetworkImage typedOther = other; return url == typedOther.url && scale == typedOther.scale; } @override int get hashCode => hashValues(url, scale); @override String toString() => '$runtimeType("$url", scale: $scale)'; }

 

这里须要引入 几个插件   其实这几个就算不修改这里 项目中确定也是必须的

http: ^0.12.0   网络请求 这个毫无疑问
transparent_image: ^0.1.0 //这是一个预加载的透明图 ,若是须要用assert图或者是自定义能够不须要 这里为了方便
path_provider: ^0.4.1 //这是获取 安卓 IOS 文件缓存路径
crypto: ^2.0.6 //这个主要是MD5 base64之类 转码 咱们根据图片的md5(url)作为key来缓存

下面直接贴修改后的 NetworkImage
class MyNetworkImage extends ImageProvider<MyNetworkImage> { /// Creates an object that fetches the image at the given URL. ///
  /// The arguments must not be null.
  const MyNetworkImage(this.url, { this.scale = 1.0 , this.headers,this.sdCache }) : assert(url != null), assert(scale != null); /// The URL from which the image will be fetched.
 final String url; final bool sdCache; //加一个标志为 是否须要缓存到sd卡 /// The scale to place in the [ImageInfo] object of the image.
  final double scale; /// The HTTP headers that will be used with [HttpClient.get] to fetch image from network.
  final Map<String, String> headers; @override Future<MyNetworkImage> obtainKey(ImageConfiguration configuration) { return SynchronousFuture<MyNetworkImage>(this); } @override ImageStreamCompleter load(MyNetworkImage key) { return MultiFrameImageStreamCompleter( codec: _loadAsync(key), scale: key.scale, informationCollector: (StringBuffer information) { information.writeln('Image provider: $this'); information.write('Image key: $key'); } ); } Future<Codec> _loadAsync(MyNetworkImage key) async { assert(key == this);
//本地已经缓存过就直接返回图片
if(sdCache!=null){ final Uint8List bytes =await _getFromSdcard(key.url); if (bytes!=null&&bytes.lengthInBytes!=null&&bytes.lengthInBytes!= 0) { print("success"); return await PaintingBinding.instance.instantiateImageCodec(bytes); } } final Uri resolved = Uri.base.resolve(key.url); http.Response response = await http.get(resolved); if (response.statusCode != HttpStatus.ok) throw Exception('HTTP request failed, statusCode: ${response?.statusCode}, $resolved'); final Uint8List bytes = await response.bodyBytes;
//网络请求结束后缓存图片到本地
if(sdCache!=null&&bytes.lengthInBytes != 0){ _saveToImage(bytes, key.url); } if (bytes.lengthInBytes == 0) throw Exception('MyNetworkImage is an empty file: $resolved'); return await PaintingBinding.instance.instantiateImageCodec(bytes); } @override bool operator ==(dynamic other) { if (other.runtimeType != runtimeType) return false; final MyNetworkImage typedOther = other; return url == typedOther.url && scale == typedOther.scale; } @override int get hashCode => hashValues(url, scale); @override String toString() => '$runtimeType("$url", scale: $scale)'; 图片路径MD5一下 缓存到本地 void _saveToImage(Uint8List mUint8List,String name) async { name = md5.convert(convert.utf8.encode(name)).toString(); Directory dir = await getTemporaryDirectory(); String path = dir.path +"/"+name; var file = File(path); bool exist = await file.exists(); print("path =${path}"); if(!exist) File(path).writeAsBytesSync(mUint8List); } _getFromSdcard(String name) async{ name = md5.convert(convert.utf8.encode(name)).toString(); Directory dir = await getTemporaryDirectory(); String path = dir.path +"/"+name; var file = File(path); bool exist = await file.exists(); if(exist){ final Uint8List bytes = await file.readAsBytes(); return bytes; } return null; } }

 

若是须要直接修改FadeInImage 或者Image 直接把代码烤到一个新的dart文件  把NetworkImage 所有改为MyNetworkImage就好了

相关文章
相关标签/搜索