[解决方案] 自动调整图片

C#

浏览数:73

2019-8-31

AD:资源代下载服务
  • 项目源码
  • 原因: 图片尺寸/大小不一,没有规则,总共有300多G
  • 目标
    • 尽量不修改原有程序及架构, 以最小的代价使PC时代的图片库适应于移动端
    • 溯源 (要和源文件对应起来)
    • 可控 (预定义规则, 并不是随便进来一个请求都会得到处理)
    • 不处理第二次(如果已经处理过,直接输出, 否则处理并保存,以供下次请求直接使用)

ASP.NET 和 NETCore 的解决方法不同. 本文分两部份

ASP.NET

利用 VirtualFile / VirtualPathProviderStaticFileHandler 完成虚拟文件解析.

溯源

溯源的最简单办法是新文件源文件 名称,路径 保持一至. 而且有规则可以依, 就得把规则加到文件名中. 综合以上两点, 可以比较以下几种访问方式:

源: 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 默认不会经过程序处理. 你可以在 GlobalBeginRequest 里设置一下断点,看能不能进来.
要想要程序可以处理静态文件,只需要在 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>

至此, 你可以在 GlobalBeginRequest 里写处理程序了.

更进一层

VirtualPathProvider , VirtualFile 这两个东西的用处很多, 不了解的,可以自行搜索一下.
我了解的也不多, 只是在很久之前用它们取出 dll 中的母版页而已.这里我用它们来处理图片请求.

  • 首先, 在 GlobalApplication_Start 里注册 VirtualPathProvider:

    HostingEnvironment.RegisterVirtualPathProvider(new  VImgProvider(baseDir));
    

    VImgProvider 源码

  • 请求进入之后,首先会到 VImgProvider.FileExists 方法, 这里主要是判断进入的请求是不是可接受的图片类型, 目标文件是不是存在.

  • 然后进入 VImgFile.Open方法 源码

    1. 判断请求的文件是否存在, 如果存在,直接返回.
    2. 如果不存在, 判断进入的请求是否满足设定的规则.
    3. 如果满足规则, 从请求里拆分/解析/装配 源图片 的路径.
    4. 如果源图片 存在, 按规则对源图片进行缩放, 并将结果保存到同一目录下.
    5. 同样的请求第二次进来,只用走到第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 不支持质量百分比.

作者:gruan