文件搜索项目

项目源码

项目源码:https://gitee.com/li_tong_jia/project.git

项目目标

  • 巩固多线程、JDBC、集合框架等技术
  • 满足文件搜索的需求

项目使用技术栈与平台

  • 所用技术:JavaFX、多线程、SQLite、pinyin4j
  • 平台与环境:Windows/Mac,IDEA,Maven

项目背景

各个操作系统下,都有提供文件搜索的功能,如windows中查找文件:

在这里插入图片描述

除系统自带的文件搜索功能外,也有大佬开发的很多搜索神器,比如Everything

在这里插入图片描述

  • Everything是基于NTFS文件系统的USN Journal(Update SequenceNumber Journal),是利用操作系统记录的文件操作日志来进行搜索,特点是效率高,速度快,但是具有一定的局限性,只能用于NTFS文件系统。
  • 本项目使用实时的本地文件进行搜索,保存文件信息以便于提高搜索效率。

项目功能

  • 指定搜索目录,显示目录中的所有文件、文件夹信息
  • 使用多线程进行文件搜索操作,文件信息保存在数据库。如果已保存有的文件信息,执行本地目录与数据库文件信息比对操作,在更新到数据库。
  • 可以根据文件名进行搜索
  • 文件名包含中文时,支持汉语拼音的搜索(全拼或是首字母匹配)

项目演示

选择文件目录

扫描本地文件,保存文件信息并显示

在这里插入图片描述

根据文件名搜索

在这里插入图片描述

根据全拼搜索

在这里插入图片描述

根据拼音首字母搜索

在这里插入图片描述

系统流程

整体流程

在这里插入图片描述

文件对比流程

在这里插入图片描述

技术栈介绍

SQLite介绍

  • SQLite是一款轻量级的嵌入式内存数据库(嵌入在进程中,运行在内存中的数据库),使用 ANSIC 编写的,并提供了简单和易于使用的 API。
  • 一个完整的 SQLite 数据库是存储在一个单一的跨平台的磁盘文件。
  • 非常小,是轻量级的,完全配置时小于 400KiB,省略可选功能配置时小于250KiB。
  • 无需安装、配置及管理。
  • 完全兼容 ACID 事务,允许从多个进程或线程安全访问。
  • 支持多种开发语言,C, C++, PHP, Perl, Java, C#,Python, Ruby等
  • 支持 SQL92(SQL2)标准的大多数查询语言的功能。

Pinyin4j介绍

  • 是一个Java的内库,提供对中文汉字到拼音的转换
  • 存在多音字的情况,根据一个字符可以获取多个字符串
String[] pinyins = PinyinHelper.toHanyuPinyinStringArray('长'); 

输出:
[zhang3, chang2]

  • 可以配置输出格式,包括大小写、是否带音调(默认带)、是否使用v(如绿的拼音lv)

JavaFX介绍

  • Java客户端UI库:JavaFX是一个强大的图形和多媒体处理工具包集合,它允许开发者来设计、创建、测试、调试和部署富客户端程序,并且和Java一样跨平台。
  • 提供了丰富的客户端组件:面板、按钮、文本框、表格等,还提供了各类事件、动画效果支持。
  • 提供了Web组件支持:HTML5的支持、CSS样式支持,JavaScript脚本支持。
  • 还提供了Scene Builder 程序,来支持拖拽式界面开发。
  • Java原有Swing、AWT客户端UI工具包,JavaFX 系统界面样式跟美观、系统架构对开发更友好、便捷

系统设计

sql表单 : 数据库设计

  • 需要保存的文件基本信息有:文件名称、路径、大小、上次修改时间
  • 文件名包含中文时,要满足全拼和拼音首字母的搜索,所以还需要保存全拼和拼音首字母
  • 中文汉字可能存在多音字的情况,我们这里只实现简单的,取第一个拼音即可

工具类 : JDBC工具类设计

  • 使用SQLite,需要先指定本地数据库文件路径,如果没有则创建该文件
  • 提供获取数据库连接的方法
  • 提供释放数据库资源的方法
    数据库初始化任务
  • 读取本地写好的sql文件,再执行数据库初始化建表操作

数据库初始化任务

  • 读取本地写好的sql文件,再执行数据库初始化建表操作

工具类 : 拼音工具类

  • 需要提供获取文件名的全拼及拼音首字母的方法

JavaFX界面设计

  • 界面的搭建
  • 绑定选择文件目录事件,在每次事件发生时,调用该事件方法:启动目录扫描任务,并在完成后刷新表格
  • 绑定搜索框内容改变事件,在每次事件发生时,调用该事件方法:搜索文件信息,并刷新表格

