赞
踩
该领域已被Lucene独占,几乎无竞争对手。
但是直接使用Lucene非常复杂,因此在出现了两个组件,一是solr,二是elastic search,elastic search流行度更高,但并非在所有应用场景占优,对于索引库已建立的情况下,如将某人的个人办公电脑所有文档进行全文搜索,这种情况下,solr的性能要明显优于es;但对于动态数据的不断插入索引库,如互联网应用,则es性能明显优于solr。
对于企业文档管理系统而言,文档处于动态变化中,但变化频率相对互联网应用频率较低,solr和es都可以使用,考虑到流行程度,最终选择的es。
在已经选择es的情况下,与SpringBoot整合,有几种技术方案:
**1.transport-api:**springboot版本不同,transport-api不同,不能适配不同的es版本;7.x不建议使用,8以后废弃
**2.JestClient:**非官方,更新慢;
**3.RestTemplate:**模拟发送Http请求,Es很多操作需要自己封装,麻烦;
4.HttpClient:同上;
**5.ElasticSearch-Rest-Client:**官方RestClient,封装了很多ES操作,
优点:API层次分明,上手简单;
缺点:
6.Spring Data Elasticsearch
支持Spring的基于@Configuration的java配置方式,或者XML配置方式
提供了用于操作ES的便捷工具类ElasticsearchTemplate。包括实现文档到POJO之间的自动智能映射。
利用Spring的数据转换服务实现的功能丰富的对象映射
基于注解的元数据映射方式,而且可扩展以支持更多不同的数据格式
根据持久层接口自动生成对应实现方法,无需人工编写基本操作代码(类似mybatis,根据接口自动得到实现)。当然,也支持人工定制查询。
1-4种集成方式缺点很明显,不予考虑。第5种是es官方自带的类库,易用性较好,但仍存在一些缺点;第6种则是spring提供的服务,考虑到spring这个大家族,最终选择Spring Data Elasticsearch。
需求上,需要实现全文搜索的对象是文档(搜索文件夹的名称意义有限,不考虑),进行全文搜索时,参与搜索的有2项信息,文档名称和文档内容,参与排序的是相关度、创建时间、更新时间。
创建索引,需要读取文件内容。
粗略考虑,能进行全文搜索的主要是office、文本、代码、pdf、markdown这几种常见格式,图片、视频、音频和压缩包不处理,需要在配置文件中明确定义,以便使用合适的读取器进行处理。
office:“docx”, “wps”, “doc”, “xls”, “xlsx”, “ppt”, “pptx”
图片:“jpg”, “jpeg”, “png”, “gif”, “bmp”, “ico”, “raw”
文本:txt,html,htm,asp,jsp,xml,json,properties,md,gitignore,log,java,py,c,cpp,sql,sh,bat,m,bas,prg,cmd
代码:“java”, “c”, “php”, “go”, “python”, “py”, “js”, “html”, “ftl”, “css”, “lua”, “sh”, “rb”, “yml”, “json”, “h”, “cpp”, “cs”, “aspx”, “jsp”
压缩包:“rar”, “zip”, “jar”, “7-zip”, “tar”, “gzip”, “7z”
多媒体:mp3,wav,mp4
markdown:md
xml:xml
pdf:pdf
flv:flv
cad:cad
对于文本类的文件,文件编码可能会有多种,如UTF-8,UTF-16,GB2312,GBK,GB18030等,读取内容时需要保证编码方式正确,否则会产生乱码。
可以分为两类,一类带bom,即文件前几个字节代表编码方式,另外一类则不带bom,需要根据字节内容判断
以常见的windows记事本生成的txt文件为例,使用以下代码可正确读取(已验证)
/**
* 判断文件的编码格式
* @param fileName :file
* @return 文件编码格式
* @throws Exception
*/
public static String codeString(File fileName) throws Exception{
BufferedInputStream bin = new BufferedInputStream(
new FileInputStream(fileName));
int p = (bin.read() << 8) + bin.read();
String code = null;
switch (p) {
case 0xefbb:
code = "UTF-8";
break;
case 0xfffe:
code = "Unicode";
break;
case 0xfeff:
code = "UTF-16BE";
break;
default:
code = "GBK";
}
IOUtils.closeQuietly(bin);
return code;
}
这种判断方式和原理对于不带bom的文件无能为力。
看上去是个小事,但是真要自己写,工作量非常可观……
网上找了下第三方类库,有以下几个:
cpdetector:基于统计学原理的,不保证完全正确,api使用相当繁琐
public static String getFileEncode(String filePath) {
String charsetName = null;
try {
File file = new File(filePath);
CodepageDetectorProxy detector = CodepageDetectorProxy.getInstance();
detector.add(new ParsingDetector(false));
detector.add(JChardetFacade.getInstance());
detector.add(ASCIIDetector.getInstance());
detector.add(UnicodeDetector.getInstance());
java.nio.charset.Charset charset = null;
charset = detector.detectCodepage(file.toURI().toURL());
if (charset != null) {
charsetName = charset.name();
} else {
charsetName = "UTF-8";
}
} catch (Exception ex) {
ex.printStackTrace();
return null;
}
return charsetName;
}
icu4j:ibm出品的,看了下最新版本是2020年12月更新的,应该还算可靠
<dependency>
<groupId>com.ibm.icu</groupId>
<artifactId>icu4j</artifactId>
<version>59.2</version>
</dependency>
public static String getFileEncoding(File file) {
//默认设置为utf-8
String encoding = "utf-8";
try {
Path path = Paths.get(file.getPath());
byte[] data = Files.readAllBytes(path);
CharsetDetector detector = new CharsetDetector();
detector.setText(data);
CharsetMatch match = detector.detect();
if (match != null) {
encoding = match.getName();
}
} catch (IOException exception) {
//读取文件失败,不处理异常
}
return encoding;
}
拿记事本试了下,另存为几种格式,发现类库输出的判断优于上面拿bom简易判断读取,最终选择使用该类库处理。
文本内容只要保证编码格式,直接读取即可,需要做内容解析的主要是二进制格式的pdf及office系列文档。
采用apache poi类库来读取Word、Excel和PowerPoint这三类文件,注意2003版本及以下,和2007版本及以上需要分别处理
<!--读取Word/Excel/PowerPoint文件内容-->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>3.17</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-scratchpad</artifactId>
<version>3.17</version>
</dependency>
采用apache pdf库来读取pdf内容
<!--读取pdf文件内容-->
<dependency>
<groupId>org.apache.pdfbox</groupId>
<artifactId>pdfbox</artifactId>
<version>2.0.3</version>
</dependency>
读取文件内容,创建索引是常规操作,这块技术上没什么问题,不过从处理流程考虑,用户上传文件,系统进行存储和写库表是主流程,且必须完成,出错应给予提示,但创建索引并不属于主流程,该工作不应该占用主流程的处理时间,所以应该进行异步处理。
因此这里通过新线程来读取文件内容并创建索引。
全文搜索组件Elasticsearch并不支持带数据权限搜索,因此只能从查询结果入手来解决,实现思路如下:
首先获取1页数据(例如,页面记录数为10),然后逐条判断当前用户是否有预览权限,如有,则加入结果列表;
判断结果列表是否已够10条,如不够,则继续读取下一页数据,当读取的记录数超出结果总数则停止。
前端默认只显示单页数据,同时不显示命中记录数,而是通过点击加载更多来加载下页数据。
同时,需考虑当前页数据之后是否还有数据,以便前端控制是否显示加载更多提示。
此处还有个棘手问题,带权限的数据拼接问题,首次查询没问题,但是后续查询,从哪里开始则是问题,相当于还需要将上次查询到哪一页记录下来,如果每次查询返回一个固定条数(如10条),则受数据权限过滤的影响,容易出现部分数据丢失的情况,例如单页数据大小是10,一共查询出28条数据,先查出10条,权限过滤后剩余了8条,再读取第2页数据,假设第11-20条数据经过权限过滤后有5条数据,则取2条补足,作为结果返回,用户此时点击了加载更多,这时候后端就面临1个麻烦,从哪个地方开始取数据,才能不重不漏,很明显上面例子产生了“半页数据”。
进一步查看es的api,发现这种类似搜索引擎的应用场景,es提供了scroll 来支持,查询结果会自动产生一个scrollId,下次查询传入该参数,则会从该记录继续往下搜索。
但是,如果每次加载相同数量的数据,还是面临半页数据的问题,因此调整实现思路,在查询结果足量情况下,每次至少返回10条数据,最多返回20条数据(主要受数据权限过滤的影响),这种实现方式,对于业务用户而言,并无多大影响。
实际并不需要自己创建索引,给实体类加上注解后,es引擎会自动创建索引库以及映射
各建1个,es7.x版本已经废弃了类型,每个索引库只有1个默认的类型,即_doc,只存在一种数据结构的映射
调整需求,只考虑文档的全文搜索,因此该问题将不存在
暂时没发现应另行定义的必要性,先按照复用已有的实体对象处理
SpringDataElasticSearch组件自身问题,根本就未读取ES组件的Field注解属性,在系统启动完成后,使用ElasticSearchRestTemplate接口,手动创建索引。
文档预览是一个很棘手的问题,对于pdf格式,有较完善的解决方案,通过技术组件可以实现预览,但对于常用的office系列,使用一些第三方的技术组件将其先转换为pdf在预览,速度是一方面,转换效果也存在较大的不确定性,要想实现比较好的预览效果,则往往需要收费服务。
【永中office】【office365】【idocv】
通过搜索,找到一个介绍看上去不多的开源的项目https://kkfileview.keking.cn/zh-cn/docs/home.html,且基于apache 2.0开源协议。
权限控制方案
kkfileview基于rest接口,调用方式如下:
var originUrl = ‘http://127.0.0.1:8080/filedownload?fileId=1’
var previewUrl = originUrl + ‘&fullfilename=test.txt’
window.open(‘http://127.0.0.1:8012/onlinePreview?url=’+encodeURIComponent(Base64.encode(previewUrl)))
在自己实现的文档管理系统中,文档的权限进行严格控制,因此,从自己系统发起的请求,可以控制是否允许访问,但是,以上调用方式,相当于kkfileview作为请求发起方,这样就涉及到1个关键问题,如何确认这个请求合法?
方案:基于令牌方式
发起预览请求时,先验证权限,权限通过后生成令牌,传递给kkfileview,kkfileview基于该令牌向应用系统请求文件。
系统名称:一二三文档管理系统
简介: 企事业单位一站式文档管理系统,让组织内文档管理有序,协作高效、安全可控
资料:csdn专栏
开源地址:Gitee
开源协议:MIT
欢迎收藏、点赞、评论,你的支持是我前行的动力。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。