├── .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
--------------------------------------------------------------------------------