• 操作系统期末实验:多用户二级文件系统


    写在最前面

    期末实验不是python写的,所以很可能是当时在github上找了一个,然后改了改hhh
    如果后续找到了链接就放过来

    问题描述

    设计一个多用户的二级文件系统,能够实现简单的文件操作。具体包括如下几条命令:
    (1)Dir 列文件目录; (2)Create 创建文件
    (3)Delete 删除文件 (4)Deldir 删除目录
    (5)Open 打开文件
    (6)Close 关闭文件
    (7)Read、Write读写文件
    (8)Search 查询文件
    (9)Copy 拷贝文件
    (10)Cut 剪切文件。

    要 求:

    本课程设计要求按照学校有关规范的要求完成,在课程设计完成后需要提交的成果和有关文档资料包括期末实验测试报告,同时检查源程序及可运行程序(含运行环境)并打分。其中期末实验测试报告格式参考此文档格式完成,其内容不能过于简单。必须包括的内容有:
    (1)功能设计部分:
    1)课程设计的基本思想,系统的总体结构和各子模块的功能说明;
    2)课程设计有关算法的描述,并画出有关算法流程图。
    (2)源程序:源程序中核心代码及说明。源代码要求在关键的位置有注释,增加程序的可读性。程序结构和变量等命名必须符合有关软件开发的技术规范。
    (3)程序运行结果
    (4)心得与体会。即本课程设计的个人总结,主要包括以下内容:
    1)课程设计中遇到的主要问题和解决方法;
    2)你的创新和得意之处;
    3)设计中存在的不足及改进的设想;
    4)本次课程设计的感想和心得体会。

    1 功能设计

    文件系统是操作系统中负责管理和存取文件信息的软件机构,它由管理文件所需的数据结构(如目录表、文件控制块、存储分配表)、相应的管理软件、以及访问文件的一组操作所组成,是对文件存储器的存储空间进行组织、分配、负责文件的存储并对存入的文件进行保护、检索的系统。

    1.1 系统层次结构

    在这里插入图片描述

    磁盘管理:系统最底层直接对内存空间的管理,如磁盘空闲空间管理,磁盘空间的分配方式等。
    文件管理:系统对于文件和目录层次的管理的,规定了FCB结构,目录结构等,包含了对接口的实现。
    系统接口: 该文件系统提供给前端可以使用的接口。
    前端页面:文件系统呈现给用户的可视化界面。

    1.2初始化

    磁盘的逻辑单元为块,内存和磁盘之间的I/O传输以块为单位执行。基于磁盘的特点功能需要:1.可以原地重写,可以从磁盘上读一块儿,修改该块,并将它写回到原来的位置;2.可以直接访问磁盘上的任意一块。因此,可以方便地按顺序或随机访问文件。
    (1)存储空间
    文件系统首先要解决的问题,是有效分配文件存储器的存储空间。文件存储器的物理空间以块为单位进行分配。
    磁盘大小:一共有10块磁盘块,每块磁盘块的大小为32*32=1024字节,磁盘块从0编号到9。
    分配方式:显式链接,即通过存储文件分配表(File Allocation Table,FAT)。
    每个磁盘的开始部分用于FAT,表中每个磁盘块都有一个FAT条目,并可通过块号索引。(未使用的块为0,使用的块包含下一个块儿号)。目录条目含有文件首块号码,通过这个块号索引的FAT条目包含文件下一块的号码,这个链会继续下去,直到最后一块,最后一块的表条目值为文件结束值。其优点为没有磁盘空间浪费,缺点是1.不支持文件的直接访问;2.需要更多的磁盘空间(来记录指针)。

    (2)空闲空间
    空闲空间表实现为位图,或位向量,标记了磁盘上所有盘块的使用情况。每块用一位(bit)表示,1表示块空闲;0表示块已分配。然后将所有空闲块用链表链接起来,并将指向第一个空闲块儿的指针保存在特殊位置,每个磁盘块含有下一个磁盘块的指针。

    (3)文件的组织结构
    文件系统第二个要解决的问题,是提供一种组织数据的方式。文件系统负责实现用户看到的逻辑特性,到物理特性的转换,实质是对“按名存取”功能的实现。
    存储空间的管理。文件的逻辑结构有两种形式:一是无结构的流式文件,二是有结构的记录式文件。本次实验中采用的是流式文件的显示链接的物理文件结构,在每一个磁盘都创建了一个文件BitMap&&Fat.txt,记录了所有文件所占用内存块的情况,及对应内存块的位置。磁盘有多少块,文件分配表就有多少项,若某文件的一个磁盘块号为i,则这个文件的下一个磁盘的块号应该记录在文件分配表第i项。每个文件的FAT包含该文件的许多详细信息。他有一个唯一的标识号,以便与目录条目相关联。
    分配磁盘块的流程图:
    在这里插入图片描述
    (4)文件的存储方法
    文件系统第三个要解决的问题,是提供合适的存取方法,以适应各种不同的应用。设计文件控制块(FCB)包含有关文件的信息,包括所有者、权限、文件内容的位置等。设计目录实现方法为哈希表,根据文件名得到一个值,并返回一个指向线性列表中元素的指针。其优点为能减少目录搜索时间,缺点为两个文件名哈希到相同的位置时可能发生冲突;因哈希表固定大小,创建文件需要哈希表重建时,比较麻烦。

    1.2 子功能设计

    文件系统应提供一组服务,使用户能处理数据以执行所需要的操作,有两个不同的设计问题,一是访问问题:如何定义文件系统对用户的接口;二是存储问题:创建数据结构和算法,把逻辑文件系统映射到物理外存设备。
    (1)选择系统于本机操作的文件夹
    使用文件导航窗口FileChooser,实现点击上传文件时弹出一个框选择上传的文件。

    (2)创建一个新的文件
    应用程序调用逻辑文件系统。逻辑文件系统指导目录结构的格式,它会分配一个新的FCB。然后系统将相应的目录信息读入内存,更新目录结构和FCB,将结果写回磁盘。
    系统调用open()将文件名传到逻辑文件系统,首先搜索整个系统的打开文件表,查看是否已经被打开,如果是,则在该进程的打开文件表创建一个条目,并指向现有整个系统的打开文件表。否则,根据文件名搜索目录结构。找到后,它的FCB会复制到内存的整个系统的开放文件表中(该表还存放着打开该文件的进程数量),接下来,在该进程的打开文件表创建一个条目,并指向现有整个系统的打开文件表。Open() 返回值:打开文件表的索引值,指向系统范围内打开文件表相应条目。

    (3)删除文件、写文件、展示目录下所有文件
    查找文件在当前目录的目录项描述内容,若存在则得到FCB的描述内容,释放FCB空间和文件数据空间,从目录表中删除文件的目录项。
    根据FAT号找到要写回的区域,之前读入的部分中FCB进行调整,然后写覆盖。
    对目录表进行遍历,同时根据文件类型做出不同的输出。

    在这里插入图片描述

    (4)目录的创建、删除及格式化
    目录的创建和删除与文件的操作大致相同。
    建立目录首先要找到建立目录的位置(父目录),然后查找该目录是否存在,如果父目录不存在,不能建立;如果存在,查找是否存在同名目录,存在,不能建立;不存在,则查找一个空目录项,为该目录申请一个盘块,并填写目录内容。注意,创建目录时目录表项的startBlock,不是FCB而是指目录的存放位置,而且还要自动为其添加多一个父目录项“…”,用于跳转。
    格式化目录时,递归删除当前目录下的所有文件和目录。

    (5)DFS算法查找文件|文件夹
    深度优先搜索在搜索过程中访问文件目录后,递归地访问此目录的所有未访问过的相邻文件名,并判断当前文件|文件夹的名称是否与搜索的文件夹名称一致,如果不一致则继续访问。如果所有邻接结点(文件|文件夹)往下都访问过了,就回溯到更上一层(文件夹)。如果访问全部后依然没有找到,返回-1。

    2 源程序

    2.1 系统实现主要的软件技术

    在功能设计的基础上,确认项目大体框架。
    在这里插入图片描述

    2.2 数据结构

    这里仅展示磁盘Block板块的核心。

    public class Block {
        private int blockName;             // 名称
        private File blockFile;              // 盘区内的文件
        private File blockBitMap;           // 位图存储文件
        private File recover;                // 恢复文件
        private FileWriter bitWriter;        // 位图存储文件书写器
        private FileWriter recoverWriter;    // 恢复文件书写器
        private int fileNum;               // 文件数量
        private double space;              // 盘区已占用空间
        public int [][] bitmap = new int[32][32];  // 内存块数组
        private Map<String, int[][] > filesBit = new HashMap<String, int[][]>();   // 文件和内存块对应表
        private ArrayList<File> files = new ArrayList<File>();   // 文件列表
        public Block(int name, File file, boolean rec) throws IOException {
            // 初始化}
        public File getBlockFile(){
            return blockFile;}
        public void putFCB(File file, double capacity) throws IOException {
            // 将FCB写入文件}
        public void rewriteBitMap() throws IOException {
            // 重写位图存储文件}
        public void rewriteRecoverWriter() throws IOException{
            // 重写恢复文件}
        public boolean createFile(File file, double capacity) throws IOException {
            // 在盘区内创建文件}
        public boolean deleteFile(File file, double capacity){
            // 在盘区内删除文件}
        public boolean renameFile(File file, String name, double capacity) throws IOException {
            // 重命名盘区内的文件}
        public int getFileNum() {
            return fileNum;}
        public double getSpace() {
            return space;}
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    2.3 后端

    (1)创建工作区

            rootFile = new File(rootPath + File.separator + "myFileSystem");
            readMe = new File(rootPath + File.separator + "myFileSystem" + File.separator + "ReadMe.txt");
    
            boolean flag = true;
    
    
    • 1
    • 2
    • 3
    • 4
    • 5

    (2)文件树
    位于左侧,呈现文件的目录结构,详情见目录结构。
    并在文件ReadMe.txt中,写入创建的磁盘块信息。

    final DefaultMutableTreeNode root = new DefaultMutableTreeNode(new fileSystem2.myFiles(rootFile, 0, 10240));
            if (!rootFile.exists()) {
                flag = false;
                try {
                    rootFile.mkdir();
                    readMe.createNewFile();
                } catch (Exception e) {
                    JOptionPane.showMessageDialog(null, "当前位置不支持创建目录!", "Error", JOptionPane.ERROR_MESSAGE);
                    System.exit(0);
                }
                FileWriter writer = new FileWriter(readMe.getPath());
                writer.write("this my fileSystem2\n");
                writer.write("Space: 10 * 1024K = 10M\n");
                writer.write("Free-Space Management:bitmap\n");
                writer.write("Store-Space Management:FAT\n");
                writer.flush();
                writer.close();
            }
    
            root.add(new DefaultMutableTreeNode(new fileSystem2.myFiles(readMe, 0, 0)));
            model.addRow(new fileSystem2.myFiles(readMe, 0, 0));
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    (3)创建磁盘块
    一共创建10个磁盘块,以磁盘1为例。

    block1 = new Block(1, new File(rootFile.getPath() + File.separator + "1"), flag);
    blocks.add(block1);
    
    root.add(new DefaultMutableTreeNode(new fileSystem2.myFiles(block1.getBlockFile(), 1, 1024.0)));
    model.addRow(new fileSystem2.myFiles(block1.getBlockFile(), 1, 1024.0));
    ((DefaultMutableTreeNode)root.getChildAt(0)).add(new DefaultMutableTreeNode("temp"));
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    (4)获取文件存储空间大小

    public double getSpace(File file){
            double space = 0;
            try {
                BufferedReader reader = new BufferedReader(new FileReader(file));
                reader.readLine();
                space = Double.parseDouble(reader.readLine());
                if (space > 1024){
                    space = 0.0;
                }
                reader.close();
            } catch (Exception e){};
            return space;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    (5)更新块信息

      public void upDateBlock(Block currentBlock){
            fileNumField.setText(String.valueOf(currentBlock.getFileNum()));
            usedField.setText(String.valueOf(currentBlock.getSpace()) + " KB");
            freeField.setText(String.valueOf(1024 - currentBlock.getSpace()) + "KB");
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    (6)将创建的工作区信息放入FCB文件中

    public void putFCB(File file, double capacity) throws IOException {
        FileWriter newFileWriter = new FileWriter(file);
        newFileWriter.write("File\r\n");
        newFileWriter.write(String.valueOf(capacity) + "\r\n");
        newFileWriter.write("Name: " + file.getName() + "\r\n");
        newFileWriter.write("Path: " + file.getPath() + "\r\n");
        String ctime = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").format(new Date(file.lastModified()));
        newFileWriter.write("Date last updated: " + ctime + "\r\n");
        newFileWriter.write("--------------------------edit file blew ------------------------------\r\n");
        newFileWriter.close();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    (7)删除当前目录

    public static void deleteDirectory(String filePath){
        File file = new File(filePath);
        if(!file.exists()){
            return;
        }
        if(file.isFile()){
            file.delete();
        }else if(file.isDirectory()){
            File[] files = file.listFiles();
            for (File myfile : files) {
                deleteDirectory(filePath + File.separator + myfile.getName());
            }
            file.delete();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    (8)创建文件
    通过调用File类的createFile()方法来实现。除了创建文件本身外,还使用了一串0和1来标识内存块的状态,以生成FCB文件。0为未使用,1表示已经被占用。创建文件时从最前方开始检索空闲块,确定使用后将0置为1。注意:文本中将自动生成FCB,不支持自行修改FCB。
    文件创建成功之后,将刷新FAT和位图。如果存储空间不足或是输入非法将导致文件生成失败。

    public boolean createFile(File file, double capacity) throws IOException {
        files.add(file);
        file.createNewFile();
        int cap[][] = new int[32][32];
        for (int i = 0; i < 32; i++){
            for (int k = 0; k < 32; k++)
                cap[i][k] = 0;
        }
        if (count > 0){
            JOptionPane.showMessageDialog(null, "内存不足!", "Fail", JOptionPane.ERROR_MESSAGE);
            file.delete();
            for (int i = 0; i < 32; i++){
                for (int k = 0; k < 32; k++){
                    if (cap[i][k] == 1){
                        bitmap[i][k] = 0;
                    }
                }
            }
            return false;
        }else{
            fileNum++;
            space += capacity;
            filesBit.put(file.getName(), cap);
            rewriteBitMap();
            rewriteRecoverWriter();
            putFCB(file, capacity); // Put FCB
            return true;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    (9)删除文件
    通过调用File类的delete()删除 针对文件夹需要递归删除,文件删除成功之后将刷新FAT和位图。刷新FAT文件的代码与创建文件的代码类似。

    public  boolean deleteFile(String filePath){
        boolean result = false;
        File file = new File(filePath);
        if(file.exists() && file.isFile()){
            result = file.delete();
        }
        return result;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    (10)文件|文件夹重命名
    通过调用File类的renameTo()方法进行重命名,如果相同目录下有相同的文件,则重命名将失败。文件|文件夹重命名时,FAT文件更改时采用先删除后增加的形式。

    public boolean renameFile(File file, String name, double capacity) throws IOException {
        String oldName = file.getName();
        int[][] tempBit = filesBit.get(oldName);
        String c = file.getParent();
        File mm;
        if(file.isFile()) {
            mm = new File(c + File.separator + name + ".txt");
            if (file.renameTo(mm)){
                file = mm;
                filesBit.remove(oldName);
                filesBit.put(file.getName(), tempBit);
                // Put FCB
                putFCB(file, capacity);
                for (int i = 0; i < files.size(); i++){
                    if (files.get(i).getName().equals(oldName)){
                        files.remove(i);
                        files.add(file);
                        break;
                    }
                }
                rewriteBitMap();
                rewriteRecoverWriter();
                return true;
            }else{
                return false;
            }
        }
        else {
            mm = new File(c + File.separator + name);
            file.renameTo(mm);
            return true;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    (11)搜索一个文件

    public boolean searchFile(String fileName, File parent){
            File [] files = parent.listFiles();
            for (File myFile:files){
                if (myFile.getName().equals(fileName)){
                    try {
                        if(Desktop.isDesktopSupported()) {
                            Desktop desktop = Desktop.getDesktop();
                            desktop.open(myFile);
                            return true;
                        }
                    } catch (IOException e1) {
                        JOptionPane.showMessageDialog(null, myFile.getPath() + " Sorry, something wrong!", "Fail to open",
                                JOptionPane.ERROR_MESSAGE);
                        return true;
                    }
                }
                if (myFile.isDirectory() && myFile.canRead()){
                    if(searchFile(fileName, myFile)){
                        return true;
                    }
                }
            }
            return false;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    (12)目录格式化
    递归删除该目录下的所有文件,并更新FAT和位图。

    JMenuItem formatItem = new JMenuItem("格式化文件|目录");
    formatItem.addActionListener(new ActionListener() {
        @Override
        public void actionPerformed(ActionEvent e) {
            DefaultMutableTreeNode node = (DefaultMutableTreeNode) tree.getLastSelectedPathComponent();
            fileSystem2.myFiles temp = (fileSystem2.myFiles)node.getUserObject();
            int blokName = temp.getBlockName();
            Block currentBlock = blocks.get(blokName - 1);
            int choose = JOptionPane.showConfirmDialog(null, "确认格式化当前目录?", "confirm", JOptionPane.YES_NO_OPTION);
            if (choose == 0){
                try{
                if (temp.getMyFile().isDirectory()) {
                    for (File myfile : temp.getMyFile().listFiles()) {
                        currentBlock.deleteFile(myfile, getSpace(myfile));
                    }
                    upDateBlock(currentBlock);
                    JOptionPane.showMessageDialog(null, "格式化成功!请重新打开以刷新现在的目录!", "Success", JOptionPane.DEFAULT_OPTION);
                    currentBlock.rewriteBitMap();
                }
                }catch (Exception E1){
                    JOptionPane.showMessageDialog(null, "格式化失败!", "Error", JOptionPane.ERROR_MESSAGE);
                }
            }
        }
    });
    myMenu.add(formatItem);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    2.4 Swing中的基本容器JFrame

    (1)文件导航窗口FileChooser
    参考:https://blog.csdn.net/liang5630/article/details/25651491

    在这里插入图片描述

            String path = File.listRoots()[0].getPath();
            String rootPath = new String();
            chooser = new JFileChooser(path);
            chooser.setDialogTitle("选择一个实现fileSystem2的目录");
            chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
            chooser.setPreferredSize(new Dimension(800, 600));
            int result = chooser.showOpenDialog(this);
            if (result == chooser.APPROVE_OPTION){
                System.out.println(chooser.getSelectedFile().getAbsolutePath());
                rootPath = chooser.getSelectedFile().getPath();
            }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    (2)帮助页面help
    本部分为优化后内容,参考文献[6]。此外,技术修改部分还参考了
    JLabel,参考https://blog.csdn.net/qq_21808961/article/details/80658063。
    JScrollPane ,参考https://blog.csdn.net/Sun_Ru/article/details/70170018。
    setPreferredSize(Dimension preferredSize) 设置此组件的首选大小。如果preferredSize为 null,则要求 UI 提供首选大小。
    JOptionPane.showMessageDialog,参考https://blog.csdn.net/weixin_43876121/article/details/100982551
    JOptionPane.DEFAULT_OPTION),没有信息提示框的图标,将图标舍弃的方法。

            JLabel help = new JLabel(helpMessage);
            help.setFont(new Font("微软雅黑", Font.CENTER_BASELINE, 20));
            JScrollPane helpPane = new JScrollPane(help);
            helpPane.setPreferredSize(new Dimension(500, 400));
            JOptionPane.showMessageDialog(null,
                    helpPane,
                    "文件系统",
                    JOptionPane.DEFAULT_OPTION);
    通过简单的HTML显示帮助系统的文字部分
        private static String helpMessage =
                "" +
                        "" +
                        "

    文件系统

    "
    + "

    操作说明

    "
    + "
    • 必须先选择一个计算机上的文件夹作为模拟工作目录
    • 左侧树状结构即文件目录
    • 双击或点击文件夹左侧小图标可以打开文件目录
    • " + "
    • 右侧空白处为表格区域,将现实相关文件信息
    • 双击表格中的项可以直接打开相关文件
    • "
      + "
    • 下方绿色为盘信息面板,将现实相应盘的相应信息
    • "
      + "
    • 最下方空白处将显示内存块当前状况
    "
    + "
  • 在树状结构中选中某一节点,右键即可选择相应的文件操作
  • "
    + "
  • 创建新的文件会要求输入文件名和文件大小(KB)
  • "
    + "" + "";
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    (3)文件显示框
    显示框位于程序右侧,用于显示文件的名称、路径、类型、大小和最近修改时间。
    使用JTable显示文件。先设置基础的颜色小等信息后,还需要编写打开文件的逻辑。
    addMouseListener()方法参考https://blog.csdn.net/weixin_44870139/article/details/106290333。
    public void mouseClicked(MouseEvent e)//鼠标按键在组件上单击(按下并释放)时调用
    getClickCount():获取鼠标被点击的次数
    通过选择对应点击的文件,返回该文件位置和名称给显示的弹窗。

    fileTable = new JTable(model);
    fileTable.getTableHeader().setFont(new Font(Font.DIALOG,Font.CENTER_BASELINE,24));
    fileTable.setSelectionBackground(Color.BLUE); //设置选中后的颜色为蓝色
    
    fileTable.updateUI();
    
    fileTable.addMouseListener(new MouseAdapter() {
        @Override
        public void mouseClicked(MouseEvent e) {
            super.mouseClicked(e);
            if (e.getClickCount() == 2 && e.getButton() == MouseEvent.BUTTON1){
                String fileName = ((String) model.getValueAt(fileTable.getSelectedRow(), 0));
                String filePath = ((String) model.getValueAt(fileTable.getSelectedRow(), 1));
                try {
                    if(Desktop.isDesktopSupported()) {
                        Desktop desktop = Desktop.getDesktop();
                        desktop.open(new File(filePath));
                    }
                } catch (IOException e1) {
                    JOptionPane.showMessageDialog(null, "Sorry, some thing wrong!", "Fail to open",
                            JOptionPane.ERROR_MESSAGE);
                }
                JOptionPane.showMessageDialog(null, "File Name: " + fileName + "\n File Path: " + filePath, "content",
                        JOptionPane.INFORMATION_MESSAGE);
            }
        }
    });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    (4)显示文件体系JTree
    文件树位于左侧,呈现文件的目录结构,详情见目录结构。
    JTree显示等级体系的数据,参考https://blog.csdn.net/sky_zhe/article/details/7552116。

    tree.putClientProperty(“Jtree.lineStyle”,  “Horizontal”); 仅使用水平线隔开一组节点
    tree.putClientProperty(“Jtree.lineStyle”,  “None”); 在节点间不显示任何行线
            final DefaultTreeModel treeModel = new DefaultTreeModel(root);
            tree = new JTree(treeModel);
            tree.setEditable(false);
            tree.putClientProperty("Jtree.lineStyle",  "Horizontal");
            tree.getSelectionModel().setSelectionMode
                    (TreeSelectionModel.SINGLE_TREE_SELECTION);
            tree.setShowsRootHandles(true);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    对于树节点的选择做出响应,可以实现一个树节点选择监听器,并且注册在这棵树上。(注:由于实现该部分时不断报错,限于现有能力等因素,这部分直接参考的文献[6]。)

    tree.addTreeSelectionListener(new TreeSelectionListener() {
        @Override
        public void valueChanged(TreeSelectionEvent e) {
            DefaultMutableTreeNode parent = null;
            TreePath parentPath = e.getPath();
            if (parentPath == null){
                parent = root;
            }else{
                parent = (DefaultMutableTreeNode) (parentPath.getLastPathComponent());
            }
            int blokName = ((fileSystem2.myFiles)parent.getUserObject()).getBlockName();
            Block currentBlock = blocks.get(blokName - 1);
            if (parentPath == null){
                parent = root;
            }else{
                parent = (DefaultMutableTreeNode) (parentPath.getLastPathComponent());
            }
    
            nameField.setText(String.valueOf(blokName));
            upDateBlock(currentBlock);
    
            model.removeRows(0, model.getRowCount());
            File rootFile = new File(((fileSystem2.myFiles)parent.getUserObject()).getFilePath());
            if (parent.getChildCount() > 0) {
                File[] childFiles = rootFile.listFiles();
    
                for (File file : childFiles) {
                    model.addRow(new fileSystem2.myFiles(file, blokName, getSpace(file)));
                }
            }
            else{
                model.addRow(new fileSystem2.myFiles(rootFile, blokName, getSpace(rootFile)));
            }
            fileTable.updateUI();
    
        }
    });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37

    上面的代码执行了以下任务:
    1.获得树的默认TreeSelectionModel(节点选择模式),然后设置它,使得在某一时刻只有一个节点被选中。
    2.注册了一个事件处理器。事件处理器是一个实现了TreeSelectionListener接口的对象。
    3. 在事件处理器中,通过调用Tree的getLastSelectedPathComponent方法获得选中的节点。
    4.使用getUserObject方法获得节点关联的数据。(节点node是一个非平凡组件,要通过它关联的数据模型获得真正的数据)

    为使得树中节点的文本值可编辑,对tree调用setEditable(true)。当用户完成一个节点的编辑时,这个model产生一个tree model事件,以告诉所有监听者(包括 Jtree):树节点被改变了。为了通知“节点改变”,实现一个TreeModelListener。当用户为一个树节点输入一个新名字时,事件会被检测到。Tree-will-expand listener 挡住了节点的展开或折叠(就是监听器侦听在展开折叠正在发生之前),在每次要展开一个节点前,监听器会提示你给出确认信息,确认是否展开。(该部分同样未能实现,参考文献[6])

    tree.addTreeWillExpandListener(new TreeWillExpandListener() {
    @Override
    public void treeWillExpand(TreeExpansionEvent event) throws ExpandVetoException {
        DefaultMutableTreeNode parent = null;
        TreePath parentPath = event.getPath();
        if (parentPath == null){
            parent = root;
        }else{
            parent = (DefaultMutableTreeNode) (parentPath.getLastPathComponent());
        }
    
        int blokName = ((fileSystem2.myFiles)parent.getUserObject()).getBlockName();
    
        File rootFile = new File(((fileSystem2.myFiles)parent.getUserObject()).getFilePath());
        File [] childFiles = rootFile.listFiles();
    
        model.removeRows(0, model.getRowCount());
        for (File myFile : childFiles){
            DefaultMutableTreeNode node = null;
            node = new DefaultMutableTreeNode(new fileSystem2.myFiles(myFile, blokName, getSpace(myFile)));
            if (myFile.isDirectory() && myFile.canRead()) {
                node.add(new DefaultMutableTreeNode("temp"));
            }
    
            treeModel.insertNodeInto(node, parent,parent.getChildCount());
            model.addRow(new fileSystem2.myFiles(myFile, blokName, getSpace(myFile)));
        }
        if (parent.getChildAt(0).toString().equals("temp") && parent.getChildCount() != 1)
            treeModel.removeNodeFromParent((MutableTreeNode) parent.getChildAt(0));
        fileTable.updateUI();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    (5)菜单项
    JPopupMenu 和JMenuItem参考https://blog.csdn.net/bananachong/article/details/81736028。

    final JPopupMenu myMenu = new JPopupMenu();
    myMenu.setPreferredSize(new Dimension(300, 200));
    
    • 1
    • 2

    包括create file(创建文件)、create dir(创建文件夹)、delete(删除文件|文件夹)、format(格式化文件|文件夹)、rename(重命名文件|文件夹)。这部分前端有异曲同工之妙,因此只展示创建文件的前端代码。
    Create file创建一个文件并且更新显示文件的fileTable。

    JMenuItem createFileItem = new JMenuItem("create a file");
    createFileItem.addActionListener(new ActionListener() {
        @Override
        public void actionPerformed(ActionEvent e) {
            DefaultMutableTreeNode node = (DefaultMutableTreeNode) tree.getLastSelectedPathComponent();
            fileSystem2.myFiles temp = (fileSystem2.myFiles)node.getUserObject();
            int blokName = temp.getBlockName();
            Block currentBlock = blocks.get(blokName - 1);
            String inputValue;
            double capacity;
            JOptionPane inputPane = new JOptionPane();
            inputPane.setPreferredSize(new Dimension(600, 600));
            inputPane.setInputValue(JOptionPane.showInputDialog("File name:"));
            if (inputPane.getInputValue() == null) {
                return;
            }
            inputValue = inputPane.getInputValue().toString();
            inputPane.setInputValue(JOptionPane.showInputDialog("Large(KB):"));
            if (inputPane.getInputValue() == null) {
                return;
            }
            capacity = Double.parseDouble(inputPane.getInputValue().toString());
    
            File newFile = new File(temp.getFilePath() + File.separator + inputValue + ".txt");
            if (!newFile.exists() && !inputValue.equals(null)){
                try {
                    if (currentBlock.createFile(newFile, capacity)) {
                        DefaultMutableTreeNode newNode = new DefaultMutableTreeNode(new fileSystem2.myFiles(newFile, blokName, capacity));
                        model.removeRows(0, model.getRowCount());
                        model.addRow(new fileSystem2.myFiles(newFile, blokName, capacity));
                        fileTable.updateUI();
                        upDateBlock(currentBlock);
                        JOptionPane.showMessageDialog(null, "Create success! Reopen the parent dir to reflash!", "Success", JOptionPane.DEFAULT_OPTION);
                    }
                } catch (IOException e1) {
                    JOptionPane.showMessageDialog(null, "Create fail!!!", "Error", JOptionPane.ERROR_MESSAGE);
                }
            }
        }
    });
    myMenu.add(createFileItem);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41

    3文件系统实现功能

    3.1 正式进入文件系统前

    (1)选择一个实现系统的目录
    在这里插入图片描述

    (2)文件系统的帮助页面

    在这里插入图片描述

    (3)创建后自动写入的ReadMe文件

    在这里插入图片描述

    3.2文件系统

    文件目录将呈现在程序的左侧,以十个磁盘为基目录。点击文件夹左侧箭头,可以将文件夹展开。注意:当执行创建和删除文件等操作,必须重新展开一次父目录才能刷新。
    在这里插入图片描述

    3.3文件系统的各项操作

    (1)菜单栏
    右击树状文件显示的目录,选中某一文件后,右键展开文件系统功能选项的弹窗。
    在这里插入图片描述

    (2)创建文件|目录
    选择第一项创建文件,会弹出对话框要求键入文件名和文件大小。文件生成成功之后只要重新展开父目录就可以看到新生的文件。双击程序右侧的显示区的文件,可以打开该文件看到它的FCB。

    在这里插入图片描述

    创建一个目录时的弹窗。
    在这里插入图片描述

    创建成功时的弹窗界面,及当创建的文件过大时的弹窗界面。
    在这里插入图片描述

    刷新后的文件系统。
    在这里插入图片描述

    (3)删除文件|目录
    删除文件|目录的弹窗界面,及删除成功时的弹窗界面。

    在这里插入图片描述

    刷新后的文件系统。
    在这里插入图片描述

    (4)格式化目录
    格式化目录的弹窗界面,及格式化成功时的弹窗界面。
    在这里插入图片描述

    格式化前后的文件系统。目录111格式化后,里面其他的文件都被清空了。(temp为空)
    在这里插入图片描述
    在这里插入图片描述

    (5)重命名文件|目录
    重命名文件|目录的弹窗界面,及重命名成功时的弹窗界面。
    在这里插入图片描述

    刷新前后的文件系统。
    在这里插入图片描述
    在这里插入图片描述

    (6)查找文件
    在最上方进行文件及目录的查找。搜索框位于顶部,可以在搜索栏中键入文件名或者文件夹名,然后点击“start”来进行检索,如果检索成功将直接打开,失败则弹框提醒
    在这里插入图片描述

    3.4操作后的文件

    1BitMap&&Fat.txt文件存储当前磁盘1所有存储块的使用情况,记录了所有文件所占用内存块的情况,及对应内存块的位置。
    在这里插入图片描述

    3.5单个文件的操作

    (1)打开文件
    双击程序中右侧的显示面板中的文件,即可打开对应文件。
    在这里插入图片描述

    (2)关闭文件
    打开文件之后点击文件上方的关闭按钮,就可以将对应文件关闭。
    (3)写文件
    打开文件之后,在FCB的内容下方输入文本即可。
    在这里插入图片描述

    (4)读文件
    双击程序右侧的显示面板中的文件,即可打开对应文件进行查看。
    在这里插入图片描述

    4 心得与体会

    4.1 参考文献

    [1] 操作系统课本P233-P277
    [2] little_qi.c++实现的十分简易文件系统.
    https://blog.csdn.net/little_qi/article/details/73801336?spm=1001.2101.3001.6650.3&utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7ECTRLIST%7Edefault-3.no_search_link&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7ECTRLIST%7Edefault-3.no_search_link&utm_relevant_index=6
    [3] 小森、. 操作系统:文件系统的实现.
    https://blog.csdn.net/weixin_44759105/article/details/111835946?utm_source=app&app_version=4.21.0&code=app_1562916241&uLinkId=usr1mkqgl919blen
    [4] qq_38474647. 基于JAVA实现的文件系统.
    https://blog.csdn.net/qq_38474647/article/details/101471229?utm_source=app&app_version=4.21.0&code=app_1562916241&uLinkId=usr1mkqgl919blen
    [5] ZhihengTao. linux下简单模拟文件系统.https://blog.csdn.net/zhihengtao/article/details/50945771
    [6] Jensen95. 【操作系统】简单FAT文件系统实现.https://segmentfault.com/a/1190000004321217
    [7] 操作系统 课程实验-实验五 磁盘文件操作.https://www.doc88.com/p-999520617983.html?r=1

    4.2 系统的优点与创新

    (1)平台可移植性
    ① 文件名称分隔符
    在文献[5]的启发下,发现在windows中文件文件分隔符用 ’ \ ’ 或者 ’ / ’ 都可以。但是在 Linux 中,是不识别 ’ \ ’ 的,在UNIX系统上,此字段的值为 ’ / ',在 Microsoft Windows 系统上,它为 ’ \ ’ 屏蔽了这些系统的区别。
    为了提高系统的多平台移植性,参考https://blog.csdn.net/qq_36537546/article/details/88421680,使用系统默认的文件分隔符号File.separator以保证在任何系统下不会出错。
    1、separatorChar为与系统有关的默认名称分隔符;
    2、pathSeparatorChar为与系统有关的路径分隔符;
    3、pathSeparator 此字符用于分隔以路径列表形式给定的文件序列中的文件名。在 UNIX 系统上此字段为 ’ : ‘;在Microsoft Windows系统上为 ’ ;’。
    改进后的代码示例:

    1. blockBitMap = new File(blockFile.getPath() + File.separator + blockName + “BitMap&&Fat.txt”);

    ② 显式链接
    通过if(!Desktop.isDesktopSupported()),来测试当前平台是否支持显式链接。

    (2)空间管理方式
    在文献[7]的启发下,优化了空间管理方式。
    存储空间:采用显式链接FAT,用一个专门的文件BitMap&&Fat.txt记录盘区内所有文件所占用内存块的情况,不存在内外内存碎片,最大程度地利用了盘区空间。
    空闲空间:采用了位图来标识内存块的状态。文件BitMap&&Fat.txt存储了盘区的存储块的状态,以及盘区内存在的文件按照链接方式实现的的内存分配情况,列出了对应内存块的位置,创建文件时从最前方开始检索空闲块,确定使用后将0置为1。空闲空间一目了然,易于管理。
    读取管理:采用文件控制块FCB为每个文件建立专门的用以收集必要属性信息的数据结构,内容包括:大小、文件名、地址、最近更新时间,以方便后续的读取管理。

    (3)文件目录结构及备份
    实验要求的是二级目录,但本实验中实现了多级目录结构。
    在文献[6]的启发下,退出该文件系统时,创建recover.txt文件作为当前磁盘的备份文档,在再次启动该应用程序时,通过读取该文件的信息到内存,还原出上次的运行状态,从而实现复盘。

    (4)前端的页面
    参考https://blog.csdn.net/liang5630/article/details/25651491,实现文件选择对话框等等。具体可见前端代码部分,有对于java前端swing及awt的学习以及调用。

    4.3 系统的改进与推广

    由于时间等方面限制,主要模拟了文件系统的各项功能,但并没有真正地为文件开了这么大的空间。且仅支持生成txt,文本文件中直接现实FCB。不支持修改内容,对于非法输入都会直接导致文件生成失败。此外,如果存档文件recover.txt被破坏,将无法打开文件。此外,文件系统还应允许多个用户共享一个文件副本。这一服务的目的是在辅存设备上只保留一个单一的应用程序和数据的副本,以提高设备利用率。但此功能需要提高对文件的保护,因此本次实验没有完成这一功能。
    还可以进一步优化文件系统的性能和效率。书上说,磁盘空间的有效使用(效率),取决于磁盘分配和目录管理算法,和保留在文件目录条目中的数据类型。因此改善性能的方法有缓冲。1.缓冲区缓存:一块独立内存,位于其中的块是马上需要使用的;2.页面缓存:将文件数据作为页而不是块来缓存。页面缓存使用虚拟内存技术,将文件数据作为页来缓存,比采用物理磁盘块来缓存更高效。
    前端可以进一步优化。如通过Jtree可以自定义文件系统前端显示的节点样式,叶子节点、可展开分支节点和可折叠分支节点的默认图标等。可以创建一个DefaultTreeCellRenderer实例,让喜欢的任何组件重复利用。接着,通过调用以下一个或多个方法去指定图标: setLeafIcon(对于叶子节点),setOpenIcon(对于可展开分支节点),setClosedIcon(对于可折叠节点)。创建了这些图标后,使用树的 setCellRender 方法去指定这个DefaultTreeCellRender来绘制它的节点。想更精巧的控制节点图标,可以创建 DefaultTreeCellRender的子类,然后覆盖getTreeCellRendererComponent方法。此外,可以利用TreeWillExpandListener接口懒加载一棵树的孩子,以避免应用程序的初始化时占用更多的类加载跟实例化时间,加快了应用程序的初始化加载时间。

    4.4心得体会

    在这大半个月的开发时间里,通过实现这个文件系统,我对操作系统有了更深一步的了解。由于在上课时没有注重对文件系统运用的学习,所以刚开始时在这方面有较大的困难,只能先将书本看了一遍。了解了磁盘组织与管理,以及文件系统的实现。在系统运行时,计算机以进程为基本单位进行资源的调度和分配;而在用户进行的输入、输出中,则以文件为基本单位。
    后来逐步上手后,想着这学期学了java,而且学了一学期前端,前端自我感觉极其良好,就想着也许我可以弄个可视化界面。然后写前端时不断报错,心态崩了,没想到CSDN博主介绍的“简单”的调包的树形结构,竟然还需要灵活运用之前学的数据结构,自己写了一学期表单form,竟然就以为自己会前端了,太天真了。结果到最后也还没弄明白怎么利用TreeWillExpandListener接口懒加载一棵树的孩子,或许过段时间再看看会有新的认识。
    本次实验让我认识到了,代码之间是没有边界的。比如之前学的数据结构等等,不是摆设,在涉及到更复杂的代码时,都是有用的。此外操作系统原理则的重要理论基础,是进一步提升代码能力的基石。这学期的整体感受:学习课本上的知识是不够的,还要多动手编写程序,才能发现学习上的漏洞,不断提高自己转化知识的能力。既然已经发现了所有学习的知识都会被用到,那么希望自己现在能打牢基础,无止境地学习吸收新的知识,在这条路上越走越远。
    感谢这学期胡书山老师对我实验的严格要求,被迫进步地很快。感谢同学们的帮助,还有(前)男友在我几次快放弃时的陪伴,一学期的折腾,感觉我的代码终于开始逐渐入门了。

    实验报告效果图

    话说回来,当年的我真卷啊。。。。

    在这里插入图片描述

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

  • 相关阅读:
    docker login 密码查看和加密保存
    “唯品会VIP商品API:一键获取奢侈品详情,尊享品质生活!“
    【读点论文】结构化剪枝
    SSO(single sign on)模式 --单点登录三种登录方式
    嵌入式属于人工智能吗?
    Keil C251条件编译宏的应用实例
    Nginx 快速入门
    Ant Design Pro【面包屑导航】二级路由和三级路由都有component的情况,三级不显示component的页面,怎么解决?
    sklearn机器学习:决策树案例
    【软件工程】介绍
  • 原文地址:https://blog.csdn.net/wtyuong/article/details/127437533