功能
- 可以按文件名搜索
- 可以按文件类型搜索
- 实现文件监听
意义
- 解决Windows下命令行搜索文件不方便问题
- 提高全局搜索时的速度
- 跨平台使用
- 锻炼编码能力
涉及的技术
- 文件操作
- 数据库
- 多线程
- JDBC
- 文件系统监控
具体实现步骤
-
搭建项目
创建maven项目
配置pom
按功能分类创建包(cmd–放的是命令行交互的代码、config–放的是属性配置文件、heart–放的是检索文件信息,索引文件信息,文件系统监控的代码,heart中又包含common包–公共使用的代码、seek包–检索、index包–索引、monitor包–文件监控、dao(数据访问对象)包–JDBC编程代码、interceptor包–拦截器、property包–模型,JDBC编程中,每次查询是根据方法名来获取它的值,是把数据库中的每一行数据挨个去取,取出来之后封装到一个类中,变成一个类的对象)
创建入口程序
具体实现每个包中的代码 -
具体代码实现
因为要实现按文件类型搜索,文件类型又有很多种,所以我们可以把文件类型抽象成一个模型,把检索条件也可以抽象成一个模型,因为检索有深度限制等,所以文件信息也抽象成一个模型- 模型
文件类型(FileType 比如:img - > png、jpg、gif,doc - > pdf、txt…)
检索条件(Ask :name、filetype、limit(数量限制)、order(排序策略))
文件信息(AfterIndex :name、path、depth、filetype)
因为把一个路径用file来表示了,要和数据库连接起来,记得先变成java文件,变成java文件后,文件的每个属性是在AfterIndex中的,所以要变成AfterIndex,然后与数据库中表中的每个属性对应
- 数据库(diy_everything.sql)
本地系统中的文件或者文件夹 -> Java File -> Java AfterIndex -> 数据库中的记录
Table
数据库的创建、数据库表的设计
JDBC -> 数据库的驱动,创建连接有3种:https://www.cnblogs.com/hzh-java/p/7745755.html, 具体可以参考这篇博客。我采用的是数据源,因为DriverManager是静态连接,如果连接后不释放,数据库连接会特别多,效率就降低了,所以用数据源的数据库连接池,当获取完连接后不用了,就开始close,实际上就是把连接还给了连接池,这个连接一直会持久,socket连接是一个长连接,所以会一直持久,它的关闭与销毁全部是由数据库的连接池决定的。 - 检索
- 数据库的初始化
- 数据库的访问(使用DataSource)
- 实现检索(查询)
- 索引
- 数据库的初始化
- 数据库的访问(使用DataSource)
- 实现索引(插入)
- 遍历文件系统中的所有文件,并且将文件对象转换为AfterIndex对象,调用数据库访问的插入操作
- 拦截器
在索引之后获取到文件,可以对文件进行打印、转换、写入,这三种操作是互不影响的,我们就可以把它们当作一个独立的函数去执行,这就是拦截器----在获取到文件之后,对它进行打印、转换(因为写入数据库需要的是AfterIndex对象,但是获得的是File对象,所以需要转换)或写入数据库。
在什么时候拦截呢?
在每扫描(index)一个文件就拦截一个。 - 配置
- 索引目录:包含的目录,排除的目录(有的目录不需要索引)
- 通过参数设置是否开启索引线程
- 查询是按照升序还是降序
- 查询的结果数量是多少(不可能把所有的都显示出来 )
- 统一调度器(DiyEverythingManager)(将配置、索引、检索、拦截器模块组合调度)
检索:立即执行返回结果
索引:后台线程执行,用多线程,每一个磁盘创建一个线程,在开始遍历创建索引之前先排除掉不包含的目录
假如我们在操作系统上新建一个文件,但数据库中是没有的,或者在操作系统上删除一个文件,但它在数据库中还存在,并不能同步,现在就需要实现文件监听,一旦操作系统有文件的增加或删除,在数据库也要做相应的操作。
- 文件监听
实现文件监听可以遍历文件系统和数据库比对还可以依赖文件系统的通知事件,因为Java是运行在虚拟机上的、跨平台的,JDK7提供了WatchService监听文件系统的变化,但是只是监听当前目录,不能监听所有,所以采用遍历文件系统和数据库比对来实现文件监听,遍历可以引入一个第三方库(一组代码的集合)-- commons-io,把文件的启动和停止交给第三方库的FileAlterationMonitor的对象去实现,实现一个适配器FileAlterationListenerAdaptor,可以根据自己的需要去覆写函数,然后把要监听的对象加入addListener()。
- 模型
问题
- 用单线程遍历电脑文件还是用多线程遍历?
解决:如果用多线程遍历会复杂很多,会出现重复统计,就要考虑避免防止重复提交了。但如果是多个盘,可以分盘遍历,一个盘创建一个线程,各自遍历。 - 在电脑上删除了一个文件,发现数据库上还存在那个文件。
解决:在检索结果之后,把它拦截一下,判断如果这个文件不在文件系统里,就回到数据库把这个文件删除 - 数据库使用MySQL还是H2?
使用H2数据库,因为MySQL数据库检索太慢了(MySQL是一个典型的C/S模型,每一次向数据库中写入数据,都需要建立服务器和客户端的连接,但并不是每一次都需要建立连接,所以使用了连接池,把数据批量写入数据库)
优化
-
如果文件已被删除,不仅要让它在本地中删除,还需要回到数据库中删除,这样每次检索完之后,还要进行一次过滤,对数据库进行操作,这样会降低效率。
解决:让数据库的删除变成异步的,可以考虑用多线程,但是用多线程,同时有很多用户,同时访问很多次,就要多次创建线程,所以可以使用生产者消费者模型—移除文件就是生产者,数据库删除文件就是消费者,这样数据库删除文件的话,就只需要把要删除的文件加入队列中就行,只需要开启一个线程,让队列中的元素依次出队列删除。 -
扩展文件监控功能
因为文件系统随时都在发生变化(创建、删除)
实现:
1. 遍历文件系统和数据库对比
2. 依赖文件系统的通知事件
为什么不用JDK自带的文件系统监控,因为太鸡肋了(只能监控当前文件,不能监控子文件),这样还不如用遍历监控 所以使用第三方库(commms-io也是遍历(使用线程遍历,速度快)) -
数据库加索引
在文件名上加一个索引,加快查询速度
测试
Bug:
- name长度不够,建立索引失败
- 类型查询有问题
- 检索结果的数量并没有限制
- 查询结果没有排序
解决:
5. 在数据库中增大name长度或排除掉文件名过长的文件
6. 代码中有错误,获取属性获取错了,把getName改为getFileType
7. 在代码中设置数量限制
8. 调用统一调度器里的排序函数,按找文件深度排序,默认是升序
如何使用
在命令行输入:help,会出现如下提示
命令:
退出:quit
帮助:help
索引:index
查询:search [ img | doc | bin | archive | other