痛点:
很多网站和app有高清图片展示的需求,前后端交互时有很大的流量,在线用户量大时带宽压力随之增大。目前针对图片服务的第三方,有阿里云的oss图片存储服务器,七牛云和又拍云,价格都不便宜。怎样能够少流量又高效地服务用户呢?webp图片编码技术应运而生(可能google根本没考虑这点,只是为了提高网络图片传输速度,哈哈)。
技术栈:
1.前端基于dart的flutter框架,主要用于构建android和ios移动应用软件;
2.后端基于golang的gin开发框架,负责提供webp静态资源图片。
解决方案:
1.客户端向服务器请求图片时,获取webp格式的图片,这里我请求一张大小为76kb的高清图片。
2.在flutter端展示时,我们使用CachedNetworkImage这个组件将其缓存。所见即所得,用户看到的图片都不用二次请求,直接从缓存写入相册中。这里注意一下cacheManager的用法,是一个单例对象。
abstract class BaseCacheManager {
/// Creates a new instance of a cache manager. This can be used to retrieve
/// files from the cache or download them online. The http headers are used
/// for the maximum age of the files. The BaseCacheManager should only be
/// used in singleton patterns.
flutter端webp图片展示如下:
3.然后我们在手机缓存中找到webp图片文件,通过dart的image库将其转换为png、jpg或者其他图片格式,保存至相册。
import 'package:image/image.dart' as DownloadImage;
这里我们储存为png格式,效果如下图,大小为745kb:
小结:webp图片传输大小为76kb,从手机缓存中恢复到相册.png形式,大小为745kb,节约了89.8%的流量费用,也降低了带宽压力。
示例源码:
flutter客户端
import 'dart:io';
import 'package:image/image.dart' as DownloadImage;
import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/material.dart';
import 'package:flutter_cache_manager/flutter_cache_manager.dart';
import 'package:image_gallery_saver/image_gallery_saver.dart';
import 'package:path_provider/path_provider.dart';
import 'package:permission_handler/permission_handler.dart';
void main() => runApp(EpubWidget());
class EpubWidget extends StatefulWidget {
@override
State<StatefulWidget> createState() {
return new EpubState();
}
}
class EpubState extends State<EpubWidget> {
final BaseCacheManager _cacheManager = DefaultCacheManager();
final String _imgUrl = 'http://192.168.0.113:8888/image/model.webp';
File _file;
@override
Widget build(BuildContext context) {
//动态申请储存权限
PermissionHandler().checkPermissionStatus(PermissionGroup.storage).then((pStatus){
if(pStatus == PermissionStatus.granted){
debugPrint("already get the right to save image on the phone");
return;
}else{
PermissionHandler().requestPermissions(<PermissionGroup>[
PermissionGroup.storage, // 在这里添加需要的权限
]);
}
});
return MaterialApp(
debugShowCheckedModeBanner: false,
title: "Fetch Epub Example",
home: new Material(
child: Scaffold(
body: Column(
children: <Widget>[
// Image.asset('assets/img0.webp'),
CachedNetworkImage(
imageUrl: _imgUrl,
),
Padding(
padding: const EdgeInsets.only(top: 60),
child: RaisedButton(
onPressed: () async{
FileInfo _fromMemory = _cacheManager.getFileFromMemory(_imgUrl);
DownloadImage.Image image = DownloadImage.decodeImage(File(_fromMemory.file.path).readAsBytesSync());
// Resize the image to a 120x? thumbnail (maintaining the aspect ratio).
DownloadImage.Image thumbnail = DownloadImage.copyResize(image, width: 120);
// Save the thumbnail as a JPG.
final dir = await getApplicationDocumentsDirectory();
// _file = File('${dir.path}/thumbnail.png')..writeAsBytesSync(DownloadImage.encodePng(thumbnail));
_file = File('${dir.path}/thumbnail.png')..writeAsBytesSync(DownloadImage.encodePng(image));
final result = await ImageGallerySaver.saveFile(_file.path);
print('result is $result');
setState(() { });
},
child: Text('download image'),
),
),
if(_file!=null)
Image.file(_file)
],
),
)
)
);
}
}
golang服务端
package main
import (
"net/http"
"github.com/gin-gonic/gin"
)
func main() {
router := gin.Default()
router.StaticFS("/image", http.Dir("./image")) //图片model.webp保存在image文件夹下
router.Run(":8888")
}
舔狗被骗了一万零八百,实在没钱吃饭了,兄弟们如果都看到这里了,麻烦打赏个10块钱,让我吃个蛋炒饭吧, 球球啦~
附言
webp转储jpg、png等任意格式图片在前端完成;而任意格式图片重新编码为webp的时间消耗较大,最好放在后台,这里借鉴一下别人的golang处理办法
golang后台任意图片转储为webp格式