任务型 :多线程目录扫描任务设计

  • 入口是在选择文件目录事件发生后,根据选定的目录进行扫描
  • 扫描到目录(文件夹)时,执行目录下一级子文件的信息对比、更新操作
  • 如果目录下一级子文件是文件夹,则开启新的子任务处理这个子文件夹
  • 在所有任务都完成以后,刷新表格

业务型:本地文件、数据库文件对比及更新

  • 本地有,数据库没有,插入该文件信息
  • 数据库有,本地没有,删除数据库中该文件信息。如果该文件是文件夹,还需要删除所有在该文件夹下的文件

功能:文件搜索

  • 根据搜索值,模糊匹配文件名,全拼或拼音首字母字段,任意匹配成功都可以获取文件信息

开发步骤

创建Maven项目

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>tong</groupId>
    <artifactId>ErLangShen</artifactId>
    <version>1.0-SNAPSHOT</version>

    <dependencies>
        <dependency>
            <groupId>com.belerweb</groupId>
            <artifactId>pinyin4j</artifactId>
            <version>2.5.1</version>
        </dependency>
        <dependency>
            <groupId>org.xerial</groupId>
            <artifactId>sqlite-jdbc</artifactId>
            <version>3.28.0</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.1</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                    <encoding>UTF-8</encoding>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-jar-plugin</artifactId>
                <version>3.1.0</version>
                <configuration>
                    <archive>
                        <manifest>
                            <mainClass>Main</mainClass> <!-- 指定入口类 -->
                            <addClasspath>true</addClasspath> <!-- 在jar的MF文件中
生成classpath属性 -->
                            <classpathPrefix>lib/</classpathPrefix> <!--
classpath前缀,即依赖jar包的路径 -->
                        </manifest>
                    </archive>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-dependency-plugin</artifactId>
                <version>3.1.1</version>
                <executions>
                    <execution>
                        <id>copy</id>
                        <phase>package</phase>
                        <goals>
                            <goal>copy-dependencies</goal>
                        </goals>
                        <configuration>
                            <!-- 指定依赖包的输出路径,需与上方的classpathPrefix保持一
                            致 -->
                            <outputDirectory>${project.build.directory}/lib</outputDirectory>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

</project>

设计数据库表

drop table if exists file_meta;

CREATE TABLE IF NOT EXISTS file_meta (
    name VARCHAR(50) NOT NULL,
    path VARCHAR(1000) NOT NULL,
    is_directory boolean not null ,
    size BIGINT NOT NULL,
    last_modified TIMESTAMP NOT NULL,
    pinyin VARCHAR(50),
    pinyin_first VARCHAR(50)
);

设计数据库工具类,及表初始化操作

  • URL格式为:jdbc:sqlite://数据库本地文件路径,没有用户名密码。使用URL创建DataSource对象后,会以该路径文件作为数据库,没有则创建该文件。
private static String getUrl(){
    //classes路径
    URL classesURL = DBUtil.class.getClassLoader().getResource("./"); //classes路径
    //target路径
    String dir = new File(classesURL.getPath()).getParent();
    //ErLangShen.db路径 jdbc:sqlite://D:\java%20code\%e9%a1%b9%e7%9b%ae\ErLangShen\target\ErLangShen.db
    String url  = "jdbc:sqlite://" + dir + File.separator + "ErLangShen.db";
    return url;
}
  • 设计单例模式获取DataSource连接池对象。及通过DataSource获取数据库连接的方法。
private static volatile DataSource DATA_SOURCE;

/**
 *提供获取数据库连接词的功能
 * 使用单例模式(多线程安全)
 * 多线程安全版本的单例模式:
 * 1.为什么在最外层判断是否为null?
 * 2.synchronized加锁以后,为什么还要判断是否为null
 * 3.为什么Datasource类变量要使用volatile关键字修饰
 * 多线程操作:原子性,可见性(从主内存拷贝到工作内存),有序性
 * synchronized保证前三个特性,volatile保证可见性,有序性
 * @return
 */
private static DataSource getDataSource(){
    if (DATA_SOURCE == null){ //提高效率
        //保证只有一个不为空的DATA_SOURCE   单例
            synchronized (DBUtil.class){
            if (DATA_SOURCE == null){
                SQLiteConfig config = new SQLiteConfig();
                config.setDateStringFormat(Util.DATA_PATTERN);
                DATA_SOURCE = new SQLiteDataSource();
                ((SQLiteDataSource)DATA_SOURCE).setUrl(getUrl());
            }
        }
    }
    return DATA_SOURCE;
}
  • 设计释放资源的方法:关闭连接Conncetion、关闭数据库操作对象Statement、关闭结果集对象ResultSet。
