├── .gitignore ├── LICENSE ├── README.md ├── pom.xml ├── src └── main │ └── java │ └── com │ └── spice │ ├── FileManagementSystem.java │ ├── constant │ ├── DirectoryConstant.java │ ├── DiskConstant.java │ └── FileConstant.java │ ├── data │ ├── DataCache.java │ └── Memory.java │ ├── entity │ ├── ActiveFile.java │ ├── Directory.java │ ├── Disk.java │ ├── FileControlBlock.java │ └── User.java │ ├── enums │ └── ProtectType.java │ ├── result │ ├── CommonResult.java │ └── ResultConstant.java │ ├── service │ ├── DirectoryService.java │ ├── DiskService.java │ ├── DisplayService.java │ ├── FileService.java │ ├── UserService.java │ └── impl │ │ ├── DirectoryServiceImpl.java │ │ ├── DiskServiceImpl.java │ │ ├── DisplayServiceImpl.java │ │ ├── FileServiceImpl.java │ │ └── UserServiceImpl.java │ └── util │ ├── CollectionUtil.java │ ├── FileUtil.java │ ├── Math.java │ └── StringUtil.java └── 文件管理系统 操作系统课程设计.doc /.gitignore: -------------------------------------------------------------------------------- 1 | HELP.md 2 | target/ 3 | !.mvn/wrapper/maven-wrapper.jar 4 | !**/src/main/**/target/ 5 | !**/src/test/**/target/ 6 | 7 | ### STS ### 8 | .apt_generated 9 | .classpath 10 | .factorypath 11 | .project 12 | .settings 13 | .springBeans 14 | .sts4-cache 15 | 16 | ### IntelliJ IDEA ### 17 | .idea 18 | *.iws 19 | *.iml 20 | *.ipr 21 | 22 | ### NetBeans ### 23 | /nbproject/private/ 24 | /nbbuild/ 25 | /dist/ 26 | /nbdist/ 27 | /.nb-gradle/ 28 | build/ 29 | !**/src/main/**/build/ 30 | !**/src/test/**/build/ 31 | 32 | ### VS Code ### 33 | .vscode/ -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # FileManagementSystem 2 | 3 | > 这个项目来自于广东工业大学的操作系统课程设计题目(多级文件系统-2)的实现 4 | 5 | ## 介绍 6 | 7 | 以下是该课设的题目要求: 8 | 9 | ### 题目 10 | 11 | 多级文件系统-2 12 | 13 | ### 指导老师 14 | 15 | 李敏老师 16 | 17 | ### 主要内容 18 | 19 | 要求设计一个模拟的多用户多级目录的文件系统。通过具体的文件存储空间的管理、文件的物理结构、目录结构和文件操作的实现,加深对文件系统内部功能和实现过程的理解 20 | 21 | ### 基本任务要求 22 | 23 | * 在内存中开辟一个==虚拟磁盘空间==作为文件存储器,在其上实现一个==多用户多目录==的文件系统 24 | * 文件物理结构可采用==连续结构== 25 | * 磁盘空闲空间的管理选择==位示图== 26 | * 文件目录结构采用多用户多级目录结构,每个目录项包含文件名、物理地址、长度等信息,还可以通过目录项实现对文件的读和写的保护 27 | * 设计一个较实用的用户界面,方便用户使用。要求提供以下==相关文件操作==: 28 | * 用户登录(login) 29 | * 系统初始化(建文件卷、提供登录模块) 30 | * 创建文件(create) 31 | * 打开文件(open) 32 | * 读取文件(read) 33 | * 写入文件(write) 34 | * 关闭文件(close) 35 | * 删除文件(delete) 36 | * 创建目录(建立子目录)(mkdir) 37 | * 改变当前目录(cd) 38 | * 列出文件目录(dir) 39 | * 退出(logout) 40 | * 系统必须可实际演示,选用程序设计语言:C++、C等 41 | 42 | ### 参考文献 43 | 44 | [1] 计算机操作系统, 汤小丹等 ,西安电子科技大学出版社 45 | 46 | [2] 操作系统实验指导书,傅秀芬,广东工业大学(自编) 47 | 48 | [3] 计算机操作系统教程 ( 第二版 ), 张尧学、 史美林,清华大学出版社 49 | 50 | [4] 现代操作系统,A.S.Tanenbaum 著,陈向群等译机械工业出版社 51 | 52 | 53 | 54 | ## 本系统的实现 55 | 56 | > 下面仅做简略的介绍。更详细的系统设计、实现、测试请查看 `文件管理系统 操作系统课程设计.doc` 文件 57 | 58 | ### 编程语言 59 | 60 | Java 61 | 62 | ### 实现的功能 63 | 64 | 实现的功能包括了上面“基本任务要求”中的所提到的所有相关文件操作。除此之外,还实现了: 65 | 66 | * 文件路径解析 67 | * 查看磁盘位示图 68 | * 查看系统帮助列表 69 | * 保存磁盘数据 70 | * 加载磁盘数据 71 | 72 | ### 环境要求 73 | 74 | JDK 8及以上、maven环境 75 | 76 | ### 注意事项 77 | 78 | 因为要保存磁盘数据,所以在运行文件系统并退出文件系统之后,会在项目文件夹的同级目录下生成一个 save 文件夹,里面会创建一个 disk.ser 文件用于保存文件系统的磁盘数据 79 | 80 | 81 | 82 | ## 改进方案 83 | 84 | > 本项目在进行操作系统课设答辩的时候拿到了优(成绩等级有:优、良、中等、及格、不及格) 85 | 86 | 下面列出一些对该项目的改进方案,大家可参考并对其改进: 87 | 88 | - [ ] 实现文件共享 89 | - [ ] 实现对文件的存取保护 90 | - [ ] 对磁盘碎片进行整理[1] 91 | - [ ] 优化树形目录结构的数据结构 92 | - [ ] 增设更多合理的文件操作[2] 93 | 94 | [1] 在进行磁盘空间的分配时,本项目采用的是连续分配,这必然会导致产生很多磁盘碎片。优化的方法可以是;①定期对磁盘空间进行“紧凑”,这样可以将碎片合并成较大的磁盘空间; ②采用更优的磁盘空间分配方式,例如离散分配 95 | 96 | [2] 还可以增设复制文件操作、剪切文件操作等更多合理的文件操作 97 | 98 | 99 | 100 | ## 最后 101 | 102 | 本项目仅供学习交流使用 -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | com.spice 8 | file-management-system 9 | 1.0-SNAPSHOT 10 | 11 | 12 | 13 | org.projectlombok 14 | lombok 15 | 1.18.20 16 | 17 | 18 | 19 | 20 | 8 21 | 8 22 | 23 | 24 | -------------------------------------------------------------------------------- /src/main/java/com/spice/FileManagementSystem.java: -------------------------------------------------------------------------------- 1 | package com.spice; 2 | 3 | import com.spice.constant.DiskConstant; 4 | import com.spice.data.DataCache; 5 | import com.spice.data.Memory; 6 | import com.spice.entity.*; 7 | import com.spice.result.CommonResult; 8 | import com.spice.service.*; 9 | import com.spice.service.impl.*; 10 | import com.spice.util.StringUtil; 11 | 12 | import java.time.LocalDateTime; 13 | import java.util.*; 14 | 15 | /** 16 | * @author spice 17 | * @date 2021/06/19 19:06 18 | */ 19 | public class FileManagementSystem { 20 | 21 | /** 22 | * 虚拟磁盘持久化文件的保存路径 23 | */ 24 | private static final String SAVE_PATH = "../save/disk.ser"; 25 | 26 | /** 27 | * 表示匹配多个空格 28 | */ 29 | private static final String SEPARATOR = "\\s+"; 30 | 31 | private static final String YES_RESPONSE = "yes"; 32 | 33 | private static final UserService userService = new UserServiceImpl(); 34 | private static final DirectoryService directoryService = new DirectoryServiceImpl(); 35 | private static final DiskService diskService = new DiskServiceImpl(); 36 | private static final DisplayService displayService = new DisplayServiceImpl(); 37 | private static final FileService fileService = new FileServiceImpl(); 38 | 39 | public static void main(String[] args) { 40 | Scanner scanner = new Scanner(System.in); 41 | String[] command; 42 | 43 | // 加载磁盘数据 44 | CommonResult loadResult = diskService.loadDisk(SAVE_PATH); 45 | if (loadResult.isSuccess()) { 46 | System.out.println(loadResult.getMessage()); 47 | } else { 48 | System.out.println(loadResult.getMessage()); 49 | System.out.print("是否初始化一个新的磁盘?(yes/no): "); 50 | if (scanner.nextLine().equalsIgnoreCase(YES_RESPONSE)) { 51 | initDisk(); 52 | } else { 53 | System.exit(0); 54 | } 55 | } 56 | 57 | while (true) { 58 | showUserAndDirectory(); 59 | command = inputResolve(scanner.nextLine()); 60 | switch (command[0]) { 61 | // 注册用户 62 | case "register": 63 | CommonResult registerResult = userService.register(command.length > 1 ? command[1] : "", 64 | command.length > 2 ? command[2] : ""); 65 | System.out.println(registerResult.getMessage()); 66 | break; 67 | // 用户登录 68 | case "login": 69 | CommonResult loginResult = userService.login(command.length > 1 ? command[1] : "", 70 | command.length > 2 ? command[2] : ""); 71 | if (!loginResult.isSuccess()) { 72 | System.out.println(loginResult.getMessage()); 73 | } 74 | break; 75 | // 用户注销 76 | case "logout": 77 | CommonResult logoutResult = userService.logout(); 78 | if (!logoutResult.isSuccess()) { 79 | System.out.println(logoutResult.getMessage()); 80 | } 81 | break; 82 | // 创建目录 83 | case "mkdir": 84 | if (Objects.isNull(Memory.getInstance().getCurrentUser())) { 85 | System.out.println("[创建目录失败]: 请先登录"); 86 | break; 87 | } 88 | CommonResult mkdirResult = directoryService.makeDirectory(command.length > 1 ? command[1] : ""); 89 | if (!mkdirResult.isSuccess()) { 90 | System.out.println(mkdirResult.getMessage()); 91 | } 92 | break; 93 | // 切换目录 94 | case "cd": 95 | if (Objects.isNull(Memory.getInstance().getCurrentUser())) { 96 | System.out.println("[切换目录失败]: 请先登录"); 97 | break; 98 | } 99 | CommonResult cdResult = directoryService.changeDirectory(command.length > 1 ? command[1] : ""); 100 | if (!cdResult.isSuccess()) { 101 | System.out.println(cdResult.getMessage()); 102 | } 103 | break; 104 | // 查看目录 105 | case "dir": 106 | if (Objects.isNull(Memory.getInstance().getCurrentUser())) { 107 | System.out.println("[查看目录失败]: 请先登录"); 108 | break; 109 | } 110 | CommonResult> dirResult = directoryService.showDirectory(Memory.getInstance().getCurrentDirectory()); 111 | if (dirResult.isSuccess()) { 112 | displayService.printFileList(dirResult.getData()); 113 | } else { 114 | System.out.println(dirResult.getMessage()); 115 | } 116 | break; 117 | // 创建文件 118 | case "create": 119 | if (Objects.isNull(Memory.getInstance().getCurrentUser())) { 120 | System.out.println("[创建文件失败]: 请先登录"); 121 | break; 122 | } 123 | CommonResult createResult = fileService.createFile(command.length > 1 ? command[1] : ""); 124 | if (!createResult.isSuccess()) { 125 | System.out.println(createResult.getMessage()); 126 | } 127 | break; 128 | // 打开文件 129 | case "open": 130 | if (Objects.isNull(Memory.getInstance().getCurrentUser())) { 131 | System.out.println("[打开文件失败]: 请先登录"); 132 | break; 133 | } 134 | CommonResult openResult = fileService.openFile(command.length > 1 ? command[1] : ""); 135 | if (openResult.isSuccess()) { 136 | displayService.printOpenedFile(openResult.getData()); 137 | } else { 138 | System.out.println(openResult.getMessage()); 139 | } 140 | break; 141 | // 读取文件 142 | case "read": 143 | if (Objects.isNull(Memory.getInstance().getCurrentUser())) { 144 | System.out.println("[读取文件失败]: 请先登录"); 145 | break; 146 | } 147 | try { 148 | CommonResult readResult = fileService.readFile(command.length > 1 ? Integer.parseInt(command[1]) : 0); 149 | if (readResult.isSuccess()) { 150 | System.out.println(readResult.getData()); 151 | } else { 152 | System.out.println(readResult.getMessage()); 153 | } 154 | break; 155 | } catch (NumberFormatException e) { 156 | System.out.println("[未能识别\"" + command[1] + "\"]: 请输入\"help\"查看帮助"); 157 | break; 158 | } 159 | // 写入文件 160 | case "write": 161 | if (Objects.isNull(Memory.getInstance().getCurrentUser())) { 162 | System.out.println("[写入文件失败]: 请先登录"); 163 | break; 164 | } 165 | 166 | StringBuilder record = new StringBuilder(); 167 | // 循环读取用户的输入,直到输入的内容以"###"结尾 168 | while (true) { 169 | String line = scanner.nextLine(); 170 | if (line.endsWith("###")) { 171 | record.append(line, 0, line.length() - 3); 172 | break; 173 | } else { 174 | record.append(line).append("\n"); 175 | } 176 | } 177 | 178 | CommonResult writeResult = fileService.writeToFile(record.toString()); 179 | if (writeResult.isSuccess()) { 180 | System.out.println(); 181 | // 显示修改后的文件记录 182 | displayService.printOpenedFile(Memory.getInstance().getActiveFile()); 183 | } else { 184 | System.out.println(writeResult.getMessage()); 185 | } 186 | break; 187 | // 删除文件 188 | case "delete": 189 | if (Objects.isNull(Memory.getInstance().getCurrentUser())) { 190 | System.out.println("[删除文件失败]: 请先登录"); 191 | break; 192 | } 193 | CommonResult deleteResult = fileService.deleteFile(command.length > 1 ? command[1] : ""); 194 | if (!deleteResult.isSuccess()) { 195 | System.out.println(deleteResult.getMessage()); 196 | } 197 | break; 198 | // 关闭文件 199 | case "close": 200 | if (Objects.isNull(Memory.getInstance().getCurrentUser())) { 201 | System.out.println("[关闭文件失败]: 请先登录"); 202 | break; 203 | } 204 | CommonResult closeResult = fileService.closeFile(); 205 | if (!closeResult.isSuccess()) { 206 | System.out.println(closeResult.getMessage()); 207 | } 208 | break; 209 | // 重命名文件 210 | case "rename": 211 | if (Objects.isNull(Memory.getInstance().getCurrentUser())) { 212 | System.out.println("[重命名文件失败]: 请先登录"); 213 | break; 214 | } 215 | CommonResult renameResult = fileService.renameFile(command.length > 1 ? command[1] : "", 216 | command.length > 2 ? command[2] : ""); 217 | if (!renameResult.isSuccess()) { 218 | System.out.println(renameResult.getMessage()); 219 | } 220 | break; 221 | // 显示帮助 222 | case "help": 223 | displayService.printHelpList(); 224 | break; 225 | // 显示位示图 226 | case "show": 227 | displayService.printBitmap(Memory.getInstance().getBitmap()); 228 | break; 229 | // 退出系统 230 | case "exit": 231 | diskService.saveDisk(SAVE_PATH); 232 | return; 233 | default: 234 | System.out.println("[" + command[0].toLowerCase() + "不是合法命令]: 请输入\"help\"查看帮助"); 235 | break; 236 | } 237 | } 238 | } 239 | 240 | /** 241 | * 解析用户输入的命令 242 | * 格式通常是:[命令] [内容],例如:mkdir spice(创建一个名为spice的目录) 243 | * 244 | * @param input 用户的输入 245 | * @return 解析结果 246 | */ 247 | private static String[] inputResolve(String input) { 248 | if (StringUtil.isAllSpace(input)) { 249 | return new String[]{""}; 250 | } 251 | 252 | return input.trim().split(SEPARATOR); 253 | } 254 | 255 | /** 256 | * 显示当前用户和当前目录 257 | */ 258 | private static void showUserAndDirectory() { 259 | StringBuilder info = new StringBuilder() 260 | .append("\n") 261 | .append("[") 262 | .append(Objects.nonNull(Memory.getInstance().getCurrentUser()) ? 263 | Memory.getInstance().getCurrentUser().getUsername() + " " : "") 264 | .append(((Memory.getInstance().getCurrentDirectory().getParentIndex() == -1) || 265 | (Memory.getInstance().getCurrentDirectory().getParentIndex() == 0)) ? "/" : 266 | Memory.getInstance().getCurrentDirectory().getFileControlBlock().getFileName()) 267 | .append("] "); 268 | System.out.print(info); 269 | } 270 | 271 | /** 272 | * 初始化一个新的磁盘 273 | */ 274 | private static void initDisk() { 275 | Disk newDisk = new Disk(); 276 | 277 | { 278 | // 初始化盘块 279 | List> disk = new ArrayList<>(1024); 280 | newDisk.setDisk(disk); 281 | 282 | // 初始化位示图 283 | Integer[][] bitmap = new Integer[DiskConstant.BITMAP_ROW_LENGTH][DiskConstant.BITMAP_LINE_LENGTH]; 284 | for (int i = 0; i < DiskConstant.BITMAP_ROW_LENGTH; i++) { 285 | for (int j = 0; j < DiskConstant.BITMAP_LINE_LENGTH; j++) { 286 | bitmap[i][j] = DiskConstant.BITMAP_FREE; 287 | } 288 | } 289 | newDisk.setBitmap(bitmap); 290 | 291 | for (int i = 0; i < DiskConstant.BLOCK_NUM; i++) { 292 | List block = new ArrayList<>(1024); 293 | disk.add(block); 294 | } 295 | 296 | // 初始化系统用户数据盘块区 297 | for (int i = DiskConstant.USER_START_BLOCK; i < DiskConstant.USER_START_BLOCK + DiskConstant.USER_BLOCK_NUM; i++) { 298 | for (int j = 0; j < DiskConstant.BLOCK_SIZE; j++) { 299 | newDisk.getDisk().get(i).add(j, 'U'); 300 | bitmap[0][i] = DiskConstant.BITMAP_BUSY; 301 | } 302 | } 303 | 304 | // 初始化文件控制块数据盘块区 305 | for (int i = DiskConstant.FCB_START_BLOCK; i < DiskConstant.FCB_START_BLOCK + DiskConstant.FCB_BLOCK_NUM; i++) { 306 | for (int j = 0; j < DiskConstant.BLOCK_SIZE; j++) { 307 | newDisk.getDisk().get(i).add(j, 'F'); 308 | bitmap[0][i] = DiskConstant.BITMAP_BUSY; 309 | } 310 | } 311 | 312 | // 初始化树形目录结构数据盘块区 313 | for (int i = DiskConstant.DIR_START_BLOCK; i < DiskConstant.DIR_START_BLOCK + DiskConstant.DIR_BLOCK_NUM; i++) { 314 | for (int j = 0; j < DiskConstant.BLOCK_SIZE; j++) { 315 | newDisk.getDisk().get(i).add(j, 'D'); 316 | bitmap[0][i] = DiskConstant.BITMAP_BUSY; 317 | } 318 | } 319 | 320 | // 初始化位示图数据盘块区 321 | for (int i = DiskConstant.BITMAP_START_BLOCK; i < DiskConstant.BITMAP_START_BLOCK + DiskConstant.BITMAP_BLOCK_NUM; i++) { 322 | for (int j = 0; j < DiskConstant.BLOCK_SIZE; j++) { 323 | newDisk.getDisk().get(i).add(j, 'U'); 324 | bitmap[0][i] = DiskConstant.BITMAP_BUSY; 325 | } 326 | } 327 | } 328 | 329 | { 330 | // 初始化系统用户集 331 | Map userMap = new HashMap<>(4); 332 | // 新增一个管理员用户 333 | userMap.put("Administrator", new User().setUsername("Administrator").setPassword("123456")); 334 | newDisk.setUserMap(userMap); 335 | } 336 | 337 | { 338 | // 初始化文件控制块集 339 | List fileControlBlockList = new LinkedList<>(); 340 | newDisk.setFileControlBlockList(fileControlBlockList); 341 | // 初始化根目录文件控制块 342 | FileControlBlock root = new FileControlBlock() 343 | .setDirectory(true) 344 | .setFileName("root") 345 | .setSuffix(null) 346 | .setStartBlock(null) 347 | .setBlockNum(null) 348 | .setProtectTypeList(null) 349 | .setCreateTime(LocalDateTime.now()) 350 | .setUpdateTime(LocalDateTime.now()); 351 | fileControlBlockList.add(root); 352 | 353 | // 初始化Administrator目录文件控制块 354 | FileControlBlock administrator = new FileControlBlock() 355 | .setDirectory(true) 356 | .setFileName("Administrator") 357 | .setSuffix(null) 358 | .setStartBlock(null) 359 | .setBlockNum(null) 360 | .setProtectTypeList(null) 361 | .setCreateTime(LocalDateTime.now()) 362 | .setUpdateTime(LocalDateTime.now()); 363 | fileControlBlockList.add(administrator); 364 | 365 | newDisk.setDirectoryStruct(new ArrayList<>()); 366 | // 初始化根目录文件项 367 | Directory rootDirectory = new Directory() 368 | .setFileControlBlock(root) 369 | .setChildDirectory(new LinkedList<>()) 370 | .setParentIndex(-1); 371 | newDisk.getDirectoryStruct().add(rootDirectory); 372 | rootDirectory.setIndex(0); 373 | 374 | // 初始化Administrator目录文件项 375 | Directory administratorDirectory = new Directory() 376 | .setFileControlBlock(administrator) 377 | .setChildDirectory(new LinkedList<>()) 378 | .setParentIndex(0); 379 | newDisk.getDirectoryStruct().add(administratorDirectory); 380 | administratorDirectory.setIndex(newDisk.getDirectoryStruct().size() - 1); 381 | newDisk.getDirectoryStruct().get(0).getChildDirectory().add(administratorDirectory); 382 | } 383 | 384 | DataCache.getInstance().setDisk(newDisk); 385 | Memory.getInstance().setCurrentDirectory(DataCache.getInstance().getDisk().getDirectoryStruct().get(0)); 386 | Memory.getInstance().setBitmap(DataCache.getInstance().getDisk().getBitmap()); 387 | } 388 | } 389 | -------------------------------------------------------------------------------- /src/main/java/com/spice/constant/DirectoryConstant.java: -------------------------------------------------------------------------------- 1 | package com.spice.constant; 2 | 3 | /** 4 | * 一些预定义好的常量 5 | * 6 | * @author spice 7 | * @date 2021/06/18 0:09 8 | */ 9 | public class DirectoryConstant { 10 | 11 | /** 12 | * 文件路径分隔符 13 | */ 14 | public static final String PATH_SEPARATOR = "/"; 15 | 16 | /** 17 | * 表示上一级目录 18 | */ 19 | public static final String BACK_TO_PREVIOUS_ONE = ".."; 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/com/spice/constant/DiskConstant.java: -------------------------------------------------------------------------------- 1 | package com.spice.constant; 2 | 3 | /** 4 | * 一些预定义好的常量 5 | * 6 | * @author spice 7 | * @date 2021/06/17 22:37 8 | */ 9 | public class DiskConstant { 10 | 11 | /** 12 | * 一个盘块的大小,单位:B 13 | */ 14 | public static final Integer BLOCK_SIZE = 1024; 15 | 16 | /** 17 | * 磁盘中盘块的数量 18 | */ 19 | public static final Integer BLOCK_NUM = 1024; 20 | 21 | /** 22 | * 磁盘的大小:1024 * 1024B,即1MB 23 | */ 24 | public static final Integer DISK_SIZE = BLOCK_SIZE * BLOCK_NUM; 25 | 26 | /** 27 | * 磁盘中系统用户数据的起始盘块号 28 | */ 29 | public static final Integer USER_START_BLOCK = 0; 30 | 31 | /** 32 | * 磁盘中系统用户数据所占用的盘块数 33 | */ 34 | public static final Integer USER_BLOCK_NUM = 2; 35 | 36 | /** 37 | * 磁盘中文件控制块数据的起始盘块号 38 | */ 39 | public static final Integer FCB_START_BLOCK = 2; 40 | 41 | /** 42 | * 磁盘中文件控制块数据所占用的盘块数 43 | */ 44 | public static final Integer FCB_BLOCK_NUM = 2; 45 | 46 | /** 47 | * 磁盘中树形目录结构数据的起始盘块号 48 | */ 49 | public static final Integer DIR_START_BLOCK = 4; 50 | 51 | /** 52 | * 磁盘中树形目录结构数据所占用的盘块数 53 | */ 54 | public static final Integer DIR_BLOCK_NUM = 2; 55 | 56 | /** 57 | * 磁盘中位示图数据的起始盘块号 58 | */ 59 | public static final Integer BITMAP_START_BLOCK = 6; 60 | 61 | /** 62 | * 磁盘中位示图数据所占用的盘块数 63 | */ 64 | public static final Integer BITMAP_BLOCK_NUM = 1; 65 | 66 | /** 67 | * 磁盘中文件记录数据的起始盘块号 68 | */ 69 | public static final Integer RECORD_START_BLOCK = 7; 70 | 71 | /** 72 | * 位示图属性:表示位示图的总行数 73 | */ 74 | public static final Integer BITMAP_ROW_LENGTH = 32; 75 | 76 | /** 77 | * 位示图属性:表示位示图的总列数 78 | */ 79 | public static final Integer BITMAP_LINE_LENGTH = 32; 80 | 81 | /** 82 | * 位示图属性:表示该盘块空闲 83 | */ 84 | public static final Integer BITMAP_FREE = 0; 85 | 86 | /** 87 | * 位示图属性:表示该盘块被占用了 88 | */ 89 | public static final Integer BITMAP_BUSY = 1; 90 | } 91 | -------------------------------------------------------------------------------- /src/main/java/com/spice/constant/FileConstant.java: -------------------------------------------------------------------------------- 1 | package com.spice.constant; 2 | 3 | /** 4 | * 一些预定义好的常量 5 | * 6 | * @author spice 7 | * @date 2021/06/17 22:31 8 | */ 9 | public class FileConstant { 10 | 11 | /** 12 | * 文件名的大小,单位:B 13 | */ 14 | public static final Integer FILE_NAME_SIZE = 36; 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/com/spice/data/DataCache.java: -------------------------------------------------------------------------------- 1 | package com.spice.data; 2 | 3 | import com.spice.entity.Disk; 4 | import lombok.Data; 5 | 6 | /** 7 | * 保存程序运行所需的一些数据 8 | * 9 | * @author spice 10 | * @date 2021/06/17 23:58 11 | */ 12 | @Data 13 | public class DataCache { 14 | 15 | private static volatile DataCache INSTANCE; 16 | 17 | /** 18 | * 表示一个磁盘 19 | */ 20 | private Disk disk; 21 | 22 | public static DataCache getInstance() { 23 | if (INSTANCE == null) { 24 | synchronized (Memory.class) { 25 | if (INSTANCE == null) { 26 | INSTANCE = new DataCache(); 27 | } 28 | } 29 | } 30 | 31 | return INSTANCE; 32 | } 33 | 34 | private DataCache() { 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/com/spice/data/Memory.java: -------------------------------------------------------------------------------- 1 | package com.spice.data; 2 | 3 | import com.spice.entity.ActiveFile; 4 | import com.spice.entity.Directory; 5 | import com.spice.entity.User; 6 | import lombok.Data; 7 | 8 | /** 9 | * 内存 10 | * 11 | * @author spice 12 | * @date 2021/06/17 22:09 13 | */ 14 | @Data 15 | public class Memory { 16 | 17 | private static volatile Memory INSTANCE; 18 | 19 | /** 20 | * 当前登录的系统用户 21 | */ 22 | private User currentUser; 23 | 24 | /** 25 | * 当前目录 26 | */ 27 | private Directory currentDirectory; 28 | 29 | /** 30 | * 磁盘位示图 31 | */ 32 | private Integer[][] bitmap; 33 | 34 | /** 35 | * 被调入内存的文件(当前打开的文件) 36 | */ 37 | private ActiveFile activeFile; 38 | 39 | public static Memory getInstance() { 40 | if (INSTANCE == null) { 41 | synchronized (Memory.class) { 42 | if (INSTANCE == null) { 43 | INSTANCE = new Memory(); 44 | } 45 | } 46 | } 47 | 48 | return INSTANCE; 49 | } 50 | 51 | private Memory() { 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/main/java/com/spice/entity/ActiveFile.java: -------------------------------------------------------------------------------- 1 | package com.spice.entity; 2 | 3 | import lombok.Data; 4 | 5 | import java.util.List; 6 | 7 | /** 8 | * 当前打开的文件 9 | * 10 | * @author spice 11 | * @date 2021/06/18 2:45 12 | */ 13 | @Data 14 | public class ActiveFile { 15 | 16 | /** 17 | * 文件控制块 18 | */ 19 | private FileControlBlock fileControlBlock; 20 | 21 | /** 22 | * 文件记录 23 | */ 24 | private List fileRecord; 25 | 26 | /** 27 | * 读指针 28 | */ 29 | private Integer readPtr; 30 | 31 | /** 32 | * 写指针 33 | */ 34 | private Integer writePtr; 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/com/spice/entity/Directory.java: -------------------------------------------------------------------------------- 1 | package com.spice.entity; 2 | 3 | import lombok.Data; 4 | import lombok.experimental.Accessors; 5 | 6 | import java.io.Serializable; 7 | import java.util.List; 8 | 9 | /** 10 | * 树形目录结构 11 | * 12 | * @author spice 13 | * @date 2021/06/17 21:26 14 | */ 15 | @Data 16 | @Accessors(chain = true) 17 | public class Directory implements Serializable { 18 | 19 | /** 20 | * 文件控制块 21 | */ 22 | private FileControlBlock fileControlBlock; 23 | 24 | /** 25 | * 在树形目录结构中的位置 26 | */ 27 | private Integer index; 28 | 29 | /** 30 | * 文件夹属性:子目录项集合 31 | */ 32 | private List childDirectory; 33 | 34 | /** 35 | * 父目录项的位置 36 | */ 37 | private Integer parentIndex; 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/com/spice/entity/Disk.java: -------------------------------------------------------------------------------- 1 | package com.spice.entity; 2 | 3 | import lombok.Data; 4 | 5 | import java.io.Serializable; 6 | import java.util.List; 7 | import java.util.Map; 8 | 9 | /** 10 | * 虚拟磁盘空间 11 | * 12 | * @author spice 13 | * @date 2021/06/17 20:08 14 | */ 15 | @Data 16 | public class Disk implements Serializable { 17 | 18 | /** 19 | * List: 表示一个盘块 20 | * List>: 表示所有盘块的集合,即一个磁盘 21 | */ 22 | private List> disk; 23 | 24 | /** 25 | * 表示存储在该磁盘上的系统用户集 26 | */ 27 | private Map userMap; 28 | 29 | /** 30 | * 表示存储在该磁盘上的所有文件控制块 31 | */ 32 | private List fileControlBlockList; 33 | 34 | /** 35 | * 表示存储在该磁盘上的树形结构目录,第0个元素为根目录 36 | */ 37 | private List directoryStruct; 38 | 39 | /** 40 | * 表示存储在该磁盘上的磁盘位示图 41 | */ 42 | private Integer[][] bitmap; 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/com/spice/entity/FileControlBlock.java: -------------------------------------------------------------------------------- 1 | package com.spice.entity; 2 | 3 | import com.spice.enums.ProtectType; 4 | import lombok.Data; 5 | import lombok.experimental.Accessors; 6 | 7 | import java.io.Serializable; 8 | import java.time.LocalDateTime; 9 | import java.util.List; 10 | 11 | /** 12 | * 文件控制块 13 | * 14 | * @author spice 15 | * @date 2021/06/17 21:01 16 | */ 17 | @Data 18 | @Accessors(chain = true) 19 | public class FileControlBlock implements Serializable { 20 | 21 | /** 22 | * 是否是目录文件 23 | */ 24 | private boolean isDirectory; 25 | 26 | /** 27 | * 文件名(包括了拓展名) 28 | */ 29 | private String fileName; 30 | 31 | /** 32 | * 拓展名 33 | */ 34 | private String suffix; 35 | 36 | /** 37 | * 起始盘块号 38 | */ 39 | private Integer startBlock; 40 | 41 | /** 42 | * 所占用的盘块数 43 | * 文件大小 = 一个盘块的大小 * 所占用的盘块数 44 | */ 45 | private Integer blockNum; 46 | 47 | /** 48 | * 文件属性:保护码列表 49 | */ 50 | private List protectTypeList; 51 | 52 | /** 53 | * 创建时间 54 | */ 55 | private LocalDateTime createTime; 56 | 57 | /** 58 | * 最后一次修改时间 59 | */ 60 | private LocalDateTime updateTime; 61 | } 62 | -------------------------------------------------------------------------------- /src/main/java/com/spice/entity/User.java: -------------------------------------------------------------------------------- 1 | package com.spice.entity; 2 | 3 | import lombok.Data; 4 | import lombok.experimental.Accessors; 5 | 6 | import java.io.Serializable; 7 | 8 | /** 9 | * 系统用户 10 | * 11 | * @author spice 12 | * @date 2021/06/19 1:16 13 | */ 14 | @Data 15 | @Accessors(chain = true) 16 | public class User implements Serializable { 17 | 18 | /** 19 | * 用户名 20 | */ 21 | private String username; 22 | 23 | /** 24 | * 密码 25 | */ 26 | private String password; 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/com/spice/enums/ProtectType.java: -------------------------------------------------------------------------------- 1 | package com.spice.enums; 2 | 3 | import java.util.Arrays; 4 | import java.util.List; 5 | 6 | /** 7 | * 文件保护码 8 | * 9 | * @author spice 10 | * @date 2021/06/06 0:39 11 | */ 12 | public enum ProtectType { 13 | 14 | // 允许读 15 | READ(100, "可读"), 16 | // 允许写 17 | WRITE(200, "可写"), 18 | // 允许执行 19 | EXECUTE(300, "可执行"); 20 | 21 | /** 22 | * 保护码 23 | */ 24 | private Integer value; 25 | 26 | /** 27 | * 描述 28 | */ 29 | private String description; 30 | 31 | ProtectType(Integer value, String description) { 32 | this.value = value; 33 | this.description = description; 34 | } 35 | 36 | public Integer getValue() { 37 | return value; 38 | } 39 | 40 | public void setValue(Integer value) { 41 | this.value = value; 42 | } 43 | 44 | public String getDescription() { 45 | return description; 46 | } 47 | 48 | public void setDescription(String description) { 49 | this.description = description; 50 | } 51 | 52 | public static List getAllPermission() { 53 | return Arrays.asList(values()); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/main/java/com/spice/result/CommonResult.java: -------------------------------------------------------------------------------- 1 | package com.spice.result; 2 | 3 | import lombok.Data; 4 | import lombok.experimental.Accessors; 5 | 6 | import static com.spice.result.ResultConstant.*; 7 | 8 | /** 9 | * @author spice 10 | * @date 2021/06/03 3:00 11 | */ 12 | @Data 13 | @Accessors(chain = true) 14 | public class CommonResult { 15 | 16 | /** 17 | * 是否成功 18 | */ 19 | private boolean isSuccess; 20 | 21 | /** 22 | * 状态码 23 | */ 24 | private int code; 25 | 26 | /** 27 | * 返回信息 28 | */ 29 | private String message; 30 | 31 | /** 32 | * 返回数据 33 | */ 34 | private T data; 35 | 36 | public CommonResult() { 37 | } 38 | 39 | public CommonResult(boolean isSuccess, int code, String message) { 40 | this.isSuccess = isSuccess; 41 | this.code = code; 42 | this.message = message; 43 | } 44 | 45 | public CommonResult(boolean isSuccess, int code, String message, T data) { 46 | this.isSuccess = isSuccess; 47 | this.code = code; 48 | this.message = message; 49 | this.data = data; 50 | } 51 | 52 | public static CommonResult operateSuccess() { 53 | return new CommonResult(true, SUCCESS_CODE, OPERATE_SUCCESS_MESSAGE); 54 | } 55 | 56 | public static CommonResult operateSuccessWithMessage(String message) { 57 | return new CommonResult<>(true, SUCCESS_CODE, message); 58 | } 59 | 60 | public static CommonResult operateSuccess(E data) { 61 | return new CommonResult<>(true, SUCCESS_CODE, OPERATE_SUCCESS_MESSAGE, data); 62 | } 63 | 64 | public static CommonResult operateFail() { 65 | return new CommonResult<>(false, FAIL_CODE, OPERATE_FAIL_MESSAGE); 66 | } 67 | 68 | public static CommonResult operateFailWithMessage(String message) { 69 | return new CommonResult(false, FAIL_CODE, message); 70 | } 71 | 72 | public static CommonResult operateFail(E data) { 73 | return new CommonResult<>(false, FAIL_CODE, OPERATE_FAIL_MESSAGE, data); 74 | } 75 | 76 | public static CommonResult autoResult(boolean isSuccess) { 77 | if (isSuccess) { 78 | return operateSuccess(); 79 | } else { 80 | return operateFail(); 81 | } 82 | } 83 | 84 | public static CommonResult autoResult(boolean isSuccess, E data) { 85 | if (isSuccess) { 86 | return CommonResult.operateSuccess(data); 87 | } else { 88 | return CommonResult.operateFail(data); 89 | } 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /src/main/java/com/spice/result/ResultConstant.java: -------------------------------------------------------------------------------- 1 | package com.spice.result; 2 | 3 | /** 4 | * @author spice 5 | * @date 2021/06/03 3:00 6 | */ 7 | public class ResultConstant { 8 | 9 | public static final String OPERATE_SUCCESS_MESSAGE = "success"; 10 | public static final String OPERATE_FAIL_MESSAGE = "fail"; 11 | 12 | public static final int SUCCESS_CODE = 200; 13 | public static final int FAIL_CODE = 400; 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/com/spice/service/DirectoryService.java: -------------------------------------------------------------------------------- 1 | package com.spice.service; 2 | 3 | import com.spice.entity.Directory; 4 | import com.spice.entity.FileControlBlock; 5 | import com.spice.result.CommonResult; 6 | 7 | import java.util.List; 8 | 9 | /** 10 | * 和目录文件相关的操作 11 | * 12 | * @author spice 13 | * @date 2021/06/17 22:18 14 | */ 15 | public interface DirectoryService { 16 | 17 | /** 18 | * 创建目录 19 | * 对应命令[mkdir] 20 | * 21 | * @param directoryName 目录名 22 | * @return 操作结果 23 | */ 24 | CommonResult makeDirectory(String directoryName); 25 | 26 | /** 27 | * 改变当前目录 28 | * 对应命令[cd] 29 | * 30 | * @param path 路径 31 | * @return 操作结果 32 | */ 33 | CommonResult changeDirectory(String path); 34 | 35 | /** 36 | * 列出文件目录 37 | * 对应命令[dir] 38 | * 39 | * @param directory 当前目录 40 | * @return 操作结果 41 | */ 42 | CommonResult> showDirectory(Directory directory); 43 | 44 | /** 45 | * 解析路径并得到目录项 46 | * 47 | * @param path 路径 48 | * @return 解析结果 49 | */ 50 | CommonResult pathResolve(String path); 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/com/spice/service/DiskService.java: -------------------------------------------------------------------------------- 1 | package com.spice.service; 2 | 3 | import com.spice.entity.FileControlBlock; 4 | import com.spice.result.CommonResult; 5 | 6 | import java.util.List; 7 | 8 | /** 9 | * 和磁盘相关的操作 10 | * 11 | * @author spice 12 | * @date 2021/06/17 22:40 13 | */ 14 | public interface DiskService { 15 | 16 | /** 17 | * 在磁盘上存储文件记录 18 | * 19 | * @param fileControlBlock 文件控制块 20 | * @param record 记录 21 | * @return 操作结果 22 | */ 23 | CommonResult storeRecord(FileControlBlock fileControlBlock, List record); 24 | 25 | /** 26 | * 释放磁盘空间(改变位示图) 27 | * 28 | * @param startBlockId 起始盘块 29 | * @param blockNum 盘块数 30 | * @return 操作结果 31 | */ 32 | CommonResult freeSpace(Integer startBlockId, Integer blockNum); 33 | 34 | /** 35 | * 获取指定盘块上的文件记录 36 | * 37 | * @param startBlockId 起始盘块 38 | * @param blockNum 盘块数 39 | * @return 操作结果 40 | */ 41 | CommonResult> getRecord(Integer startBlockId, Integer blockNum); 42 | 43 | /** 44 | * 保存虚拟磁盘数据到本地文件中 45 | * 46 | * @param savePath 保存路径 47 | * @return 操作结果 48 | */ 49 | CommonResult saveDisk(String savePath); 50 | 51 | /** 52 | * 从一个文件中加载虚拟磁盘 53 | * 54 | * @param diskFilePath 文件 55 | * @return 加载结果 56 | */ 57 | CommonResult loadDisk(String diskFilePath); 58 | } 59 | -------------------------------------------------------------------------------- /src/main/java/com/spice/service/DisplayService.java: -------------------------------------------------------------------------------- 1 | package com.spice.service; 2 | 3 | import com.spice.entity.ActiveFile; 4 | import com.spice.entity.FileControlBlock; 5 | import com.spice.result.CommonResult; 6 | 7 | import java.util.List; 8 | 9 | /** 10 | * 一些显示操作 11 | * 12 | * @author spice 13 | * @date 2021/06/20 2:08 14 | */ 15 | public interface DisplayService { 16 | 17 | /** 18 | * 在控制台打印文件信息 19 | * 20 | * @param fileControlBlockList 文件控制块集合 21 | * @return 操作结果 22 | */ 23 | CommonResult printFileList(List fileControlBlockList); 24 | 25 | /** 26 | * 在控制台打印一个被打开的文件的信息 27 | * 28 | * @param activeFile 打开的文件 29 | * @return 操作结果 30 | */ 31 | CommonResult printOpenedFile(ActiveFile activeFile); 32 | 33 | /** 34 | * 打印帮助列表 35 | * 对应命令[help] 36 | * 37 | * @return 操作结果 38 | */ 39 | CommonResult printHelpList(); 40 | 41 | /** 42 | * 在控制台打印磁盘位示图 43 | * 44 | * @param bitmap 位示图 45 | * @return 操作结果 46 | */ 47 | CommonResult printBitmap(Integer[][] bitmap); 48 | } 49 | -------------------------------------------------------------------------------- /src/main/java/com/spice/service/FileService.java: -------------------------------------------------------------------------------- 1 | package com.spice.service; 2 | 3 | import com.spice.entity.ActiveFile; 4 | import com.spice.result.CommonResult; 5 | 6 | /** 7 | * 和数据文件相关的操作 8 | * 9 | * @author spice 10 | * @date 2021/06/18 1:07 11 | */ 12 | public interface FileService { 13 | 14 | /** 15 | * 创建文件 16 | * 对应命令[create] 17 | * 18 | * @param fileName 文件名 19 | * @return 操作结果 20 | */ 21 | CommonResult createFile(String fileName); 22 | 23 | /** 24 | * 打开文件 25 | * 对应命令[open] 26 | * 27 | * @param path 路径 28 | * @return 操作结果 29 | */ 30 | CommonResult openFile(String path); 31 | 32 | /** 33 | * 读取文件 34 | * 对应命令[read] 35 | * 36 | * @param recordNum 要读取的记录数 37 | * @return 读取结果 38 | */ 39 | CommonResult readFile(Integer recordNum); 40 | 41 | /** 42 | * 写入文件 43 | * 对应命令[write] 44 | * 45 | * @param record 要写入的记录 46 | * @return 写入结果 47 | */ 48 | CommonResult writeToFile(String record); 49 | 50 | /** 51 | * 关闭文件 52 | * 对应命令[close] 53 | * 54 | * @return 操作结果 55 | */ 56 | CommonResult closeFile(); 57 | 58 | /** 59 | * 删除文件(数据文件) 60 | * 对应命令[delete] 61 | * 62 | * @param path 文件路径 63 | * @return 操作结果 64 | */ 65 | CommonResult deleteFile(String path); 66 | 67 | /** 68 | * 重命名文件(目录文件或者数据文件都行) 69 | * 对应命令[rename] 70 | * 71 | * @param path 文件路径 72 | * @param newName 新的文件名 73 | * @return 操作结果 74 | */ 75 | CommonResult renameFile(String path, String newName); 76 | } 77 | -------------------------------------------------------------------------------- /src/main/java/com/spice/service/UserService.java: -------------------------------------------------------------------------------- 1 | package com.spice.service; 2 | 3 | import com.spice.result.CommonResult; 4 | 5 | /** 6 | * 和用户相关的操作 7 | * 8 | * @author spice 9 | * @date 2021/06/19 1:09 10 | */ 11 | public interface UserService { 12 | 13 | /** 14 | * 注册一个系统用户 15 | * 对应命令[register] 16 | * 17 | * @param username 用户名 18 | * @param password 密码 19 | * @return 操作结果 20 | */ 21 | CommonResult register(String username, String password); 22 | 23 | /** 24 | * 用户登录 25 | * 对应命令[login] 26 | * 27 | * @param username 用户名 28 | * @param password 密码 29 | * @return 操作结果 30 | */ 31 | CommonResult login(String username, String password); 32 | 33 | /** 34 | * 用户注销 35 | * 对应命令[logout] 36 | * 37 | * @return 操作结果 38 | */ 39 | CommonResult logout(); 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/com/spice/service/impl/DirectoryServiceImpl.java: -------------------------------------------------------------------------------- 1 | package com.spice.service.impl; 2 | 3 | import com.spice.constant.DirectoryConstant; 4 | import com.spice.data.DataCache; 5 | import com.spice.data.Memory; 6 | import com.spice.entity.Directory; 7 | import com.spice.entity.FileControlBlock; 8 | import com.spice.result.CommonResult; 9 | import com.spice.service.DirectoryService; 10 | import com.spice.util.StringUtil; 11 | 12 | import java.time.LocalDateTime; 13 | import java.util.LinkedList; 14 | import java.util.List; 15 | import java.util.Objects; 16 | 17 | /** 18 | * @author spice 19 | * @date 2021/06/17 22:19 20 | */ 21 | public class DirectoryServiceImpl implements DirectoryService { 22 | 23 | @Override 24 | public CommonResult makeDirectory(String directoryName) { 25 | if (StringUtil.isAllSpace(directoryName)) { 26 | return CommonResult.operateFailWithMessage("[创建目录失败]: 文件名不能为空"); 27 | } 28 | 29 | // 去除前后空格 30 | directoryName = directoryName.trim(); 31 | for (Directory directory : Memory.getInstance().getCurrentDirectory().getChildDirectory()) { 32 | if (directory.getFileControlBlock().getFileName().equalsIgnoreCase(directoryName)) { 33 | return CommonResult.operateFailWithMessage("[创建目录失败]: 文件名重复"); 34 | } 35 | } 36 | 37 | // 新建目录文件的文件控制块。注意:目录文件不为其分配盘块 38 | FileControlBlock fileControlBlock = new FileControlBlock() 39 | .setDirectory(true) 40 | .setFileName(directoryName) 41 | .setSuffix(null) 42 | .setStartBlock(null) 43 | .setBlockNum(null) 44 | .setCreateTime(LocalDateTime.now()) 45 | .setUpdateTime(LocalDateTime.now()) 46 | .setProtectTypeList(null); 47 | // 将文件控制块存储到磁盘中 48 | DataCache.getInstance().getDisk().getFileControlBlockList().add(fileControlBlock); 49 | 50 | // 新建一个目录项 51 | Directory directory = new Directory() 52 | .setFileControlBlock(fileControlBlock) 53 | .setChildDirectory(new LinkedList<>()) 54 | .setParentIndex(DataCache.getInstance().getDisk().getDirectoryStruct().indexOf(Memory.getInstance().getCurrentDirectory())); 55 | // 将目录项保存到树形结构目录中 56 | DataCache.getInstance().getDisk().getDirectoryStruct().add(directory); 57 | directory.setIndex(DataCache.getInstance().getDisk().getDirectoryStruct().size() - 1); 58 | Memory.getInstance().getCurrentDirectory().getChildDirectory().add(directory); 59 | return CommonResult.operateSuccess(); 60 | } 61 | 62 | @Override 63 | public CommonResult changeDirectory(String path) { 64 | if (StringUtil.isAllSpace(path)) { 65 | // 如果路径为空,直接返回当前目录 66 | return CommonResult.operateSuccess(); 67 | } 68 | 69 | CommonResult resolveResult = pathResolve(path); 70 | if (resolveResult.isSuccess()) { 71 | if (resolveResult.getData().getFileControlBlock().isDirectory()) { 72 | // 切换到目标目录 73 | Memory.getInstance().setCurrentDirectory(resolveResult.getData()); 74 | return CommonResult.operateSuccess(); 75 | } else { 76 | // 如果最终找到的目录项不是目录文件类型,则报错 77 | return CommonResult.operateFailWithMessage("[切换目录失败]: 找不到对应目录"); 78 | } 79 | } else { 80 | return CommonResult.operateFailWithMessage("[切换目录失败]: 找不到对应目录"); 81 | } 82 | } 83 | 84 | @Override 85 | public CommonResult> showDirectory(Directory directory) { 86 | if (Objects.isNull(directory)) { 87 | return CommonResult.operateFailWithMessage("[显示文件失败]: 系统出错!!"); 88 | } 89 | 90 | if (!directory.getFileControlBlock().isDirectory()) { 91 | return CommonResult.operateFailWithMessage("[显示文件失败]: " + directory.getFileControlBlock().getFileName() + "不是目录"); 92 | } 93 | 94 | List fileControlBlockList = new LinkedList<>(); 95 | for (Directory d : directory.getChildDirectory()) { 96 | fileControlBlockList.add(d.getFileControlBlock()); 97 | } 98 | return CommonResult.operateSuccess(fileControlBlockList); 99 | } 100 | 101 | @Override 102 | public CommonResult pathResolve(String path) { 103 | if (StringUtil.isAllSpace(path)) { 104 | return CommonResult.operateFailWithMessage("[路径解析失败]: 路径不能为空"); 105 | } 106 | 107 | Directory currentDirectory = Memory.getInstance().getCurrentDirectory(); 108 | String[] pathArray = path.trim().split(DirectoryConstant.PATH_SEPARATOR); 109 | for (int i = 0; i < pathArray.length; i++) { 110 | if (isBackToPrevious(pathArray[i])) { 111 | if (currentDirectory.getParentIndex() != 0) { 112 | // 回退到上一级目录,知道当前目录为用户的根目录 113 | currentDirectory = DataCache.getInstance().getDisk().getDirectoryStruct().get(currentDirectory.getParentIndex()); 114 | } 115 | continue; 116 | } 117 | 118 | if (StringUtil.isAllSpace(pathArray[i])) { 119 | if ((i != 0) && (i != pathArray.length - 1)) { 120 | // 如果路径中间有空格则报错找不到目录(因为文件名不能为空) 121 | return CommonResult.operateFailWithMessage("[路径解析失败]: 找不到对应目录"); 122 | } 123 | continue; 124 | } 125 | 126 | // 寻找子目录项 127 | Directory childDirectory = searchChildDirectory(currentDirectory, pathArray[i]); 128 | if (Objects.isNull(childDirectory)) { 129 | return CommonResult.operateFailWithMessage("[路径解析失败]: 找不到对应文件"); 130 | } else { 131 | if ((i + 1) < pathArray.length && !childDirectory.getFileControlBlock().isDirectory()) { 132 | // 说明是路径的中间部分,路径的中间部分文件名应该都是对应文件夹类型才是正确的,否则报错 133 | return CommonResult.operateFailWithMessage("[路径解析失败]: 找不到对应文件"); 134 | } 135 | currentDirectory = childDirectory; 136 | } 137 | } 138 | 139 | return CommonResult.operateSuccess(currentDirectory); 140 | } 141 | 142 | /** 143 | * 判断是否返回上一级目录 144 | * 145 | * @param path 路径 146 | * @return true-返回 147 | */ 148 | private boolean isBackToPrevious(String path) { 149 | if (path != null && path.length() >= 2) { 150 | // 规定: 形如".."、"../"、".. "、" .."都是表示上一级目录的路径 151 | return (path.length() == 2 && DirectoryConstant.BACK_TO_PREVIOUS_ONE.equals(path)) || 152 | (path.length() > 2 && DirectoryConstant.BACK_TO_PREVIOUS_ONE.equals(path.trim())); 153 | } 154 | 155 | return false; 156 | } 157 | 158 | /** 159 | * 在一个目录下面寻找子目录项 160 | * 161 | * @param directory 目录文件 162 | * @param directoryName 子目录项文件名 163 | * @return 子目录项(如果找不到则为null) 164 | */ 165 | private Directory searchChildDirectory(Directory directory, String directoryName) { 166 | if (Objects.isNull(directory)) { 167 | return null; 168 | } 169 | 170 | for (Directory childDirectory : directory.getChildDirectory()) { 171 | if (childDirectory.getFileControlBlock().getFileName().equals(directoryName)) { 172 | return childDirectory; 173 | } 174 | } 175 | 176 | return null; 177 | } 178 | } 179 | -------------------------------------------------------------------------------- /src/main/java/com/spice/service/impl/DiskServiceImpl.java: -------------------------------------------------------------------------------- 1 | package com.spice.service.impl; 2 | 3 | import com.spice.constant.DiskConstant; 4 | import com.spice.data.DataCache; 5 | import com.spice.data.Memory; 6 | import com.spice.entity.Disk; 7 | import com.spice.entity.FileControlBlock; 8 | import com.spice.result.CommonResult; 9 | import com.spice.service.DiskService; 10 | import com.spice.util.CollectionUtil; 11 | import com.spice.util.Math; 12 | 13 | import java.io.*; 14 | import java.util.LinkedList; 15 | import java.util.List; 16 | import java.util.Objects; 17 | 18 | /** 19 | * @author spice 20 | * @date 2021/06/17 22:41 21 | */ 22 | public class DiskServiceImpl implements DiskService { 23 | 24 | @Override 25 | public CommonResult storeRecord(FileControlBlock fileControlBlock, List record) { 26 | if (CollectionUtil.isEmpty(record)) { 27 | return CommonResult.operateSuccess(null); 28 | } 29 | 30 | // 修改位示图,为重新分配盘块做准备 31 | changeBitmapStatus(fileControlBlock.getStartBlock(), fileControlBlock.getBlockNum(), true); 32 | // 计算存储该长度记录所需要的盘块数量 33 | Integer requiredNum = Math.ceilDivide(record.size(), DiskConstant.BLOCK_SIZE); 34 | 35 | int count = 0; 36 | int startBlockId = DiskConstant.RECORD_START_BLOCK; 37 | 38 | for (int i = 0; i < DiskConstant.BITMAP_ROW_LENGTH; i++) { 39 | for (int j = 0; j < DiskConstant.BITMAP_LINE_LENGTH; j++) { 40 | // 如果该盘块空闲 41 | if (DiskConstant.BITMAP_FREE.compareTo(Memory.getInstance().getBitmap()[i][j]) == 0) { 42 | if (count == 0) { 43 | // 记下起始盘块号: 当前行 * 总列数 + 当前列 44 | startBlockId = i * DiskConstant.BITMAP_LINE_LENGTH + j; 45 | } 46 | 47 | count++; 48 | if (count == requiredNum) { 49 | // 如果有足够的连续盘区供存储,则进行存储,并改变位示图的相应状态 50 | storeToDisk(startBlockId, record); 51 | changeBitmapStatus(startBlockId, requiredNum, false); 52 | return CommonResult.operateSuccess(fileControlBlock.setStartBlock(startBlockId).setBlockNum(requiredNum)); 53 | } 54 | } else { 55 | // 因为是连续分配,如果该盘块不空闲则要重新计数 56 | count = 0; 57 | } 58 | } 59 | } 60 | 61 | return CommonResult.operateFailWithMessage("[分配盘块失败]: 磁盘空间不足"); 62 | } 63 | 64 | @Override 65 | public CommonResult freeSpace(Integer startBlockId, Integer blockNum) { 66 | changeBitmapStatus(startBlockId, blockNum, true); 67 | return CommonResult.operateSuccess(); 68 | } 69 | 70 | @Override 71 | public CommonResult> getRecord(Integer startBlockId, Integer blockNum) { 72 | List record = new LinkedList<>(); 73 | if (Objects.isNull(startBlockId)) { 74 | return CommonResult.operateSuccess(record); 75 | } 76 | 77 | if (startBlockId < DiskConstant.RECORD_START_BLOCK || startBlockId >= DiskConstant.BLOCK_NUM || 78 | blockNum <= 0 || blockNum > DiskConstant.BLOCK_NUM) { 79 | return CommonResult.operateFailWithMessage("[获取记录失败]: 系统错误!!"); 80 | } 81 | 82 | Disk disk = DataCache.getInstance().getDisk(); 83 | // 盘块指针,用于从盘块中读取记录,加载进内存 84 | int blockPtr = 0; 85 | for (int i = 0; i < blockNum; ) { 86 | if (i == blockNum - 1) { 87 | if (blockPtr >= disk.getDisk().get(startBlockId + i).size() || 88 | disk.getDisk().get(startBlockId + i).get(blockPtr) == null) { 89 | // 说明所有记录都读取完成了 90 | break; 91 | } 92 | } 93 | 94 | record.add(disk.getDisk().get(startBlockId + i).get(blockPtr)); 95 | if (blockPtr >= DiskConstant.BLOCK_SIZE) { 96 | blockPtr = 0; 97 | i++; 98 | } else { 99 | blockPtr++; 100 | } 101 | } 102 | 103 | return CommonResult.operateSuccess(record); 104 | } 105 | 106 | @Override 107 | public CommonResult saveDisk(String savePath) { 108 | File file = new File(savePath); 109 | // 检查文件是否存在 110 | if (!file.exists()) { 111 | if (!file.getParentFile().exists()) { 112 | if (new File(file.getParentFile().getPath()).mkdirs()) { 113 | try { 114 | if (!file.createNewFile()) { 115 | return CommonResult.operateFailWithMessage("[保存磁盘失败]: 创建保存文件失败"); 116 | } 117 | } catch (IOException ioException) { 118 | return CommonResult.operateFailWithMessage("[保存磁盘失败]: IO异常"); 119 | } 120 | } else { 121 | return CommonResult.operateFailWithMessage("[保存磁盘失败]: 创建保存目录失败"); 122 | } 123 | } 124 | } 125 | 126 | ObjectOutputStream outputStream = null; 127 | try { 128 | outputStream = new ObjectOutputStream(new FileOutputStream(file)); 129 | // 持久化到保存文件中 130 | outputStream.writeObject(DataCache.getInstance().getDisk()); 131 | outputStream.flush(); 132 | return CommonResult.operateSuccessWithMessage("[保存磁盘成功]"); 133 | } catch (FileNotFoundException e) { 134 | return CommonResult.operateFailWithMessage("[保存磁盘失败]: 找不到文件"); 135 | } catch (IOException ioException) { 136 | return CommonResult.operateFailWithMessage("[保存磁盘失败]: 保存到文件出错"); 137 | } finally { 138 | try { 139 | if (Objects.nonNull(outputStream)) { 140 | outputStream.close(); 141 | } 142 | } catch (IOException ignored) { 143 | } 144 | } 145 | } 146 | 147 | @Override 148 | public CommonResult loadDisk(String diskFilePath) { 149 | File file = new File(diskFilePath); 150 | if (!file.exists()) { 151 | return CommonResult.operateFailWithMessage("[加载磁盘失败]: 找不到文件"); 152 | } 153 | 154 | ObjectInputStream inputStream = null; 155 | try { 156 | inputStream = new ObjectInputStream(new FileInputStream(file)); 157 | // 加载磁盘数据 158 | DataCache.getInstance().setDisk((Disk) inputStream.readObject()); 159 | Memory.getInstance().setCurrentDirectory(DataCache.getInstance().getDisk().getDirectoryStruct().get(0)); 160 | Memory.getInstance().setBitmap(DataCache.getInstance().getDisk().getBitmap()); 161 | return CommonResult.operateSuccessWithMessage("[加载磁盘成功]"); 162 | } catch (FileNotFoundException e) { 163 | return CommonResult.operateFailWithMessage("[加载磁盘失败]: 找不到文件"); 164 | } catch (IOException ioException) { 165 | return CommonResult.operateFailWithMessage("[加载磁盘失败]: IO异常"); 166 | } catch (ClassNotFoundException e) { 167 | return CommonResult.operateFailWithMessage("[加载磁盘失败]: 非磁盘保存文件"); 168 | } finally { 169 | try { 170 | if (Objects.nonNull(inputStream)) { 171 | inputStream.close(); 172 | } 173 | } catch (IOException ignored) { 174 | } 175 | } 176 | } 177 | 178 | /** 179 | * 将记录存储到磁盘中 180 | * 181 | * @param startBlockId 起始盘块号 182 | * @param record 待存储的记录 183 | */ 184 | private void storeToDisk(Integer startBlockId, List record) { 185 | int index = 0; 186 | int blockId = startBlockId; 187 | Disk disk = DataCache.getInstance().getDisk(); 188 | 189 | // 可以直接覆盖掉原来的磁盘中的记录 190 | for (Character ch : record) { 191 | if (index >= DiskConstant.BLOCK_SIZE) { 192 | // 如果一个盘块的空间被用完了,则使用下一个盘块来进行存储 193 | blockId++; 194 | index = 0; 195 | } 196 | 197 | disk.getDisk().get(blockId).add(index, ch); 198 | index++; 199 | } 200 | 201 | while (index < DiskConstant.BLOCK_SIZE && disk.getDisk().get(blockId).size() > index) { 202 | // 擦除最后一个盘块中没有用到的空间 203 | disk.getDisk().get(blockId).set(index, null); 204 | index++; 205 | } 206 | } 207 | 208 | /** 209 | * 更改相应的位示图的状态 210 | * 仅用于连续分配 211 | * 212 | * @param startBlockId 起始盘块号 213 | * @param blockNum 要更改的盘块数量 214 | * @param changeToFree 是否改为空闲状态:true-空闲状态,false-被占用状态 215 | */ 216 | private void changeBitmapStatus(Integer startBlockId, Integer blockNum, boolean changeToFree) { 217 | if (Objects.isNull(startBlockId) || startBlockId < DiskConstant.RECORD_START_BLOCK || 218 | startBlockId >= DiskConstant.BLOCK_NUM || blockNum <= 0) { 219 | return; 220 | } 221 | 222 | // 解析该盘块在位示图中的第几行 223 | int row = startBlockId / DiskConstant.BITMAP_LINE_LENGTH; 224 | // 解析该盘块在位示图中的第几列 225 | int line = startBlockId % DiskConstant.BITMAP_LINE_LENGTH; 226 | for (int i = 0; i < blockNum; i++) { 227 | Memory.getInstance().getBitmap()[row][line] = changeToFree ? DiskConstant.BITMAP_FREE : DiskConstant.BITMAP_BUSY; 228 | if (line >= DiskConstant.BITMAP_LINE_LENGTH - 1) { 229 | line = 0; 230 | row++; 231 | } else { 232 | line++; 233 | } 234 | } 235 | } 236 | } 237 | -------------------------------------------------------------------------------- /src/main/java/com/spice/service/impl/DisplayServiceImpl.java: -------------------------------------------------------------------------------- 1 | package com.spice.service.impl; 2 | 3 | import com.spice.constant.DiskConstant; 4 | import com.spice.entity.ActiveFile; 5 | import com.spice.entity.FileControlBlock; 6 | import com.spice.enums.ProtectType; 7 | import com.spice.result.CommonResult; 8 | import com.spice.service.DisplayService; 9 | 10 | import java.time.format.DateTimeFormatter; 11 | import java.util.List; 12 | import java.util.Objects; 13 | 14 | /** 15 | * @author spice 16 | * @date 2021/06/20 2:10 17 | */ 18 | public class DisplayServiceImpl implements DisplayService { 19 | 20 | private final DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm"); 21 | 22 | @Override 23 | public CommonResult printFileList(List fileControlBlockList) { 24 | if (fileControlBlockList == null || fileControlBlockList.size() == 0) { 25 | return CommonResult.operateSuccess(); 26 | } 27 | 28 | System.out.println("----------------------------------------------------------------------------------------"); 29 | System.out.printf("%-20s", "文件名"); 30 | System.out.printf("%-20s", "文件类型"); 31 | System.out.printf("%-20s", "文件长度"); 32 | System.out.printf("%-20s", "创建时间"); 33 | System.out.printf("%-20s", "修改时间"); 34 | System.out.printf("%-20s", "文件属性"); 35 | System.out.println(); 36 | 37 | for (FileControlBlock fcb : fileControlBlockList) { 38 | System.out.printf("%-20s", fcb.getFileName()); 39 | if (fcb.isDirectory()) { 40 | System.out.printf("%-20s", "文件夹"); 41 | System.out.printf("%-20s", "null"); 42 | System.out.printf("%-20s", fcb.getCreateTime().format(dtf)); 43 | System.out.printf("%-20s", fcb.getUpdateTime().format(dtf)); 44 | System.out.printf("%-20s", "null"); 45 | } else { 46 | System.out.printf("%-20s", fcb.getSuffix() + " 文件"); 47 | System.out.printf("%-20s", fcb.getBlockNum() + " KB"); 48 | System.out.printf("%-20s", fcb.getCreateTime().format(dtf)); 49 | System.out.printf("%-20s", fcb.getUpdateTime().format(dtf)); 50 | StringBuilder protectTypeString = new StringBuilder(); 51 | for (ProtectType protectType : fcb.getProtectTypeList()) { 52 | protectTypeString.append(protectType.getDescription()).append(" | "); 53 | } 54 | System.out.printf("|%-20s", protectTypeString); 55 | } 56 | System.out.println(); 57 | } 58 | 59 | System.out.println("\n----------------------------------------------------------------------------------------"); 60 | return CommonResult.operateSuccess(); 61 | } 62 | 63 | @Override 64 | public CommonResult printOpenedFile(ActiveFile activeFile) { 65 | if (Objects.isNull(activeFile)) { 66 | return CommonResult.operateFailWithMessage("[显示文件失败]: 文件为空"); 67 | } 68 | 69 | System.out.println("[" + activeFile.getFileControlBlock().getFileName() + "]: "); 70 | StringBuilder record = new StringBuilder(); 71 | for (Character ch : activeFile.getFileRecord()) { 72 | record.append(ch); 73 | } 74 | System.out.println(record); 75 | System.out.println("[END]"); 76 | return CommonResult.operateSuccess(); 77 | } 78 | 79 | @Override 80 | public CommonResult printHelpList() { 81 | System.out.println("----------------------------------------------------"); 82 | System.out.printf("%-20s", "命令"); 83 | System.out.printf("%-20s", "作用"); 84 | System.out.printf("%-20s", "用法"); 85 | System.out.println(); 86 | 87 | System.out.printf("%-20s", "help"); 88 | System.out.printf("%-20s", "显示帮助"); 89 | System.out.printf("%-20s", "help"); 90 | System.out.println(); 91 | 92 | System.out.printf("%-20s", "show"); 93 | System.out.printf("%-20s", "显示磁盘位示图"); 94 | System.out.printf("%-20s", "show"); 95 | System.out.println(); 96 | 97 | System.out.printf("%-20s", "register"); 98 | System.out.printf("%-20s", "用户注册"); 99 | System.out.printf("%-20s", "register [用户名] [密码]"); 100 | System.out.println(); 101 | 102 | System.out.printf("%-20s", "login"); 103 | System.out.printf("%-20s", "用户登录"); 104 | System.out.printf("%-20s", "login [用户名] [密码]"); 105 | System.out.println(); 106 | 107 | System.out.printf("%-20s", "logout"); 108 | System.out.printf("%-20s", "用户注销"); 109 | System.out.printf("%-20s", "logout"); 110 | System.out.println(); 111 | 112 | System.out.printf("%-20s", "exit"); 113 | System.out.printf("%-20s", "退出系统"); 114 | System.out.printf("%-20s", "exit"); 115 | System.out.println(); 116 | 117 | System.out.printf("%-20s", "mkdir"); 118 | System.out.printf("%-20s", "创建目录"); 119 | System.out.printf("%-20s", "mkdir [目录名]"); 120 | System.out.println(); 121 | 122 | System.out.printf("%-20s", "dir"); 123 | System.out.printf("%-20s", "列出文件"); 124 | System.out.printf("%-20s", "dir"); 125 | System.out.println(); 126 | 127 | System.out.printf("%-20s", "cd"); 128 | System.out.printf("%-20s", "切换目录"); 129 | System.out.printf("%-20s", "cd [目录名 or 目录路径] ([cd ..]表示返回上一级目录)"); 130 | System.out.println(); 131 | 132 | System.out.printf("%-20s", "create"); 133 | System.out.printf("%-20s", "创建文件"); 134 | System.out.printf("%-20s", "create [文件名]"); 135 | System.out.println(); 136 | 137 | System.out.printf("%-20s", "open"); 138 | System.out.printf("%-20s", "打开文件"); 139 | System.out.printf("%-20s", "open [文件名 or 文件路径]"); 140 | System.out.println(); 141 | 142 | System.out.printf("%-20s", "read"); 143 | System.out.printf("%-20s", "读取文件"); 144 | System.out.printf("%-20s", "read [要读取的记录个数]"); 145 | System.out.println(); 146 | 147 | System.out.printf("%-20s", "write"); 148 | System.out.printf("%-20s", "写入文件"); 149 | System.out.printf("%-20s", "write [回车键],然后输入要写入的内容,支持换行,以\"###\"做为写入结束标志"); 150 | System.out.println(); 151 | 152 | System.out.printf("%-20s", "close"); 153 | System.out.printf("%-20s", "关闭文件"); 154 | System.out.printf("%-20s", "close"); 155 | System.out.println(); 156 | 157 | System.out.printf("%-20s", "delete"); 158 | System.out.printf("%-20s", "删除文件"); 159 | System.out.printf("%-20s", "delete [文件名 or 文件路径]"); 160 | System.out.println(); 161 | 162 | System.out.printf("%-20s", "rename"); 163 | System.out.printf("%-20s", "重命名文件"); 164 | System.out.printf("%-20s", "rename [文件名 or 文件路径] [新文件名] (也可以重命名目录文件)"); 165 | System.out.println(); 166 | 167 | System.out.println("----------------------------------------------------"); 168 | return CommonResult.operateSuccess(); 169 | } 170 | 171 | @Override 172 | public CommonResult printBitmap(Integer[][] bitmap) { 173 | System.out.print(" "); 174 | for (int i = 0; i < DiskConstant.BITMAP_LINE_LENGTH; i++) { 175 | System.out.printf("%-3d", i); 176 | } 177 | System.out.println(); 178 | 179 | System.out.print(" -"); 180 | for (int i = 0; i < DiskConstant.BITMAP_LINE_LENGTH; i++) { 181 | System.out.print("---"); 182 | } 183 | System.out.println(); 184 | 185 | for (int i = 0; i < DiskConstant.BITMAP_ROW_LENGTH; i++) { 186 | System.out.printf("%2d", i); 187 | System.out.print("|["); 188 | 189 | int j; 190 | for (j = 0; j < DiskConstant.BITMAP_LINE_LENGTH - 1; j++) { 191 | System.out.print(bitmap[i][j] + ", "); 192 | } 193 | System.out.print(bitmap[i][j]); 194 | System.out.println("]"); 195 | } 196 | System.out.println("Tip: 0-表示空闲,1-表示已分配"); 197 | System.out.println("磁盘信息: 盘块数[" + DiskConstant.BLOCK_NUM + "个], 盘块大小[" + DiskConstant.BLOCK_SIZE + "B], " + 198 | "磁盘总大小[" + DiskConstant.DISK_SIZE + "B]"); 199 | 200 | return CommonResult.operateSuccess(); 201 | } 202 | } 203 | -------------------------------------------------------------------------------- /src/main/java/com/spice/service/impl/FileServiceImpl.java: -------------------------------------------------------------------------------- 1 | package com.spice.service.impl; 2 | 3 | import com.spice.data.DataCache; 4 | import com.spice.data.Memory; 5 | import com.spice.entity.ActiveFile; 6 | import com.spice.entity.Directory; 7 | import com.spice.entity.FileControlBlock; 8 | import com.spice.enums.ProtectType; 9 | import com.spice.result.CommonResult; 10 | import com.spice.service.DirectoryService; 11 | import com.spice.service.DiskService; 12 | import com.spice.service.FileService; 13 | import com.spice.util.FileUtil; 14 | import com.spice.util.StringUtil; 15 | 16 | import java.time.LocalDateTime; 17 | import java.util.LinkedList; 18 | import java.util.List; 19 | import java.util.Objects; 20 | 21 | /** 22 | * @author spice 23 | * @date 2021/06/18 1:07 24 | */ 25 | public class FileServiceImpl implements FileService { 26 | 27 | private final DiskService diskService = new DiskServiceImpl(); 28 | 29 | private final DirectoryService directoryService = new DirectoryServiceImpl(); 30 | 31 | @Override 32 | public CommonResult createFile(String fileName) { 33 | if (StringUtil.isAllSpace(fileName)) { 34 | return CommonResult.operateFailWithMessage("[创建文件失败]: 文件名不能为空"); 35 | } 36 | 37 | // 去除前后空格 38 | fileName = fileName.trim(); 39 | for (Directory directory : Memory.getInstance().getCurrentDirectory().getChildDirectory()) { 40 | if (directory.getFileControlBlock().getFileName().equalsIgnoreCase(fileName)) { 41 | return CommonResult.operateFailWithMessage("[创建文件失败]: 文件名重复"); 42 | } 43 | } 44 | 45 | // 新建数据文件的文件控制块。注意:创建新文件的时候,文件没有记录,不为它分配盘块 46 | FileControlBlock fileControlBlock = new FileControlBlock() 47 | .setDirectory(false) 48 | .setFileName(fileName) 49 | .setSuffix(FileUtil.getSuffix(fileName)) 50 | .setStartBlock(null) 51 | .setBlockNum(0) 52 | .setCreateTime(LocalDateTime.now()) 53 | .setUpdateTime(LocalDateTime.now()) 54 | .setProtectTypeList(ProtectType.getAllPermission()); 55 | // 将文件控制块存储到磁盘中 56 | DataCache.getInstance().getDisk().getFileControlBlockList().add(fileControlBlock); 57 | 58 | // 新建一个目录项 59 | Directory directory = new Directory() 60 | .setFileControlBlock(fileControlBlock) 61 | .setChildDirectory(null) 62 | .setParentIndex(DataCache.getInstance().getDisk().getDirectoryStruct().indexOf(Memory.getInstance().getCurrentDirectory())); 63 | // 将目录项保存到树形结构目录中 64 | DataCache.getInstance().getDisk().getDirectoryStruct().add(directory); 65 | directory.setIndex(DataCache.getInstance().getDisk().getDirectoryStruct().size() - 1); 66 | Memory.getInstance().getCurrentDirectory().getChildDirectory().add(directory); 67 | return CommonResult.operateSuccess(); 68 | } 69 | 70 | @Override 71 | public CommonResult openFile(String path) { 72 | if (StringUtil.isAllSpace(path)) { 73 | return CommonResult.operateFailWithMessage("[打开文件失败]: 文件名不能为空"); 74 | } 75 | 76 | if (Objects.nonNull(Memory.getInstance().getActiveFile())) { 77 | return CommonResult.operateFailWithMessage("[打开文件失败]: 当前存在未关闭的文件"); 78 | } 79 | 80 | CommonResult resolveResult = directoryService.pathResolve(path); 81 | if (resolveResult.isSuccess() && !resolveResult.getData().getFileControlBlock().isDirectory()) { 82 | // 将文件的记录从磁盘中读取出来 83 | CommonResult> result = diskService.getRecord(resolveResult.getData().getFileControlBlock().getStartBlock(), 84 | resolveResult.getData().getFileControlBlock().getBlockNum()); 85 | if (result.isSuccess()) { 86 | ActiveFile activeFile = new ActiveFile(); 87 | activeFile.setFileControlBlock(resolveResult.getData().getFileControlBlock()); 88 | activeFile.setFileRecord(result.getData()); 89 | activeFile.setReadPtr(0); 90 | activeFile.setWritePtr(activeFile.getFileRecord().size()); 91 | 92 | // 加载进内存中 93 | Memory.getInstance().setActiveFile(activeFile); 94 | return CommonResult.operateSuccess(activeFile); 95 | } else { 96 | return CommonResult.operateFailWithMessage("[打开文件失败]: " + result.getMessage()); 97 | } 98 | } 99 | 100 | return CommonResult.operateFailWithMessage("[打开文件失败]: 文件不存在"); 101 | } 102 | 103 | @Override 104 | public CommonResult readFile(Integer recordNum) { 105 | if (Objects.isNull(recordNum) || recordNum == 0) { 106 | return CommonResult.operateSuccess(""); 107 | } 108 | 109 | ActiveFile activeFile = Memory.getInstance().getActiveFile(); 110 | if (Objects.isNull(activeFile)) { 111 | return CommonResult.operateFailWithMessage("[读取文件失败]: 请先打开文件再进行读取"); 112 | } 113 | 114 | StringBuilder readResult = new StringBuilder(); 115 | if (recordNum > 0) { 116 | // 向前读取记录时 117 | if (activeFile.getReadPtr() + recordNum > activeFile.getFileRecord().size()) { 118 | return CommonResult.operateFailWithMessage("[读取文件失败]: 超过文件长度"); 119 | } 120 | 121 | // 读取记录 122 | for (int i = 0; i < recordNum; i++) { 123 | // 读取一个记录 124 | readResult.append(activeFile.getFileRecord().get(activeFile.getReadPtr())); 125 | // 读指针向前移动 126 | activeFile.setReadPtr(activeFile.getReadPtr() + 1); 127 | } 128 | } else { 129 | // 向后读取记录时 130 | if (activeFile.getReadPtr() + recordNum < 0) { 131 | return CommonResult.operateFailWithMessage("[读取文件失败]: 小于文件长度"); 132 | } 133 | 134 | // 读取记录 135 | for (int i = 0; i > recordNum; i--) { 136 | // 读取一个记录 137 | readResult.insert(0, activeFile.getFileRecord().get(activeFile.getReadPtr())); 138 | // 读指针向后移动 139 | activeFile.setReadPtr(activeFile.getReadPtr() - 1); 140 | } 141 | } 142 | 143 | return CommonResult.operateSuccess(readResult.toString()); 144 | } 145 | 146 | @Override 147 | public CommonResult writeToFile(String record) { 148 | if (StringUtil.isEmpty(record)) { 149 | return CommonResult.operateSuccess(); 150 | } 151 | 152 | ActiveFile activeFile = Memory.getInstance().getActiveFile(); 153 | if (Objects.isNull(activeFile)) { 154 | return CommonResult.operateFailWithMessage("[写入文件失败]: 请先打开文件再进行写入"); 155 | } 156 | 157 | // 原文件记录 158 | List originalRecord = activeFile.getFileRecord(); 159 | List toInsert = new LinkedList<>(); 160 | for (char ch : record.toCharArray()) { 161 | toInsert.add(ch); 162 | } 163 | originalRecord.addAll(activeFile.getWritePtr(), toInsert); 164 | 165 | // 存储记录 166 | CommonResult resultTwo = diskService.storeRecord(activeFile.getFileControlBlock(), originalRecord); 167 | if (resultTwo.isSuccess()) { 168 | // 设置写指针 169 | Memory.getInstance().getActiveFile().setWritePtr(Memory.getInstance().getActiveFile().getWritePtr() + record.length()); 170 | // 设置内存中的文件记录 171 | Memory.getInstance().getActiveFile().setFileRecord(originalRecord); 172 | Memory.getInstance().getActiveFile().setFileControlBlock(resultTwo.getData()); 173 | return CommonResult.operateSuccess(); 174 | } else { 175 | return CommonResult.operateFailWithMessage("[写入文件失败]: " + resultTwo.getMessage()); 176 | } 177 | } 178 | 179 | @Override 180 | public CommonResult closeFile() { 181 | if (Objects.isNull(Memory.getInstance().getActiveFile())) { 182 | return CommonResult.operateFailWithMessage("[关闭文件失败]: 当前没有文件被打开"); 183 | } 184 | 185 | Memory.getInstance().getActiveFile().getFileControlBlock().setUpdateTime(LocalDateTime.now()); 186 | Memory.getInstance().setActiveFile(null); 187 | return CommonResult.operateSuccess(); 188 | } 189 | 190 | @Override 191 | public CommonResult deleteFile(String path) { 192 | if (StringUtil.isAllSpace(path)) { 193 | return CommonResult.operateFailWithMessage("[删除文件失败]: 文件名不能为空"); 194 | } 195 | 196 | CommonResult resolveResult = directoryService.pathResolve(path); 197 | if (resolveResult.isSuccess() && !resolveResult.getData().getFileControlBlock().isDirectory()) { 198 | if (Objects.nonNull(Memory.getInstance().getActiveFile()) && 199 | resolveResult.getData().getFileControlBlock().equals(Memory.getInstance().getActiveFile().getFileControlBlock())) { 200 | return CommonResult.operateFailWithMessage("[删除文件失败]: 该文件已被打开,请先关闭"); 201 | } 202 | 203 | // 如果找到目录项且是数据文件类型 204 | Directory parentDirectory = DataCache.getInstance().getDisk().getDirectoryStruct().get(resolveResult.getData().getParentIndex()); 205 | parentDirectory.getChildDirectory().remove(resolveResult.getData()); 206 | DataCache.getInstance().getDisk().getDirectoryStruct().set(resolveResult.getData().getIndex(), null); 207 | // 释放空间 208 | diskService.freeSpace(resolveResult.getData().getFileControlBlock().getStartBlock(), 209 | resolveResult.getData().getFileControlBlock().getBlockNum()); 210 | return CommonResult.operateSuccess(); 211 | } else { 212 | return CommonResult.operateFailWithMessage("[删除文件失败]: 找不到对应文件"); 213 | } 214 | } 215 | 216 | @Override 217 | public CommonResult renameFile(String path, String newName) { 218 | if (StringUtil.isAllSpace(path) || StringUtil.isAllSpace(newName)) { 219 | return CommonResult.operateFailWithMessage("[重命名文件失败]: 文件名不能为空"); 220 | } 221 | 222 | CommonResult resolveResult = directoryService.pathResolve(path); 223 | if (resolveResult.isSuccess()) { 224 | Directory parentDirectory = DataCache.getInstance().getDisk().getDirectoryStruct().get(resolveResult.getData().getParentIndex()); 225 | for (Directory childDirectory : parentDirectory.getChildDirectory()) { 226 | if (childDirectory.getFileControlBlock().getFileName().equalsIgnoreCase(newName)) { 227 | return CommonResult.operateFailWithMessage("[重命名文件失败]: 文件名重复"); 228 | } 229 | } 230 | 231 | resolveResult.getData().getFileControlBlock() 232 | .setFileName(newName) 233 | .setSuffix(resolveResult.getData().getFileControlBlock().isDirectory() ? null : FileUtil.getSuffix(newName)) 234 | .setUpdateTime(LocalDateTime.now()); 235 | return CommonResult.operateSuccess(); 236 | } 237 | return CommonResult.operateFailWithMessage("[重命名文件失败]: 文件不存在"); 238 | } 239 | } 240 | -------------------------------------------------------------------------------- /src/main/java/com/spice/service/impl/UserServiceImpl.java: -------------------------------------------------------------------------------- 1 | package com.spice.service.impl; 2 | 3 | import com.spice.data.DataCache; 4 | import com.spice.data.Memory; 5 | import com.spice.entity.Directory; 6 | import com.spice.entity.FileControlBlock; 7 | import com.spice.entity.User; 8 | import com.spice.result.CommonResult; 9 | import com.spice.service.UserService; 10 | import com.spice.util.StringUtil; 11 | 12 | import java.time.LocalDateTime; 13 | import java.util.LinkedList; 14 | import java.util.Objects; 15 | 16 | /** 17 | * @author spice 18 | * @date 2021/06/19 1:13 19 | */ 20 | public class UserServiceImpl implements UserService { 21 | 22 | @Override 23 | public CommonResult register(String username, String password) { 24 | if (Objects.nonNull(Memory.getInstance().getCurrentUser())) { 25 | return CommonResult.operateFailWithMessage("[注册用户失败]: 请先注销当前已登录用户"); 26 | } 27 | 28 | if (StringUtil.isAllSpace(username)) { 29 | return CommonResult.operateFailWithMessage("[注册用户失败]: 用户名不能为空"); 30 | } 31 | if (StringUtil.isAllSpace(password)) { 32 | return CommonResult.operateFailWithMessage("[注册用户失败]: 密码不能为空"); 33 | } 34 | 35 | User existedUser = DataCache.getInstance().getDisk().getUserMap().get(username); 36 | if (Objects.nonNull(existedUser)) { 37 | return CommonResult.operateFailWithMessage("[注册用户失败]: 该用户已存在"); 38 | } 39 | 40 | // 新建一个系统用户并保存 41 | DataCache.getInstance().getDisk().getUserMap().put(username, new User().setUsername(username).setPassword(password)); 42 | // 新建一个用户文件夹 43 | FileControlBlock fileControlBlock = new FileControlBlock() 44 | .setDirectory(true) 45 | .setFileName(username) 46 | .setSuffix(null) 47 | .setStartBlock(null) 48 | .setBlockNum(null) 49 | .setCreateTime(LocalDateTime.now()) 50 | .setUpdateTime(LocalDateTime.now()) 51 | .setProtectTypeList(null); 52 | DataCache.getInstance().getDisk().getFileControlBlockList().add(fileControlBlock); 53 | 54 | // 新建一个用户目录项 55 | Directory directory = new Directory() 56 | .setFileControlBlock(fileControlBlock) 57 | .setChildDirectory(new LinkedList<>()) 58 | .setParentIndex(0); 59 | // 保存 60 | DataCache.getInstance().getDisk().getDirectoryStruct().add(directory); 61 | directory.setIndex(DataCache.getInstance().getDisk().getDirectoryStruct().size() - 1); 62 | // 添加到根目录下 63 | DataCache.getInstance().getDisk().getDirectoryStruct().get(0).getChildDirectory().add(directory); 64 | 65 | return CommonResult.operateSuccessWithMessage("[注册用户成功]"); 66 | } 67 | 68 | @Override 69 | public CommonResult login(String username, String password) { 70 | if (Objects.nonNull(Memory.getInstance().getCurrentUser())) { 71 | return CommonResult.operateFailWithMessage("[用户登录失败]: 请先注销当前已登录用户"); 72 | } 73 | 74 | User user = DataCache.getInstance().getDisk().getUserMap().get(username); 75 | if (Objects.isNull(user)) { 76 | return CommonResult.operateFailWithMessage("[用户登录失败]: 该用户不存在"); 77 | } 78 | if (!user.getPassword().equals(password)) { 79 | return CommonResult.operateFailWithMessage("[用户登录失败]: 密码错误"); 80 | } 81 | 82 | for (Directory directory : DataCache.getInstance().getDisk().getDirectoryStruct().get(0).getChildDirectory()) { 83 | if (directory.getFileControlBlock().isDirectory() && directory.getFileControlBlock().getFileName().equals(username)) { 84 | Memory.getInstance().setCurrentUser(user); 85 | // 切换到用户目录 86 | Memory.getInstance().setCurrentDirectory(directory); 87 | return CommonResult.operateSuccess(); 88 | } 89 | } 90 | return CommonResult.operateFailWithMessage("[用户登录失败]: 系统错误!找不到用户目录!!"); 91 | } 92 | 93 | @Override 94 | public CommonResult logout() { 95 | if (Objects.isNull(Memory.getInstance().getCurrentUser())) { 96 | return CommonResult.operateFailWithMessage("[用户注销失败]: 当前没有登录用户"); 97 | } 98 | 99 | Memory.getInstance().setCurrentUser(null); 100 | // 切换到根目录 101 | Memory.getInstance().setCurrentDirectory(DataCache.getInstance().getDisk().getDirectoryStruct().get(0)); 102 | return CommonResult.operateSuccess(); 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /src/main/java/com/spice/util/CollectionUtil.java: -------------------------------------------------------------------------------- 1 | package com.spice.util; 2 | 3 | import java.util.Collection; 4 | 5 | /** 6 | * 集合操作工具类 7 | * 8 | * @author spice 9 | * @date 2021/06/08 23:32 10 | */ 11 | public class CollectionUtil { 12 | 13 | /** 14 | * 判断集合是否为空 15 | * 16 | * @param collection 集合 17 | * @return true-为空;false-不为空 18 | */ 19 | public static boolean isEmpty(Collection collection) { 20 | return (collection == null || collection.isEmpty()); 21 | } 22 | 23 | /** 24 | * 判断集合是否不为空 25 | * 26 | * @param collection 集合 27 | * @return true-不为空;false-为空 28 | */ 29 | public static boolean isNotEmpty(Collection collection) { 30 | return !isEmpty(collection); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/com/spice/util/FileUtil.java: -------------------------------------------------------------------------------- 1 | package com.spice.util; 2 | 3 | /** 4 | * 文件操作工具类 5 | * 6 | * @author spice 7 | * @date 2021/06/08 23:49 8 | */ 9 | public class FileUtil { 10 | 11 | /** 12 | * 文件名与文件后缀名之间的分隔符 13 | */ 14 | private static final String SEPARATOR = "."; 15 | 16 | /** 17 | * 获取文件的后缀名 18 | * 19 | * @param fileName 文件名 20 | * @return 后缀名 21 | */ 22 | public static String getSuffix(String fileName) { 23 | if (fileName == null || "".equals(fileName)) { 24 | return ""; 25 | } 26 | 27 | int index = fileName.lastIndexOf(SEPARATOR); 28 | if (index == fileName.length()) { 29 | return ""; 30 | } 31 | return fileName.substring(index + 1, fileName.length()); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/com/spice/util/Math.java: -------------------------------------------------------------------------------- 1 | package com.spice.util; 2 | 3 | /** 4 | * 和计算有关的工具类 5 | * 6 | * @author spice 7 | * @date 2021/06/17 22:47 8 | */ 9 | public class Math { 10 | 11 | /** 12 | * 向上取整的除法 13 | * 14 | * @param dividend 被除数 15 | * @param divisor 除数 16 | * @return 返回 (dividend / divisor) 的向上取整结果 17 | */ 18 | public static Integer ceilDivide(Integer dividend, Integer divisor) { 19 | if (dividend % divisor == 0) { 20 | // 整除 21 | return dividend / divisor; 22 | } else { 23 | // 不整除,向上取整 24 | return (dividend + divisor) / divisor; 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/com/spice/util/StringUtil.java: -------------------------------------------------------------------------------- 1 | package com.spice.util; 2 | 3 | /** 4 | * 字符串操作工具类 5 | * 6 | * @author spice 7 | * @date 2021/06/08 23:56 8 | */ 9 | public class StringUtil { 10 | 11 | /** 12 | * 判断字符串是否为空 13 | * 14 | * @param str 字符串 15 | * @return true-为空;false-不为空 16 | */ 17 | public static boolean isEmpty(String str) { 18 | return (str == null || "".equals(str)); 19 | } 20 | 21 | /** 22 | * 判断字符串是否不为空 23 | * 24 | * @param str 字符串 25 | * @return true-不为空;false-为空 26 | */ 27 | public static boolean isNotEmpty(String str) { 28 | return !isEmpty(str); 29 | } 30 | 31 | /** 32 | * 判断字符串是否为空,或者是否全是空格符 33 | * 34 | * @param str 字符串 35 | * @return true-为空,或者全是空格符 36 | */ 37 | public static boolean isAllSpace(String str) { 38 | return (str == null || "".equals(str.trim())); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /文件管理系统 操作系统课程设计.doc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spice-wolf/FileManagementSystem/6b8f4c9e176d3ffe4625d9c50ab1796a67cc6585/文件管理系统 操作系统课程设计.doc --------------------------------------------------------------------------------