用Elasticsearch存储图片并在Kibana中显示

2021-03-02 10:07:23 浏览数 (1)

@Toc

能用Elasticsearch来存储图片吗?有不少朋友都问过这个问题,Elasticsearch作为一个NoSQL数据库,一个搜索引擎,一个大数据存储系统,原则上来说,对于各种结构化,非结构化数据,文本类,非文本类数据都能够存储。即图片也是可以用来存储的,但现实中这种实际的操作方式是不常见的,因为对象存储等基础设施会是一个更低成本的选择。不过,考虑某些综合场景,比如,用户希望只搭建一套大数据系统来支撑不同的使用需求,那么Elasticsearch确实是比Hadoop生态这种包含非常多组件的系统要简单得多。

图片的存储

那么,我们该如何在Elasticsearch进行图片的存储呢?

第一个要解决的问题是我们应该选择何种类型来进行图片的存储。因为图片数据不同于文本数据,其包含的内容是像素点的颜色,位置,大小等相关信息,属于我们无法理解数据类型,因此,不需要对图片数据内容做倒排索引,Keyword,points等用于加速搜索、排序、聚合的数据结构,因此,需要用二进制的方式存储。

而对于图片的元数据,比如,图片的类型,图片的名称,图片中包含的内容(需要通过机器学习算法来提取),图片的向量值,这些属于可搜索内容的,则可以设置为不同的类型,比如:

  • 图片的类型,图片的名称,图片中包含的内容等局可以文本的方式(text type)来进行存储
  • 图片的向量可以用以vector type来进行存储

Binary field type

binary类型接受一个二进制值作为Base64编码的字符串。该字段默认不存储,也不可搜索。

代码语言:txt复制
PUT my-index-000001
{
  "mappings": {
    "properties": {
      "name": {
        "type": "text"
      },
      "blob": {
        "type": "binary"
      }
    }
  }
}

PUT my-index-000001/_doc/1
{
  "name": "Some binary blob",
  "blob": "U29tZSBiaW5hcnkgYmxvYg==" 
}

二进制字段接受以下参数。

  • doc_values 字段是否应该以列存的方式存储在磁盘上,以便以后可以用于排序、汇总或脚本,可配置为 "true "或 "false"(默认)
  • store 字段值是否应与 _source 字段分开存储和检索。可配置为rue 或 false(默认)

图片的摄入 经验证,这部分因为encoding的原因无法正确摄入binary数据

其实图片的存储并不复杂,只需要选择合适的数据类型来对应不同的数据内容即可。主要的问题是在图片的摄入,在默认的Elastic Stack技术栈里,并没有提供专门的工具来进行图片数据的摄入。需要我们做一定的适配

以下,我们通过filebeat进行图片摄入的一个样例。

首先,filebeat默认来说,是以文件为对象对文件里文本类型的数据进行采集的,以行为单位,检查n等符号来确定一个event的边界。即检测到n后,采集器会往队列里push一条event (两个n之间的内容)。而图片,是以文件为单位,每个文件对应一个图片,即文件=图片=event。

因此,我们需要让filebeat能够将整个文件作为一个event来采集就必须借助multilines参数。具体的配置项可参考官方文档。我们针对png图片为例,可通过以下配置来进行采集:

代码语言:txt复制
filebeat.inputs:
- type: log
  paths:
  - '/Users/lex.li/Documents/elastic/materials/s*.png'
  harvester_buffer_size: 16384000
  fields:
    format: "png"
  multiline:
    pattern: '^?PNG'
    match: after
    negate: true
    max_lines: 100000

processors:
   - dissect:
        tokenizer: "%{key0}/materials/%{filename}.png"
        field: "log.file.path"
        target_prefix: ""
   - drop_fields:
        when:
            has_fields: ['key0']
        fields: ["key0"]
   - drop_fields:
        fields: ["agent","ecs"]

output.elasticsearch:
  hosts: ["http://localhost:9200"]
  username: "elastic"
  password: "changeme"

  indices:
  - index: "images"

  bulk_max_size: 1500
  worker: 3

setup:
  template.enabled: false
  ilm.enabled: false

这里,因为考虑到图片可能很大,我们将单个采集器的buffer和multilines的行数配置得比较大:

代码语言:txt复制
...
  harvester_buffer_size: 16384000
  ...
    max_lines: 100000

通过dissect processors , 提取一些基本的图片信息,比如文件名和文件类型:

代码语言:txt复制
  fields:
    format: "png"
···
processors:
   - dissect:
        tokenizer: "%{key0}/materials/%{filename}.png"
        field: "log.file.path"
        target_prefix: ""
   - drop_fields:
        when:
            has_fields: ['key0']
        fields: ["key0"]
   - drop_fields:
        fields: ["agent","ecs"]

存放到名为images的索引中。因为filebeat默认会把内容放在message字段中,我们需要提前设置该字段的类型为binary:

代码语言:txt复制
PUT images
{
  "mappings": {
    "properties": {
      "message": {
        "type": "binary"
      }
    }
  }  
}

呈现方式如下,我们采集了所有以s开头的png图片

在这里插入图片描述在这里插入图片描述

采集之后,可以通过Kibana界面查看

在这里插入图片描述在这里插入图片描述

存储的是图片的数据

在这里插入图片描述在这里插入图片描述

如果我们需要把这些数据还原回去,还需要从这个json当中,把message里的内容保存为一个文件,并需使用正确的格式扩展名(比如这里的png)。

在Kibana中查看图片

我们可以在Kibana中查看我们搜索的图片。这时需要借助script field。

首先打开索引模式。

在这里插入图片描述在这里插入图片描述

然后添加脚本字段

在这里插入图片描述在这里插入图片描述

其格式为URL->图像,这里的URL输入为http服务的地址,可见后文。并且,注意设置一下宽和高

在这里插入图片描述在这里插入图片描述

创建该字段,内容为 filename.keyword, 并将其通过{{value}}带入到URL中。(见上图)

在这里插入图片描述在这里插入图片描述

这时,可以在discovery中看到图片的预览:

在这里插入图片描述在这里插入图片描述

这里需要在图片文件存储的目录启动一个http server:

代码语言:txt复制
lexlideMacBook-Pro:materials lex.li$ python3 -m http.server
Serving HTTP on 0.0.0.0 port 8000 (http://0.0.0.0:8000/) ...

0 人点赞