public static void close(Connection connection, Statement statement) {
    close(connection,statement,null);
}

public static void close(Connection connection, Statement statement, ResultSet resultSet)  {
    try {
        if (connection != null) {
            connection.close();
        }
        if (statement != null) {
            statement.close();
        }
        if (resultSet != null){
            resultSet.close();
        }
    }catch (SQLException e) {
        e.printStackTrace();
        throw new RuntimeException("释放资源失败",e);
    }

}
  • 建立连接
public static Connection getConnection() throws SQLException {
    return getDataSource().getConnection();

}

初始化数据库

/**
 * 读取sql文件
 *
 */
public static String[] readSql() {
    try {
        //通过ClassLoader获取流
        InputStream in = DBInit.class.getClassLoader().getResourceAsStream("init.sql");
        //字节流转换为字符流
        BufferedReader br = new BufferedReader(new InputStreamReader(in,"UTF-8"));
        StringBuilder sb = new StringBuilder();
        String line ;
        while((line = br.readLine()) != null){
            if (line.contains("--")){
                line = line.substring(0,line.indexOf("--"));
            }
            sb.append(line);
        }
        String[] sqls = sb.toString().split(";");
        return sqls;
    } catch (Exception e) {
        e.printStackTrace();
        throw new RuntimeException("读取错误");
    }
}

public static void init() {
    //数据库jdbc操作
    //1.建立数据库连接connection
    Connection connection = null;
    Statement statement = null;
    try {
        //1.建立数据库连接connection
        connection = DBUtil.getConnection();
        //2.创建sql语句执行对象Statement
        String[] sqls = readSql();
        //2.创建sql语句执行对象Statement
        statement = connection.createStatement();
        for (String sql:sqls
             ) {
            //3.执行sql
            statement.executeUpdate(sql);
        }
        //4.如果是查询操作,获取结果集ResultSet,处理结果集
    } catch (SQLException e) {
        e.printStackTrace();
        throw new RuntimeException("初始话数据库表操作失败",e);
    }finally {
        //5.释放资源
        DBUtil.close(connection,statement);
    }

}

public static void main(String[] args) {
    System.out.println(Arrays.toString(readSql()));
}

设计汉语拼音工具

  • 使用第三方库提供的汉字到拼音的转换,一个汉字转换出来可能有多个拼音。配置汉字的字符范围,及拼音的输出格式(小写、不带音调、包含V字符)
/**
 * 中文字符格式
 */
private static final String CHINESE_PATTERN = "[\\u4E00-\\u9FA5]";

/**
 * 汉语拼音格式化类
 */
private static final HanyuPinyinOutputFormat FORMAT = new HanyuPinyinOutputFormat();

static {
    FORMAT.setCaseType(HanyuPinyinCaseType.LOWERCASE); //小写拼音
    FORMAT.setToneType(HanyuPinyinToneType.WITHOUT_TONE);  //不带声调
    FORMAT.setVCharType(HanyuPinyinVCharType.WITH_V);  //带v

}

/**
 * 字符串中是否包含中文
 * @param name
 * @return
 */
public static boolean containsChinese(String name) {
    //正则表达式
    return name.matches(".*" + CHINESE_PATTERN + ".*");
}
  • API有提供单个字符的拼音转换:
String[] temp = PinyinHelper.toHanyuPinyinStringArray(c,
PINYIN_OUTPUT_FORMAT); 
  • 多个字符组成的文件名,需要按照每个字符返回的字符串数组进行排列组合,我们只简单实现,每个字符取第一个返回的字符串作为拼音。拼音分全拼,和汉字首字母拼接的两种方式
/**
 * 通过文件名获取全拼+拼音首字母
 * 中华人民共和国--->zhongghuarenmingongheguo/zhrmghg(不考虑多音字)
 * @param name 文件名
 * @return    全拼+拼音首字母  数组
 */
