@Toc
能用Elasticsearch来存储图片吗?有不少朋友都问过这个问题,Elasticsearch作为一个NoSQL数据库,一个搜索引擎,一个大数据存储系统,原则上来说,对于各种结构化,非结构化数据,文本类,非文本类数据都能够存储。即图片也是可以用来存储的,但现实中这种实际的操作方式是不常见的,因为对象存储等基础设施会是一个更低成本的选择。不过,考虑某些综合场景,比如,用户希望只搭建一套大数据系统来支撑不同的使用需求,那么Elasticsearch确实是比Hadoop生态这种包含非常多组件的系统要简单得多。
图片的存储
那么,我们该如何在Elasticsearch进行图片的存储呢?
第一个要解决的问题是我们应该选择何种类型来进行图片的存储。因为图片数据不同于文本数据,其包含的内容是像素点的颜色,位置,大小等相关信息,属于我们无法理解数据类型,因此,不需要对图片数据内容做倒排索引,Keyword,points等用于加速搜索、排序、聚合的数据结构,因此,需要用二进制的方式存储。
而对于图片的元数据,比如,图片的类型,图片的名称,图片中包含的内容(需要通过机器学习算法来提取),图片的向量值,这些属于可搜索内容的,则可以设置为不同的类型,比如:
- 图片的类型,图片的名称,图片中包含的内容等局可以文本的方式(
text
type)来进行存储 - 图片的向量可以用以
vector
type来进行存储
Binary field type
binary
类型接受一个二进制值作为Base64编码的字符串。该字段默认不存储,也不可搜索。
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图片为例,可通过以下配置来进行采集:
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
, 提取一些基本的图片信息,比如文件名和文件类型:
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
:
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/) ...