[解决方案] 自动调整图片
- 项目源码
- 原因: 图片尺寸/大小不一,没有规则,总共有300多G
- 目标
- 尽量不修改原有程序及架构, 以最小的代价使PC时代的图片库适应于移动端
- 溯源 (要和源文件对应起来)
- 可控 (预定义规则, 并不是随便进来一个请求都会得到处理)
- 不处理第二次(如果已经处理过,直接输出, 否则处理并保存,以供下次请求直接使用)
ASP.NET 和 NETCore 的解决方法不同. 本文分两部份
ASP.NET
利用
VirtualFile
/VirtualPathProvider
及StaticFileHandler
完成虚拟文件解析.
溯源
溯源的最简单办法是新文件
和源文件
名称
,路径
保持一至. 而且有规则可以依
, 就得把规则加到文件名中. 综合以上两点, 可以比较以下几种访问方式:
源: http://www.baidu.com/imgs/2017/09/09/abc.jpg
1. http://www.xxx.com/imgs/2017/09/09/abc.auto.s1.jpg
2. http://www.xxx.com/imgs/2017/09/09/abc.jpg?100x100
3. http://www.xxx.com/img?source=/imgs/2017/09/09/abc.jpg&w=100&h=100
- 第1种在文件名和扩展名中间插入了占位符
auto
和 规则s1
. 拿到源文件地址后, 要处理成这种格式的, 不大方便 (仅此而已) - 第2种没有强插, 但是多了后缀. 这个的坏处就多了去,
- 首先, 有问号和没有问号的都代表的是同一个文件,尤其是同时显示源图,和新图的时候, 问题更加突出.
- Windows 下, 问号 (
?
) 是不能出现在文件名中的 (Linux 下没有测试,不知道). 这条是违背了第4条目标的.
- 第3种就不多说了,太LOW了,不在考虑范围内.
综上所述, 我们这里选择第1种访问方式.
但是要使用这种方式, 就有点为难了, 因为: 没有入口 !
给个入口
静态类型的文件,ASP.NET 默认不会经过程序处理. 你可以在 Global
的 BeginRequest
里设置一下断点,看能不能进来.
要想要程序可以处理静态文件,只需要在 web.config
里加入如下配置:
<system.webServer>
<validation validateIntegratedModeConfiguration="false"/>
<handlers>
<!--设置可以由该程序处理的文件类型,必须-->
<add name="jpg" path="*.jpg" verb="GET" type="System.Web.StaticFileHandler"/>
<add name="gif" path="*.gif" verb="GET" type="System.Web.StaticFileHandler"/>
<add name="png" path="*.png" verb="GET" type="System.Web.StaticFileHandler"/>
</handlers>
</system.webServer>
至此, 你可以在 Global
的 BeginRequest
里写处理程序了.
更进一层
VirtualPathProvider
, VirtualFile
这两个东西的用处很多, 不了解的,可以自行搜索一下.
我了解的也不多, 只是在很久之前用它们取出 dll 中的母版页而已.这里我用它们来处理图片请求.
-
首先, 在
Global
的Application_Start
里注册VirtualPathProvider
:HostingEnvironment.RegisterVirtualPathProvider(new VImgProvider(baseDir));
-
请求进入之后,首先会到
VImgProvider.FileExists
方法, 这里主要是判断进入的请求是不是可接受的图片类型, 目标文件是不是存在. -
然后进入
VImgFile.Open
方法 源码- 判断请求的文件是否存在, 如果存在,直接返回.
- 如果不存在, 判断进入的请求是否满足设定的规则.
- 如果满足规则, 从请求里拆分/解析/装配
源图片
的路径. - 如果
源图片
存在, 按规则对源图片
进行缩放, 并将结果保存到同一目录下. - 同样的请求第二次进来,只用走到第1步就可以返回了.
附
为了方便添加修改规则,我定义了一个热插拔的 JsonConfig. 只需要修改 Cfgs/SizeTypes.json
即可:
示例:
{
"Types": [
{
"name": "s1",
"width": 100,
"height": 100,
"quality": 70
},
{
"name": "s2",
"width": 200,
"height": 200,
"quality": 70
},
{
"name": "s3",
"width": 113,
"height": 128
}
]
}
当然, 以经生成的图片要手动删除, 才能应用新的配置
NETCore
通过修改 StaticFile 中间件的 FileProvider 来实现.
相对而言, NETCore 的实现要简单的多: 只需要修改 StaticFileMiddleware 的 FileProvider 即可.
var ap = new AutoImgFileProvider(this.Configuration);
//优先 WebRootFileProvider , 如果文件在它中能找得到,就不在去 AutoImageFileProvider 中在找一次了.
var cp = new CompositeFileProvider(env.WebRootFileProvider, ap);
var opt = new StaticFileOptions()
{
FileProvider = cp
};
app.UseStaticFiles(opt);
需要添加 Nuget
- Microsoft.Extensions.FileProviders.Composite
- Microsoft.Extensions.FileProviders.Phyical
AutoImgFileProvider 是对 PhyicalFileProvider
做了一层包装, 而不是继承.
收到请求时 (GetFileInfo
), 首先调用包装的 PhyicalFileProvider.GetFileInfo
, 如果目标存在, 直接返回; 否则, 和ASP.NET的流程一样: 拆分/解析/组装/保存, 然后返回.
附
NETCore 规则配置采用了独立的配置文件, 修改规则,只需修改:
AutoImg.json 即可
示例:
{
"AutoImg": {
"BaseDir": "d:\\Imgs",
"Types": [
{
"name": "s1",
"width": 100,
"height": 100
},
{
"name": "s2",
"width": 200,
"height": 200
},
{
"name": "s3",
"width": 113,
"height": 128
}
]
}
}
另附
NETCore 下图片处理采用 ImageSharp , NET FX 下用的是 ImageProcessor.
ImageSharp 不支持质量百分比.