public static String[] get(String name){
    String[] result = new String[2];
    StringBuilder all = new StringBuilder();//全拼
    StringBuilder first = new StringBuilder();//首字母

    for (char c:name.toCharArray()
         ) {
        try{
            String[] pinyins = PinyinHelper.toHanyuPinyinStringArray(c,FORMAT);//多音字
            if(pinyins == null || pinyins.length == 0){
                all.append(c);
                first.append(c);
            }else {
                all.append(pinyins[0]);
                first.append(pinyins[0].charAt(0));
            }
        }catch (BadHanyuPinyinOutputFormatCombination badHanyuPinyinOutputFormatCombination) {
            all.append(c);
            first.append(c);
        }
    }
    result[0] = all.toString();
    result[1] = first.toString();
    return result;
}
  • 如果考虑多音字的话
/**
 * 多音字版获取名称
 * @param name 文件名
 * @param fullSpell true为全拼,false为首字母
 * @return 文件名多音字数组
 */
public static String[][] get(String name,boolean fullSpell){
    char[] chars = name.toCharArray();
    String[][] result = new String[chars.length][];
    for (int i = 0; i < chars.length; i++) {
        try{
            //去除音调后,会有重复,"只":[zhi,zhi...]---------quchong
            String[] pinyins = PinyinHelper.toHanyuPinyinStringArray(chars[i],FORMAT);//多音字

            if(pinyins == null || pinyins.length == 0){
               result[i] = new String[]{String.valueOf(chars[i])};
            }else{
                result[i] = unique(pinyins,fullSpell);
            }
        }catch (BadHanyuPinyinOutputFormatCombination badHanyuPinyinOutputFormatCombination) {
            result[i] = new String[]{String.valueOf(chars[i])};
        }
    }
    return result;
}

/**
 * 数组去重操作
 * @param array
 * @param fullSpell
 * @return
 */
public static String[] unique(String[] array,boolean fullSpell){
    Set<String> set = new HashSet<>();
    for (String s:array
         ) {
        if(fullSpell){
            set.add(s);
        }else {
            set.add(String.valueOf(s.charAt(0)));
        }
    }
    return set.toArray(new String[set.size()]);
}

/**
 * 每个中文字符返回的是字符串数组,每两个字符串数组合并为一个字符串数组,之后以此类推
 * @param pinyinArray 文件名多音字数组
 * @return 返回文件名的排列组合
 */
public static String[] compose(String[][] pinyinArray){
    if (pinyinArray == null || pinyinArray.length == 0){
        return null;
    }else if (pinyinArray.length == 1){
        return pinyinArray[0];
    }else {
        String[] result = pinyinArray[0];
        for (int i = 1; i < pinyinArray.length; i++) {
            result = compose(result,pinyinArray[i]);
        }
        return result;
    }
}

/**
 * 拼音数组两两组合
 * @param pinyin1
 * @param pinyin2
 * @return 返回组合
 */
public static String[] compose(String[] pinyin1,String[] pinyin2){
    String[] result = new String[pinyin1.length*pinyin2.length];
    int k = 0;
    for (int i = 0; i < pinyin1.length; i++) {
        for (int j = 0; j < pinyin2.length; j++) {
            result[k] = pinyin1[i] + pinyin2[j];
            k++;
        }
    }
    return result;
}

设计界面及文件信息搜索

  • Main.java
public class Main extends Application {

    @Override
    public void start(Stage primaryStage) throws Exception{
        Parent root = FXMLLoader.load(getClass().getClassLoader().getResource("app.fxml"));
        primaryStage.setTitle("ErLangShen");
        primaryStage.setScene(new Scene(root, 1000, 800));
        primaryStage.show();
    }
    public static void main(String[] args) {
        launch(args);
    }
}
  • app.fxml
<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.geometry.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.control.cell.*?>

<GridPane fx:id="rootPane" alignment="center" hgap="10" vgap="10" xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/8" fx:controller="app.Controller">
    <children>

        <Button onMouseClicked="#choose" prefWidth="90" text="选择目录" GridPane.columnIndex="0" GridPane.rowIndex="0" />
        <Label fx:id="srcDirectory">
            <GridPane.margin>
                <Insets left="100.0" />
            </GridPane.margin>
        </Label>
        <TextField fx:id="searchField" prefWidth="900" GridPane.columnIndex="0" GridPane.rowIndex="1" />

        <TableView fx:id="fileTable" prefHeight="700" prefWidth="900" GridPane.columnIndex="0" GridPane.columnSpan="2" GridPane.rowIndex="2">
            <columns>
                <TableColumn fx:id="nameColumn" prefWidth="220" text="名称">
                    <cellValueFactory>
                        <PropertyValueFactory property="name" />
                    </cellValueFactory>
                </TableColumn>
                <TableColumn prefWidth="400" text="路径">
                    <cellValueFactory>
                        <PropertyValueFactory property="path" />
                    </cellValueFactory>
                </TableColumn>
                <TableColumn fx:id="sizeColumn" prefWidth="120" text="大小">
                    <cellValueFactory>
                        <PropertyValueFactory property="sizeText" />
                    </cellValueFactory>
                </TableColumn>
                <TableColumn fx:id="lastModifiedColumn" prefWidth="160" text="修改时间">
                    <cellValueFactory>
                        <PropertyValueFactory property="lastModifiedText" />
                    </cellValueFactory>
                </TableColumn>
            </columns>
        </TableView>
    </children>
</GridPane>
  • Controller
public class Controller implements Initializable {

    @FXML
    private GridPane rootPane;

    @FXML
    private TextField searchField;

    @FXML
    private TableView<FileMeta> fileTable;

    @FXML
    private Label srcDirectory;//标签

    private Thread task;
    public void initialize(URL location, ResourceBundle resources) {
        //界面初始化时,需要初始化数据库及表
        DBInit.init();
        // 添加搜索框监听器,内容改变时执行监听事件
        searchField.textProperty().addListener(new ChangeListener<String>() {

            public void changed(ObservableValue<? extends String> observable, String oldValue, String newValue) {
                freshTable();
            }
        });
    }

    /**
     * 点击选择目录
     * @param event
     */
    public void choose(Event event) {
        // 选择文件目录
        DirectoryChooser directoryChooser=new DirectoryChooser();
        Window window = rootPane.getScene().getWindow();
        File file = directoryChooser.showDialog(window);
        if(file == null)
            return;
        // 获取选择的目录路径,并显示
        String path = file.getPath();
        srcDirectory.setText(path);
        //选择目录就需要执行目录的扫描任务:该目录下的目录及子目录下的文件夹

        if(task != null){
            task.interrupt();//中断
        }
            task = new Thread(new Runnable() {
                @Override
                public void run() {
                    System.out.println("执行文件扫描任务");
                    ScanCallback callback = new FileSave();
                    FileScanner scan = new FileScanner(callback);
                    scan.scan(path);//多线程扫描.提高效率
                    //等待扫描完毕;
                    try {
                        scan.waitFinish();
                        System.out.println("所有任务执行完毕,刷新表格");
                        //刷新表格:将扫描的子文件和子文件夹都展示在表格
                        freshTable();
                    } catch (InterruptedException e) {
                        e.printStackTrace();

                    }
                }
            });
            task.start();

    }



    // 刷新表格数据
    private void freshTable(){
        ObservableList<FileMeta> metas = fileTable.getItems();
        metas.clear();
        String dir = srcDirectory.getText();
        if (dir != null && dir.trim().length() != 0){
            String content = searchField.getText();
            //提供数据库的查询方法
            List<FileMeta> fileMetas = FileSearch.search(dir,content);
            metas.addAll(fileMetas);
        }
    }
}

设计多线程扫描任务及文件信息保存

  • 多线程执行大量任务,使用线程池来提高执行效率
private ExecutorService pool = Executors.newFixedThreadPool(4);
  • 为便于在线程中执行的任务有较好的扩展性,可以考虑使用接口回调的方式实现。传入时设定为文件信息保存的任务。
public FileScanner(ScanCallback callback) {
    this.callback = callback;
}
  • 在线程执行时,待执行任务数+1, 执行完后,待执行任务数-1,开启子任务时,每个子任务都执行任务数+1操作,这样在最后可以判断出是否所有线程执行完毕。
private volatile AtomicInteger count = new AtomicInteger();
/**
 * 扫描文件目录
 * 开始不知道多少子文件夹
 * @param path
 */
public void scan(String path) {
    count.incrementAndGet();//根目录扫描任务,计数器++i
    doScan(new File(path));
}

private void doScan(File dir){
    callback.callback(dir);
    pool.execute(new Runnable() {
        @Override
        public void run() {
            try {
                File[] children = dir.listFiles();
                if (children != null) {
                    for (File file : children
                    ) {
                        if (file.isDirectory()) {//如果是文件夹,递归
                           // System.out.println("文件夹" + file.getPath());
                            count.incrementAndGet();//++i
                            doScan(file);
                        }
                    }
                }
            }finally {
                int c = count.decrementAndGet();
                if (c == 0){
                    synchronized (lock){
                        lock.notify();
                        System.out.println("********************************************");
                    }
                }
            }
        }
    });
}

本地文件、数据库文件对比及更新

  • 本地有,数据库没有,插入该文件信息
/**
 * 文件保存在数据库
 * @param file
 */
private void save(FileMeta file){
    Connection connection = null;
    PreparedStatement statement = null;
    try {
        connection = DBUtil.getConnection();
        String sql = "insert into file_meta" +
                "(name, path, is_directory, size, last_modified, pinyin, pinyin_first)" +
                " values (?,?,?,?,?,?,?)";
        statement = connection.prepareStatement(sql);
        statement.setString(1,file.getName());
        statement.setString(2,file.getPath());
        statement.setBoolean(3,file.getDir());
        statement.setLong(4,file.getSize());
        statement.setTimestamp(5,new Timestamp(file.getLastModified().getTime()));
        statement.setString(6,file.getPinyin());
        statement.setString(7,file.getPinyinFirst());
        System.out.printf("insert name=%s, path=%s\n", file.getName(), file.getPath());
        statement.executeUpdate();
    } catch (SQLException e) {
        throw new RuntimeException("文件保存失败" + e);
    }finally {
        DBUtil.close(connection,statement);
    }
}
  • 数据库有,本地没有,删除数据库中该文件信息。如果该文件是文件夹,还需要删除所有在该文件夹下的文件
private void delete(FileMeta meta) {
    Connection connection = null;
    PreparedStatement ps = null;
    try {
        connection  = DBUtil.getConnection();
        String sql = "delete from file_meta where" +
                " (name = ? and path = ? and is_directory = ?)" ;
        if (meta.getDir()){
            sql += " or path=?" +
                    " or path like ?";
        }
        ps = connection.prepareStatement(sql);
        ps.setString(1,meta.getName());
        ps.setString(2,meta.getPath());
        ps.setBoolean(3,meta.getDir());
        if (meta.getDir()){
            ps.setString(4,meta.getPath() + File.separator + meta.getName());
            ps.setString(5,meta.getPath() + File.separator + meta.getName() + File.separator +"%");
        }
        System.out.printf("删除文件信息,dir=%s\n",
                meta.getPath()+File.separator+meta.getName());
        ps.executeUpdate();
    } catch (SQLException e) {
        e.printStackTrace();
        throw new RuntimeException("删除文件信息出错,检查delete语句", e);
    }finally {
        DBUtil.close(connection,ps);
    }
}

项目总结

  • 项目优点:使用多线程来进行文件遍历,提高了效率。
  • 项目缺点:中文汉字多音字没有进行排列组合,只能支持多音字拼音的一种组合搜索。
  • 项目扩展:我们还能将该项目写的更完善,可以往以下几个发展方向走:
    1. 多音字支持
    2. 将项目打包成exe安装文件

项目参考资料

  • SQLite Home Page
  • Pinyin4j、http://pinyin4j.sourceforge.net/
  • JavaFX官网、FX China、JavaFX 教程
  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
IT文件服务器项目文件是指用于存储、管理和共享IT项目相关文件的服务器。在IT项目中,会产生大量的文件,包括需求文档、设计文档、代码文档、测试报告等等。这些文件项目开发过程中的重要产物,也是项目成果的重要体现。 IT文件服务器的作用是通过提供统一的存储空间,方便团队成员进行文件的上传、下载、删除和修改。通过文件服务器,团队成员可以快速访问需要的文件,减少了文件传递的时间和成本,提高了团队协作效率。 IT文件服务器一般具备以下功能: 1. 文件存储与管理:文件服务器可以提供大容量的存储空间,方便团队成员进行文件的存储和管理。可以通过权限设置,确保只有授权人员可以进行文件上传、删除和修改。 2. 文件共享:文件服务器可以将文件共享给相关团队成员,方便大家在需要的时候进行访问。通过共享功能,避免了文件重复传递和存储,提高了协作效率。 3. 版本控制:文件服务器可以提供版本控制功能,方便团队成员进行文件版本的管理和追踪。当需要对文件进行修改时,可以创建新的版本,保留历史版本,方便回溯和比较。 4. 搜索与检索:文件服务器可以提供搜索与检索功能,方便团队成员快速找到需要的文件。可以通过文件名、关键词等方式进行搜索,提高了文件的查找效率。 IT文件服务器项目文件的建立对于IT项目的顺利进行和团队合作来说非常重要。通过良好的文件管理,可以确保所有团队成员都能够及时获得最新的文件,并且能够方便地对文件进行修改和共享。这有助于提高团队的工作效率,避免因为文件传递和管理不畅造成的沟通障碍和工作延误。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值