├── .idea ├── .gitignore ├── encodings.xml ├── misc.xml ├── modules.xml ├── uiDesigner.xml └── vcs.xml ├── README.md ├── img └── Package controller.png ├── mvc_file_manager.iml ├── out └── production │ └── mvc_file_manager │ ├── META-INF │ └── mvc_file_manager.kotlin_module │ ├── main │ ├── Util │ │ ├── FileCode.class │ │ ├── FileCopy.class │ │ ├── FileIconCreate.class │ │ └── FileZip.class │ ├── controller │ │ ├── FileController$CopyListener.class │ │ ├── FileController$CreateFileListener.class │ │ ├── FileController$DecodeListener.class │ │ ├── FileController$DeleteListener.class │ │ ├── FileController$EncodeListener.class │ │ ├── FileController$FileClickListener.class │ │ ├── FileController$FileTreeSelListener.class │ │ ├── FileController$GoListener.class │ │ ├── FileController$PasteListener.class │ │ ├── FileController$RedoListener.class │ │ ├── FileController$UnZipListener.class │ │ ├── FileController$UndoListener.class │ │ ├── FileController$ZipListener.class │ │ └── FileController.class │ ├── model │ │ ├── FileModel.class │ │ └── FileTreeModel.class │ └── view │ │ ├── App.class │ │ ├── FileDisplay.class │ │ ├── FileListCellRenderer.class │ │ ├── FilesTree$DefaultFileNodes.class │ │ └── FilesTree.class │ └── res │ └── ui.png └── src ├── main ├── Util │ ├── FileCode.java │ ├── FileCopy.java │ ├── FileIconCreate.java │ └── FileZip.java ├── controller │ └── FileController.java ├── model │ ├── FileModel.java │ └── FileTreeModel.java └── view │ ├── App.java │ ├── FileDisplay.java │ ├── FileListCellRenderer.java │ └── FilesTree.java └── res └── ui.png /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /workspace.xml -------------------------------------------------------------------------------- /.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/uiDesigner.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 设计思想 2 | 3 | ## `MVC`设计模式 4 | 5 | 本项目引入`mvc`设计模式。 6 | 7 | MVC 模式代表 Model-View-Controller(模型-视图-控制器) 模式。这种模式用于应用程序的分层开发。 8 | 9 | - **Model(模型)** - 模型代表一个存取数据的对象或 JAVA POJO。它也可以带有逻辑,在数据变化时更新控制器。 10 | - **View(视图)** - 视图代表模型包含的数据的可视化。 11 | - **Controller(控制器)** - 控制器作用于模型和视图上。它控制数据流向模型对象,并在数据变化时更新视图。它使视图与模型分离开。 12 | 13 | `Java swing`的设计本身就是MVC设计的典范。在这里我们也采用MVC设计模式(尽管并不是那么贴合),与`swing`本身相结合。 14 | 15 | 对我们来说,**view**由`Java swing`负责显示,**model**是我们自己建立的数据结构,同时结合`swing`自身的**model**,所有操作的具体实现由**model**负责,即涉及到和系统文件层的交互等,**controller**由我们自行创建,负责添加事件监听并做出相应的逻辑操作,对界面的多个部分进行协同配合,完成视图的更新和数据的更新。 16 | 17 | # 各模块功能实现 18 | 19 | ## 功能实现清单 20 | 21 | 1. **界面跳转:** 22 | 1. 后退,逐级返回到父级目录。 23 | 2. 前进,按照后退的顺序依次进入子目录。 24 | 3. 当前目录路径显示,输入目录地址进行跳转。 25 | 2. **界面显示:** 26 | 1. 文件目录树显示(只显示文件夹)。 27 | 2. 文件列表显示(只显示当前目录下的文件夹和文件),带有系统图标。 28 | 3. 点击文件树中的结点,在文件列表同步显示。 29 | 3. **选择与打开** 30 | 1. 支持多选,选中文件高亮显示,点击空白区域取消选中。 31 | 2. 单击文件夹进入,单击文件打开应用。 32 | 4. **右键选中文件** 33 | 1. 批量复制,包括所有子目录和文件。 34 | 2. 批量压缩,格式为zip,将多个文件压缩成一个,自定义命名。 35 | 3. 单文件加密,自定义加密密钥。 36 | 5. **右键空白区域** 37 | 1. 创建文件夹,自定义命名; 38 | 2. 批量粘贴,粘贴的目录不能是源文件目录的子目录; 39 | 3. 批量解压,解压到当前目录下。 40 | 4. 单文件解密,输入加密密钥,若密钥不正确,加密后的文件仍无法打开。 41 | 42 | ## 类的设计 43 | 44 | 采用MVC设计模式。 45 | 46 | ### package view 47 | 48 | 1. #### **class App** 49 | 50 | 1. 总的界面窗口UI,继承自`JFrame`类,采用Border-layout布局。 51 | 52 | 2. 工具栏`toolbar`位于上方,实现后退、前进、当前文件路径显示和路径跳转。 53 | 54 | 3. 左侧是文件树,中部是文件显示区域。 55 | 56 | 4. 实现对应的`addListener`函数,如: 57 | 58 | ```java 59 | public void addUndoListener(ActionListener undo){ 60 | undoButton.addActionListener(undo); 61 | } 62 | public void addRedoListener(ActionListener redo){ 63 | redoButton.addActionListener(redo); 64 | } 65 | ``` 66 | 67 | 2. #### **class FileDisplay** 68 | 69 | 1. 文件主显示区,继承自`JList`,以文件列表的形式显示。 70 | 2. 内嵌了`JPopMenu`,添加`JMenuItem`,用以实现功能。 71 | 3. 实现`updateView`函数,更新视图。 72 | 4. 实现对应的`addListener`函数,添加事件监听。 73 | 74 | 3. #### **class FilesTree** 75 | 76 | 1. 文件目录树,继承自`JTree`。 77 | 2. 实现了`loadingTree`函数,更新视图。 78 | 3. 实现对应的`addListener`函数,添加事件监听。 79 | 80 | 4. #### **class FileListCellRenderer** 81 | 82 | 1. 文件列表渲染类,继承自`DefaultListCellRenderer`。 83 | 84 | ### package model 85 | 86 | 1. #### **class FileModel** 87 | 88 | 1. 文件实体类,具体实现了文件管理的各个功能。 89 | 90 | 2. #### **class FileTreeModel** 91 | 92 | 1. 对应于文件树。 93 | 94 | ### package controller 95 | 96 | 1. #### **class FileController** 97 | 98 | 1. 包含了 99 | 100 | ```java 101 | private App app; // 窗体UI 102 | private FileDisplay fileDisplay; // 为了方便调用,内部存储 103 | private FilesTree filesTree; // 为了方便调用,内部存储 104 | private FileModel fileModel; // File Model 105 | private FileTreeModel treeModel; // Tree Model 106 | ``` 107 | 108 | 2. 初始化的时候,由`model`,`view`参数: 109 | 110 | ```java 111 | public FileController(App app, FileModel fileModel, FileTreeModel treeModel) 112 | ``` 113 | 114 | 3. 实现了对应的事件监听类接口,添加方法。 115 | 116 | 4. 调用`model`和`view`实现。 117 | 118 | ### package util 119 | 120 | 1. #### **class FileCode** 文件加密工具类 121 | 122 | 2. #### **class FileCopy** 文件复制工具类 123 | 124 | 3. #### **class FileIconCreate** 系统图标工具类 125 | 126 | 4. #### **class FileZip** 文件压缩工具类 127 | 128 | 129 | 130 | ## 算法设计 131 | 132 | 主要是`FileModel`的实现 133 | 134 | 1. ### 数据记录 135 | 136 | 1. 由`List defaultListModel`和`File currentFile`负责记录当前目录和目录下的文件、文件夹。 137 | 138 | 2. ### **初始化** 139 | 140 | 1. `initRoots`方法,将磁盘目录,如`C:\`,`D:\`,`E:\`添加进文件列表。 141 | 142 | 3. ### **文件更新** 143 | 144 | 1. `updateModels(File file)`方法,将当前文件设为`file`,并将其当前目录层级下的文件(夹)添加进文件列表。 145 | 146 | 4. ### **前进和后退** 147 | 148 | 1. 由`redoStack`负责记录操作路径,undo时,得到父级目录下文件和文件夹,更新文件列表,并将文件压入`redoStack`。 149 | 2. redo时,弹出栈顶文件,更新当前文件列表。 150 | 3. 细节,如果父级为`null`,说明当前是磁盘根目录,如`D:\`,则调用初始化方法`initRoots`。 151 | 152 | 5. ### **文件复制** 153 | 154 | 1. 由`copySources`负责存储粘贴板中的文件。 155 | 2. 选中文件后,加入到`copySources`中。 156 | 3. 粘贴时,判断是文件,还是文件夹 157 | 1. 如果是文件,则进行文件复制。 158 | 2. 如果是文件夹,递归调用。 159 | 4. 异常处理,粘贴的目录不能是源目录的子目录,由`isParent`进行判断。 160 | 161 | 6. ### **创建和删除** 162 | 163 | 1. 如果不存在,则创建。 164 | 2. 递归删除,由`deleteHelp`负责递归,如果删除的是文件夹,就递归调用,最终删除根文件。 165 | 3. 对选中的文件遍历执行,批量删除。 166 | 167 | 7. ### **压缩和解压** 168 | 169 | 1. 对于文件,直接压缩。 170 | 2. 对于文件夹,递归压缩。 171 | 172 | 8. ### **文件加密和解密** 173 | 174 | 1. 对文件读取字节内容,与密钥进行简单的异或加密。 175 | 2. 加密后得到新文件,删除原始文件,并将新文件重命名为原文件。(也可以不删除,防止出问题,重要文件) 176 | 3. 解密的时候,与加密过程相同,输入密钥,读取文件,异或解密。 177 | 178 | 9. ### **取消选中** 179 | 180 | 1. 重写`locationToIndex`函数,判断点击的位置是否有效 181 | 182 | ```java 183 | int index = super.locationToIndex(location); 184 | if (index != -1 && !getCellBounds(index, index).contains(location)) return -1; 185 | ``` 186 | 187 | 10. ### **样式渲染** 188 | 189 | 1. 实现`FileListCellRenderer`类继承`DefaultListCellRenderer` 190 | 2. 设置图标,设置显示信息,磁盘根目录使用`getPath()`,其他目录使用`getName()`。 191 | 3. 选中设置颜色,取消选中恢复之前的颜色。 192 | 193 | 11. ### **文件树展开** 194 | 195 | 1. 实现`TreeSelectionListener`接口,采用延迟加载,即每次点击展开时再加载目录文件。 196 | 197 | # 软件调试分析 198 | 199 | ## 执行过程 200 | 201 | 1. 启动`App`类的`main`函数 202 | 203 | 2. 在`FileController`初始化的时候,完成对数据成员的初始化,包括添加事件监听,更新初始视图。 204 | 205 | ```java 206 | public FileController(App app, FileModel fileModel, FileTreeModel treeModel) { 207 | this.fileDisplay = app.getFileDisplay(); 208 | this.app = app; 209 | this.fileModel = fileModel; 210 | this.filesTree = app.getFilesTree(); 211 | this.treeModel = treeModel; 212 | // 添加事件监听 213 | fileDisplay.addCopyListener(new CopyListener()); 214 | fileDisplay.addCreateListener(new CreateFileListener()); 215 | fileDisplay.addPasteListener(new PasteListener()); 216 | fileDisplay.addUnzipListener(new UnZipListener()); 217 | fileDisplay.addZipListener(new ZipListener()); 218 | fileDisplay.addDeleteListener(new DeleteListener()); 219 | fileDisplay.addMyMouseListener(new FileClickListener()); 220 | fileDisplay.addEncodeListener(new EncodeListener()); 221 | fileDisplay.addDecodeListener(new DecodeListener()); 222 | app.addUndoListener(new UndoListener()); 223 | app.addRedoListener(new RedoListener()); 224 | app.addGoListener(new GoListener()); 225 | filesTree.addTreeSelectionListener(new FileTreeSelListener()); 226 | filesTree.setRootVisible(false); 227 | // 初始化主视图 228 | fileDisplay.updateView(fileModel.getDefaultListModel()); 229 | } 230 | ``` 231 | 232 | 3. 监听到事件发生,调用`model`和`view`进行逻辑实现并更新视图。例如,展开文件目录树时,需要对三部分视图进行更新(文件树,文件列表,url地址栏) 233 | 234 | ```java 235 | class FileTreeSelListener implements TreeSelectionListener { 236 | @Override 237 | public void valueChanged(TreeSelectionEvent e) { 238 | File file = filesTree.getCurrentFile(); // 得到点击的文件结点 239 | // 加载目录树 240 | filesTree.loadingTree((DefaultMutableTreeNode) 241 | filesTree.getLastSelectedPathComponent(), treeModel.getFiles(file)); 242 | // 更新文件数据模型 243 | fileModel.updateModels(file); 244 | // 更新文件列表视图 245 | fileDisplay.updateView(fileModel.getDefaultListModel()); 246 | // 更新url地址显示 247 | String url = fileModel.getUrl(); 248 | app.updateUrl(url); 249 | } 250 | } 251 | ``` 252 | 253 | 再如,undo操作时,`model`进行undo,更新数据,然后视图更新 254 | 255 | ```java 256 | class UndoListener implements ActionListener{ 257 | @Override 258 | public void actionPerformed(ActionEvent e) { 259 | fileModel.undo(); // 数据模型更新 260 | fileDisplay.updateView(fileModel.getDefaultListModel()); // 列表视图更新 261 | String url = fileModel.getUrl(); 262 | app.updateUrl(url); // url地址栏更新 263 | } 264 | } 265 | ``` 266 | 267 | ## 界面展示 268 | 269 | 270 | 271 | ### 主界面 272 | 273 | 274 | 275 | - 上侧可以后撤、前进,url地址栏显示 276 | - 左侧目录树展开,并同步更新右侧文件列表 277 | 278 | ### 文件及文件夹拷贝 279 | 280 | 281 | 282 | 右键粘贴,将子目录下所有文件(夹)粘贴 283 | 284 | ### 解压压缩 285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | ### 文件加密 293 | 294 | (以整数`int`类型为密钥,**加密需要等待一些时间**) 295 | 296 | 解密后,检查是否可以正常打开. 297 | 298 | **注:**对文件夹进行加密是无效的 299 | 300 | ### 其他 301 | 302 | 见项目 -------------------------------------------------------------------------------- /img/Package controller.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ifrozenwhale/file_manager/b586afaf635af00a435acc639250c65718ffe46c/img/Package controller.png -------------------------------------------------------------------------------- /mvc_file_manager.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /out/production/mvc_file_manager/META-INF/mvc_file_manager.kotlin_module: -------------------------------------------------------------------------------- 1 |  -------------------------------------------------------------------------------- /out/production/mvc_file_manager/main/Util/FileCode.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ifrozenwhale/file_manager/b586afaf635af00a435acc639250c65718ffe46c/out/production/mvc_file_manager/main/Util/FileCode.class -------------------------------------------------------------------------------- /out/production/mvc_file_manager/main/Util/FileCopy.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ifrozenwhale/file_manager/b586afaf635af00a435acc639250c65718ffe46c/out/production/mvc_file_manager/main/Util/FileCopy.class -------------------------------------------------------------------------------- /out/production/mvc_file_manager/main/Util/FileIconCreate.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ifrozenwhale/file_manager/b586afaf635af00a435acc639250c65718ffe46c/out/production/mvc_file_manager/main/Util/FileIconCreate.class -------------------------------------------------------------------------------- /out/production/mvc_file_manager/main/Util/FileZip.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ifrozenwhale/file_manager/b586afaf635af00a435acc639250c65718ffe46c/out/production/mvc_file_manager/main/Util/FileZip.class -------------------------------------------------------------------------------- /out/production/mvc_file_manager/main/controller/FileController$CopyListener.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ifrozenwhale/file_manager/b586afaf635af00a435acc639250c65718ffe46c/out/production/mvc_file_manager/main/controller/FileController$CopyListener.class -------------------------------------------------------------------------------- /out/production/mvc_file_manager/main/controller/FileController$CreateFileListener.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ifrozenwhale/file_manager/b586afaf635af00a435acc639250c65718ffe46c/out/production/mvc_file_manager/main/controller/FileController$CreateFileListener.class -------------------------------------------------------------------------------- /out/production/mvc_file_manager/main/controller/FileController$DecodeListener.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ifrozenwhale/file_manager/b586afaf635af00a435acc639250c65718ffe46c/out/production/mvc_file_manager/main/controller/FileController$DecodeListener.class -------------------------------------------------------------------------------- /out/production/mvc_file_manager/main/controller/FileController$DeleteListener.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ifrozenwhale/file_manager/b586afaf635af00a435acc639250c65718ffe46c/out/production/mvc_file_manager/main/controller/FileController$DeleteListener.class -------------------------------------------------------------------------------- /out/production/mvc_file_manager/main/controller/FileController$EncodeListener.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ifrozenwhale/file_manager/b586afaf635af00a435acc639250c65718ffe46c/out/production/mvc_file_manager/main/controller/FileController$EncodeListener.class -------------------------------------------------------------------------------- /out/production/mvc_file_manager/main/controller/FileController$FileClickListener.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ifrozenwhale/file_manager/b586afaf635af00a435acc639250c65718ffe46c/out/production/mvc_file_manager/main/controller/FileController$FileClickListener.class -------------------------------------------------------------------------------- /out/production/mvc_file_manager/main/controller/FileController$FileTreeSelListener.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ifrozenwhale/file_manager/b586afaf635af00a435acc639250c65718ffe46c/out/production/mvc_file_manager/main/controller/FileController$FileTreeSelListener.class -------------------------------------------------------------------------------- /out/production/mvc_file_manager/main/controller/FileController$GoListener.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ifrozenwhale/file_manager/b586afaf635af00a435acc639250c65718ffe46c/out/production/mvc_file_manager/main/controller/FileController$GoListener.class -------------------------------------------------------------------------------- /out/production/mvc_file_manager/main/controller/FileController$PasteListener.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ifrozenwhale/file_manager/b586afaf635af00a435acc639250c65718ffe46c/out/production/mvc_file_manager/main/controller/FileController$PasteListener.class -------------------------------------------------------------------------------- /out/production/mvc_file_manager/main/controller/FileController$RedoListener.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ifrozenwhale/file_manager/b586afaf635af00a435acc639250c65718ffe46c/out/production/mvc_file_manager/main/controller/FileController$RedoListener.class -------------------------------------------------------------------------------- /out/production/mvc_file_manager/main/controller/FileController$UnZipListener.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ifrozenwhale/file_manager/b586afaf635af00a435acc639250c65718ffe46c/out/production/mvc_file_manager/main/controller/FileController$UnZipListener.class -------------------------------------------------------------------------------- /out/production/mvc_file_manager/main/controller/FileController$UndoListener.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ifrozenwhale/file_manager/b586afaf635af00a435acc639250c65718ffe46c/out/production/mvc_file_manager/main/controller/FileController$UndoListener.class -------------------------------------------------------------------------------- /out/production/mvc_file_manager/main/controller/FileController$ZipListener.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ifrozenwhale/file_manager/b586afaf635af00a435acc639250c65718ffe46c/out/production/mvc_file_manager/main/controller/FileController$ZipListener.class -------------------------------------------------------------------------------- /out/production/mvc_file_manager/main/controller/FileController.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ifrozenwhale/file_manager/b586afaf635af00a435acc639250c65718ffe46c/out/production/mvc_file_manager/main/controller/FileController.class -------------------------------------------------------------------------------- /out/production/mvc_file_manager/main/model/FileModel.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ifrozenwhale/file_manager/b586afaf635af00a435acc639250c65718ffe46c/out/production/mvc_file_manager/main/model/FileModel.class -------------------------------------------------------------------------------- /out/production/mvc_file_manager/main/model/FileTreeModel.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ifrozenwhale/file_manager/b586afaf635af00a435acc639250c65718ffe46c/out/production/mvc_file_manager/main/model/FileTreeModel.class -------------------------------------------------------------------------------- /out/production/mvc_file_manager/main/view/App.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ifrozenwhale/file_manager/b586afaf635af00a435acc639250c65718ffe46c/out/production/mvc_file_manager/main/view/App.class -------------------------------------------------------------------------------- /out/production/mvc_file_manager/main/view/FileDisplay.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ifrozenwhale/file_manager/b586afaf635af00a435acc639250c65718ffe46c/out/production/mvc_file_manager/main/view/FileDisplay.class -------------------------------------------------------------------------------- /out/production/mvc_file_manager/main/view/FileListCellRenderer.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ifrozenwhale/file_manager/b586afaf635af00a435acc639250c65718ffe46c/out/production/mvc_file_manager/main/view/FileListCellRenderer.class -------------------------------------------------------------------------------- /out/production/mvc_file_manager/main/view/FilesTree$DefaultFileNodes.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ifrozenwhale/file_manager/b586afaf635af00a435acc639250c65718ffe46c/out/production/mvc_file_manager/main/view/FilesTree$DefaultFileNodes.class -------------------------------------------------------------------------------- /out/production/mvc_file_manager/main/view/FilesTree.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ifrozenwhale/file_manager/b586afaf635af00a435acc639250c65718ffe46c/out/production/mvc_file_manager/main/view/FilesTree.class -------------------------------------------------------------------------------- /out/production/mvc_file_manager/res/ui.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ifrozenwhale/file_manager/b586afaf635af00a435acc639250c65718ffe46c/out/production/mvc_file_manager/res/ui.png -------------------------------------------------------------------------------- /src/main/Util/FileCode.java: -------------------------------------------------------------------------------- 1 | package main.Util; 2 | 3 | import java.io.*; 4 | 5 | /** 6 | * 文件加密工具类 7 | */ 8 | public class FileCode { 9 | private static int dataOfFile = 0; //文件字节内容 10 | 11 | /** 12 | * 加密 13 | * @param srcFile 要加密的文件 14 | * @param encFile 加密后的文件 15 | * @param key 密钥 16 | * @throws Exception 异常 17 | */ 18 | public static void EncFile(File srcFile, File encFile, int key) throws Exception { 19 | // 检查是否存在 20 | if(!srcFile.exists()){ 21 | System.out.println("source file not exixt"); 22 | return; 23 | } 24 | if(!encFile.exists()){ 25 | System.out.println("encrypt file created"); 26 | encFile.createNewFile(); 27 | } 28 | InputStream fis = new FileInputStream(srcFile); 29 | OutputStream fos = new FileOutputStream(encFile); 30 | 31 | while ((dataOfFile = fis.read()) > -1) { 32 | fos.write(dataOfFile^key); 33 | } 34 | 35 | fis.close(); 36 | fos.flush(); 37 | fos.close(); 38 | } 39 | 40 | /** 41 | * 解密 42 | * @param encFile 加密了的文件 43 | * @param decFile 要解密生成的文件 44 | * @param key 密钥 45 | * @throws Exception 异常 46 | */ 47 | public static void DecFile(File encFile, File decFile, int key) throws Exception { 48 | if(!encFile.exists()){ 49 | System.out.println("encrypt file not exixt"); 50 | return; 51 | } 52 | 53 | if(!decFile.exists()){ 54 | System.out.println("decrypt file created"); 55 | decFile.createNewFile(); 56 | } 57 | InputStream fis = new FileInputStream(encFile); 58 | OutputStream fos = new FileOutputStream(decFile); 59 | while ((dataOfFile = fis.read()) > -1) {fos.write(dataOfFile^key); } 60 | 61 | fis.close(); 62 | fos.flush(); 63 | fos.close(); 64 | } 65 | 66 | /** 67 | * 测试类 68 | * @param args 参数 69 | */ 70 | public static void main(String[] args) { 71 | File srcFile = new File("E:\\Alice\\html\\whale2.jpg"); //初始文件 72 | File encFile = new File("E:\\Alice\\html\\whale2Encode.jpg"); //加密文件 73 | File decFile = new File("E:\\Alice\\html\\whale2Decode.jpg"); //解密文件 74 | 75 | try { 76 | EncFile(srcFile, encFile,5123); //加密操作 77 | DecFile(encFile, decFile, 51223); 78 | } catch (Exception e) { 79 | e.printStackTrace(); 80 | } 81 | } 82 | 83 | } -------------------------------------------------------------------------------- /src/main/Util/FileCopy.java: -------------------------------------------------------------------------------- 1 | package main.Util; 2 | 3 | import javax.swing.*; 4 | import java.io.*; 5 | 6 | /** 7 | * 文件拷贝工具类 8 | */ 9 | public class FileCopy { 10 | /** 11 | * 拷贝文件 12 | * @param sourceUrl 源文件 13 | * @param targetUrl 目标文件 14 | */ 15 | public static void copyFile(String sourceUrl, String targetUrl){ 16 | File source = new File(sourceUrl); 17 | File target = new File(targetUrl); 18 | try(BufferedInputStream bis=new BufferedInputStream(new FileInputStream(source)); 19 | BufferedOutputStream bos=new BufferedOutputStream(new FileOutputStream(target))) { 20 | int len = 0; 21 | byte[] flush = new byte[1024]; 22 | while((len=bis.read(flush)) != -1) { 23 | bos.write(flush, 0, len); 24 | } 25 | bos.flush(); 26 | } catch(IOException e) { 27 | e.printStackTrace(); 28 | } 29 | } 30 | 31 | /** 32 | * 判断是否是其父级目录 33 | * @param f1 f1 is Parent? 34 | * @param f2 f2 is child? 35 | * @return true if f1 is parent of f2, else not 36 | */ 37 | private static boolean isParent(File f1, File f2){ 38 | if(f2.getParentFile() == null){ 39 | return false; 40 | }else if(f2.getParentFile().equals(f1)){ 41 | return true; 42 | }else{ 43 | return isParent(f1, f2.getParentFile()); 44 | } 45 | } 46 | 47 | /** 48 | * 拷贝文件夹 49 | * @param sourceUrl 源文件夹 50 | * @param targetUrl 目标文件夹 51 | */ 52 | public static void copyDir(String sourceUrl, String targetUrl){ 53 | File source = new File(sourceUrl); 54 | File target = new File(targetUrl); 55 | if(isParent(source, target)){ 56 | System.out.println("Can't copy!"); 57 | JOptionPane.showMessageDialog(null, "source path is father of target", "Copy Error",JOptionPane.ERROR_MESSAGE); 58 | return; 59 | }; 60 | String[] filesUrl = source.list(); 61 | // 新建 62 | if(!target.exists()){ 63 | target.mkdir(); 64 | } 65 | for(String fileUrl : filesUrl){ 66 | String tmp1 = sourceUrl + File.separator + fileUrl; 67 | String tmp2 = targetUrl + File.separator + fileUrl; 68 | if(new File(tmp1).isDirectory()){ 69 | copyDir(tmp1, tmp2);//递归调用 70 | }else{ 71 | copyFile(tmp1, tmp2); // 直接拷贝文件 72 | } 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/main/Util/FileIconCreate.java: -------------------------------------------------------------------------------- 1 | package main.Util; 2 | 3 | import javax.swing.*; 4 | import javax.swing.filechooser.FileSystemView; 5 | import java.io.File; 6 | 7 | /** 8 | * 创建文件图标工具类 9 | * 得到系统图标 10 | */ 11 | public class FileIconCreate { 12 | public static Icon getSmallIcon(File f ) { 13 | if ( f != null && f.exists() ) { 14 | FileSystemView fsv = FileSystemView.getFileSystemView(); 15 | return(fsv.getSystemIcon( f ) ); 16 | } 17 | return(null); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/main/Util/FileZip.java: -------------------------------------------------------------------------------- 1 | package main.Util; 2 | 3 | import java.io.*; 4 | import java.util.Enumeration; 5 | import java.util.List; 6 | import java.util.zip.*; 7 | 8 | /** 9 | * 文件压缩工具类 10 | * 密钥(int)与文件字节异或加密 11 | */ 12 | public class FileZip { 13 | static final int BUFFER = 8192; 14 | 15 | /** 16 | * 压缩 17 | * @param pathName 要压缩的文件列表 18 | * @param zipFilePath 压缩后的路径 19 | */ 20 | public static void compress(List pathName, String zipFilePath) { 21 | File zipFile = new File(zipFilePath); 22 | ZipOutputStream out = null; 23 | try { 24 | FileOutputStream fileOutputStream = new FileOutputStream(zipFile); 25 | CheckedOutputStream cos = new CheckedOutputStream(fileOutputStream, 26 | new CRC32()); 27 | out = new ZipOutputStream(cos); 28 | String basedir = ""; 29 | for (int i = 0; i < pathName.size(); i++) { 30 | compress(new File(pathName.get(i)), out, basedir); 31 | } 32 | out.close(); 33 | } catch (Exception e) { 34 | throw new RuntimeException(e); 35 | } 36 | } 37 | 38 | /** 39 | * 压缩单个文件 40 | * @param srcPathName 源文件 41 | * @param targetPathName 目标地址 42 | */ 43 | public static void compress(String srcPathName, String targetPathName) { 44 | File zipFile = new File(targetPathName); 45 | File file = new File(srcPathName); 46 | if (!file.exists()) 47 | throw new RuntimeException(srcPathName + "不存在!"); 48 | try { 49 | FileOutputStream fileOutputStream = new FileOutputStream(zipFile); 50 | CheckedOutputStream cos = new CheckedOutputStream(fileOutputStream, 51 | new CRC32()); 52 | ZipOutputStream out = new ZipOutputStream(cos); 53 | String basedir = ""; 54 | compress(file, out, basedir); 55 | out.close(); 56 | } catch (Exception e) { 57 | throw new RuntimeException(e); 58 | } 59 | } 60 | 61 | /** 62 | * 压缩 63 | * @param file 选中的文件 64 | * @param out 输出 65 | * @param basedir 基目录 66 | */ 67 | private static void compress(File file, ZipOutputStream out, String basedir) { 68 | /* 判断是目录还是文件 */ 69 | if (file.isDirectory()) { 70 | System.out.println("压缩:" + basedir + file.getName()); 71 | compressDirectory(file, out, basedir); 72 | } else { 73 | System.out.println("压缩:" + basedir + file.getName()); 74 | compressFile(file, out, basedir); 75 | } 76 | } 77 | 78 | /** 79 | * 压缩一个目录 80 | */ 81 | private static void compressDirectory(File dir, ZipOutputStream out, String basedir) { 82 | if (!dir.exists()) 83 | return; 84 | 85 | File[] files = dir.listFiles(); 86 | for (int i = 0; i < files.length; i++) { 87 | /* 递归 */ 88 | compress(files[i], out, basedir + dir.getName() + "/"); 89 | } 90 | } 91 | 92 | /** 93 | * 压缩一个文件 94 | */ 95 | private static void compressFile(File file, ZipOutputStream out, String basedir) { 96 | if (!file.exists()) { 97 | return; 98 | } 99 | try { 100 | BufferedInputStream bis = new BufferedInputStream( 101 | new FileInputStream(file)); 102 | ZipEntry entry = new ZipEntry(basedir + file.getName()); 103 | out.putNextEntry(entry); 104 | int count; 105 | byte data[] = new byte[BUFFER]; 106 | while ((count = bis.read(data, 0, BUFFER)) != -1) { 107 | out.write(data, 0, count); 108 | } 109 | bis.close(); 110 | } catch (Exception e) { 111 | throw new RuntimeException(e); 112 | } 113 | } 114 | 115 | /** 116 | * 解压文件 117 | * @param inputFile 要解压的文件 118 | * @param destDirPath 目标文件 119 | * @throws Exception 异常 120 | */ 121 | public static void zipUncompress(String inputFile,String destDirPath) throws Exception { 122 | File srcFile = new File(inputFile);//获取当前压缩文件 123 | // 判断源文件是否存在 124 | if (!srcFile.exists()) { 125 | throw new Exception(srcFile.getPath() + "所指文件不存在"); 126 | } 127 | ZipFile zipFile = new ZipFile(srcFile);//创建压缩文件对象 128 | //开始解压 129 | Enumeration entries = zipFile.entries(); 130 | while (entries.hasMoreElements()) { 131 | ZipEntry entry = (ZipEntry) entries.nextElement(); 132 | // 如果是文件夹,就创建个文件夹 133 | if (entry.isDirectory()) { 134 | String dirPath = destDirPath + "/" + entry.getName(); 135 | srcFile.mkdirs(); 136 | } else { 137 | // 如果是文件,就先创建一个文件,然后用io流把内容copy过去 138 | File targetFile = new File(destDirPath + "/" + entry.getName()); 139 | // 保证这个文件的父文件夹必须要存在 140 | if (!targetFile.getParentFile().exists()) { 141 | targetFile.getParentFile().mkdirs(); 142 | } 143 | targetFile.createNewFile(); 144 | // 将压缩文件内容写入到这个文件中 145 | InputStream is = zipFile.getInputStream(entry); 146 | FileOutputStream fos = new FileOutputStream(targetFile); 147 | int len; 148 | byte[] buf = new byte[1024]; 149 | while ((len = is.read(buf)) != -1) { 150 | fos.write(buf, 0, len); 151 | } 152 | // 关流顺序,先打开的后关闭 153 | fos.close(); 154 | is.close(); 155 | } 156 | } 157 | } 158 | } 159 | -------------------------------------------------------------------------------- /src/main/controller/FileController.java: -------------------------------------------------------------------------------- 1 | package main.controller; 2 | 3 | import main.model.FileModel; 4 | import main.view.FileDisplay; 5 | import main.model.FileTreeModel; 6 | import main.view.FilesTree; 7 | import main.view.App; 8 | 9 | import java.awt.event.ActionEvent; 10 | import java.awt.event.ActionListener; 11 | import java.awt.event.MouseAdapter; 12 | import java.awt.event.MouseEvent; 13 | import java.io.File; 14 | import java.util.List; 15 | 16 | import javax.swing.JList; 17 | import javax.swing.JOptionPane; 18 | import javax.swing.event.TreeSelectionEvent; 19 | import javax.swing.event.TreeSelectionListener; 20 | import javax.swing.tree.DefaultMutableTreeNode; 21 | 22 | public class FileController { 23 | private FileDisplay fileDisplay; // 文件列表显示区 24 | private App app; // 主窗口UI 25 | private FileModel fileModel; // 文件数据 26 | private FilesTree filesTree; // 文件树 27 | private FileTreeModel treeModel; // 文件树数据 28 | public FileController(App app, FileModel fileModel, FileTreeModel treeModel) { 29 | // 初始化赋值 30 | this.fileDisplay = app.getFileDisplay(); 31 | this.app = app; 32 | this.fileModel = fileModel; 33 | this.filesTree = app.getFilesTree(); 34 | this.treeModel = treeModel; 35 | // 初始化添加事件监听 36 | fileDisplay.addCopyListener(new CopyListener()); 37 | fileDisplay.addCreateListener(new CreateFileListener()); 38 | fileDisplay.addPasteListener(new PasteListener()); 39 | fileDisplay.addUnzipListener(new UnZipListener()); 40 | fileDisplay.addZipListener(new ZipListener()); 41 | fileDisplay.addDeleteListener(new DeleteListener()); 42 | fileDisplay.addMyMouseListener(new FileClickListener()); 43 | fileDisplay.addEncodeListener(new EncodeListener()); 44 | fileDisplay.addDecodeListener(new DecodeListener()); 45 | app.addUndoListener(new UndoListener()); 46 | app.addRedoListener(new RedoListener()); 47 | app.addGoListener(new GoListener()); 48 | filesTree.addTreeSelectionListener(new FileTreeSelListener()); 49 | filesTree.setRootVisible(false); 50 | // 初始化更新视图 51 | fileDisplay.updateView(fileModel.getDefaultListModel()); 52 | 53 | } 54 | 55 | /** 56 | * copy 事件监听类 57 | */ 58 | class CopyListener implements ActionListener{ 59 | @Override 60 | public void actionPerformed(ActionEvent e) { 61 | @SuppressWarnings("unchecked") 62 | List valuesList = fileDisplay.getSelectedValuesList(); 63 | fileModel.addCopySources(valuesList); // 添加进剪切板 64 | fileDisplay.updateView(fileModel.getDefaultListModel()); // 更新视图 65 | } 66 | 67 | } 68 | 69 | /** 70 | * 压缩监听类 71 | */ 72 | class ZipListener implements ActionListener{ 73 | @Override 74 | public void actionPerformed(ActionEvent e) { 75 | @SuppressWarnings("unchecked") 76 | List valuesList = fileDisplay.getSelectedValuesList(); 77 | String name = JOptionPane.showInputDialog("input the name of zip");// 得到文件名 78 | fileModel.zipCompress(valuesList, name);// 压缩 79 | fileDisplay.updateView(fileModel.getDefaultListModel()); // 更新视图 80 | } 81 | } 82 | 83 | /** 84 | * 解压事件类 85 | */ 86 | class UnZipListener implements ActionListener{ 87 | @Override 88 | public void actionPerformed(ActionEvent e) { 89 | @SuppressWarnings("unchecked") 90 | List fileList = fileDisplay.getSelectedValuesList(); 91 | fileModel.zipUnCompress(fileList); 92 | fileDisplay.updateView(fileModel.getDefaultListModel()); 93 | } 94 | } 95 | 96 | /** 97 | * 粘贴事件类 98 | */ 99 | class PasteListener implements ActionListener{ 100 | @Override 101 | public void actionPerformed(ActionEvent e) { 102 | fileModel.paste(); 103 | fileDisplay.updateView(fileModel.getDefaultListModel()); 104 | } 105 | } 106 | 107 | /** 108 | * 新建文件夹事件类 109 | */ 110 | class CreateFileListener implements ActionListener{ 111 | @Override 112 | public void actionPerformed(ActionEvent e) { 113 | String name = JOptionPane.showInputDialog("please input the name of dict"); 114 | if(name != null){ 115 | fileModel.createFile(name); 116 | } 117 | fileDisplay.updateView(fileModel.getDefaultListModel()); 118 | } 119 | } 120 | 121 | /** 122 | * 删除事件类 123 | */ 124 | class DeleteListener implements ActionListener{ 125 | @Override 126 | public void actionPerformed(ActionEvent e) { 127 | @SuppressWarnings("unchecked") 128 | List fileList = fileDisplay.getSelectedValuesList(); 129 | fileModel.delete(fileList); 130 | fileDisplay.updateView(fileModel.getDefaultListModel()); 131 | } 132 | } 133 | 134 | /** 135 | * 加密事件类 136 | */ 137 | class EncodeListener implements ActionListener{ 138 | @Override 139 | public void actionPerformed(ActionEvent e) { 140 | @SuppressWarnings("unchecked") 141 | List files = (List) fileDisplay.getSelectedValuesList(); 142 | int key = Integer.parseInt(JOptionPane.showInputDialog("please input the key(int)")); 143 | fileModel.enCrypt(files, key); 144 | fileDisplay.updateView(fileModel.getDefaultListModel()); 145 | } 146 | } 147 | 148 | /** 149 | * 解压事件类 150 | */ 151 | class DecodeListener implements ActionListener{ 152 | @Override 153 | public void actionPerformed(ActionEvent e) { 154 | @SuppressWarnings("unchecked") 155 | List files = (List) fileDisplay.getSelectedValuesList(); 156 | int key = Integer.parseInt(JOptionPane.showInputDialog("please input the key(int)")); 157 | fileModel.decrypt(files, key); 158 | fileDisplay.updateView(fileModel.getDefaultListModel()); 159 | JOptionPane.showMessageDialog(null, "Please check it.\nIf it can't be opened, make sure key is right"); 160 | } 161 | } 162 | 163 | /** 164 | * 文件点击事件类 165 | */ 166 | class FileClickListener extends MouseAdapter { 167 | @Override 168 | public void mouseClicked(MouseEvent e) { 169 | JList list = (JList) e.getSource(); 170 | if (list.locationToIndex(e.getPoint()) == -1 && !e.isShiftDown()){ 171 | list.clearSelection(); 172 | } 173 | } 174 | @Override 175 | public void mousePressed(MouseEvent e) { 176 | if(e.getButton() == MouseEvent.BUTTON3){ // 如果是右键 177 | // 这是右键选中 178 | // int index = locationToIndex(e.getPoint()); 179 | // setSelectedIndex(index); 180 | if(!fileDisplay.getSelectedValuesList().isEmpty()){ // 在空白区域点击 181 | fileDisplay.showFileMenu(e); 182 | }else{ // 选中文件点击 183 | fileDisplay.showDicMenu(e); 184 | } 185 | }else if(e.getButton() == MouseEvent.BUTTON1 && !fileDisplay.getSelectedValuesList().isEmpty()) { // 左键单击选中给定的文件 186 | if (e.getClickCount() == 2) { // 双击 187 | File file = (File) fileDisplay.getSelectedValue(); 188 | if(!fileModel.openFile(file)){ 189 | JOptionPane.showMessageDialog(null, "No application is associated with \nthe specified file for this operation"); 190 | } 191 | fileDisplay.updateView(fileModel.getDefaultListModel()); 192 | String url = fileModel.getUrl(); 193 | app.updateUrl(url); 194 | } 195 | } 196 | } 197 | } 198 | 199 | /** 200 | * 撤回事件类 201 | */ 202 | class UndoListener implements ActionListener{ 203 | @Override 204 | public void actionPerformed(ActionEvent e) { 205 | fileModel.undo(); 206 | fileDisplay.updateView(fileModel.getDefaultListModel()); 207 | String url = fileModel.getUrl(); 208 | app.updateUrl(url); 209 | } 210 | } 211 | 212 | /** 213 | * 前进事件类 214 | */ 215 | class RedoListener implements ActionListener{ 216 | @Override 217 | public void actionPerformed(ActionEvent e) { 218 | fileModel.redo(); 219 | fileDisplay.updateView(fileModel.getDefaultListModel()); 220 | String url = fileModel.getUrl(); 221 | app.updateUrl(url); 222 | } 223 | } 224 | 225 | /** 226 | * 跳转事件类 227 | */ 228 | class GoListener implements ActionListener{ 229 | @Override 230 | public void actionPerformed(ActionEvent e) { 231 | String url = app.getUrlInfo(); 232 | File file = new File(url); 233 | fileModel.updateModels(file); 234 | fileDisplay.updateView(fileModel.getDefaultListModel()); 235 | } 236 | } 237 | 238 | /** 239 | * 文件树选择事件类 240 | */ 241 | class FileTreeSelListener implements TreeSelectionListener { 242 | @Override 243 | public void valueChanged(TreeSelectionEvent e) { 244 | File file = filesTree.getCurrentFile(); 245 | filesTree.loadingTree((DefaultMutableTreeNode) filesTree.getLastSelectedPathComponent(), treeModel.getFiles(file)); 246 | fileModel.updateModels(file); 247 | fileDisplay.updateView(fileModel.getDefaultListModel()); 248 | String url = fileModel.getUrl(); 249 | app.updateUrl(url); 250 | } 251 | } 252 | } 253 | -------------------------------------------------------------------------------- /src/main/model/FileModel.java: -------------------------------------------------------------------------------- 1 | package main.model; 2 | 3 | import main.Util.FileCode; 4 | import main.Util.FileCopy; 5 | import main.Util.FileZip; 6 | 7 | import java.awt.*; 8 | import java.io.File; 9 | import java.io.IOException; 10 | import java.util.ArrayList; 11 | import java.util.Arrays; 12 | import java.util.List; 13 | import java.util.Stack; 14 | 15 | /** 16 | * 文件数据模型 17 | */ 18 | public class FileModel{ 19 | private List defaultListModel; // 文件列表 20 | private List copySources; // 剪切板 21 | private File currentFile; // 当前文件路径 22 | private Stack redoStack; // redo 栈 23 | 24 | /** 25 | * 得到当前url 26 | * @return url地址 27 | */ 28 | public String getUrl(){ 29 | if(currentFile == null) return null; 30 | return currentFile.getPath(); 31 | } 32 | public FileModel(){ 33 | init(); 34 | } 35 | public List getDefaultListModel() { 36 | return defaultListModel; 37 | } 38 | 39 | private void init(){ 40 | defaultListModel = new ArrayList<>(); 41 | copySources = new ArrayList<>(); 42 | redoStack = new Stack<>(); 43 | initRoots(); 44 | } 45 | 46 | /** 47 | * 初始化根目录 48 | */ 49 | public void initRoots(){ 50 | defaultListModel.clear(); 51 | defaultListModel.addAll(Arrays.asList(File.listRoots())); 52 | } 53 | 54 | /** 55 | * 更新文件数据 56 | * @param file 当前的文件 57 | */ 58 | public void updateModels(File file){ 59 | currentFile = file; 60 | defaultListModel.clear(); 61 | File[] files = file.listFiles(); 62 | defaultListModel.addAll(Arrays.asList(files)); 63 | } 64 | 65 | /** 66 | * 添加文件到剪切板 67 | * @param files 文件列表 68 | */ 69 | public void addCopySources(List files){ 70 | for(File file: files){ 71 | String str = file.getPath(); 72 | copySources.add(str); 73 | } 74 | } 75 | 76 | /** 77 | * 压缩 78 | * @param files 文件列表 79 | * @param name 文件名 80 | */ 81 | public void zipCompress(List files, String name){ 82 | List zipFiles = new ArrayList<>(); 83 | for(File file:files ){ 84 | zipFiles.add(file.getPath()); 85 | } 86 | 87 | if(name != null){ 88 | FileZip.compress(zipFiles, currentFile.getPath() + File.separator + name + ".zip"); 89 | updateModels(currentFile); 90 | } 91 | } 92 | 93 | /** 94 | * 解压 95 | * @param fileList 文件列表 96 | */ 97 | public void zipUnCompress(List fileList){ 98 | for(File f: fileList){ 99 | try { 100 | FileZip.zipUncompress(f.getPath(), currentFile.getPath()); 101 | } catch (Exception e) { 102 | e.printStackTrace(); 103 | } 104 | } 105 | updateModels(currentFile); 106 | } 107 | 108 | /** 109 | * 剪切 110 | */ 111 | public void paste(){ 112 | for(String s : copySources){ 113 | File tmp = new File(s); 114 | if(tmp.isFile()) { 115 | FileCopy.copyFile(s, currentFile.getPath() + File.separator + tmp.getName()); 116 | } else { 117 | FileCopy.copyDir(s, currentFile.getPath() + File.separator + tmp.getName()); 118 | } 119 | } 120 | copySources.clear(); 121 | updateModels(currentFile); 122 | } 123 | 124 | /** 125 | * 新建文件夹爱 126 | * @param name 文件夹名字 127 | */ 128 | public void createFile(String name){ 129 | File dir = new File( currentFile.getPath() + File.separator + name); 130 | if (!dir.exists()) {// 判断目录是否存在 131 | dir.mkdir(); 132 | getDefaultListModel().add(dir); 133 | } 134 | } 135 | 136 | /** 137 | * 递归删除函数 138 | * @param file 要递归删除的文件 139 | * @return 是否成功删除 140 | */ 141 | private boolean deleteHelp(File file){ 142 | if(!file.exists()){ 143 | return false; 144 | } 145 | if(file.isDirectory()){ 146 | File[] files = file.listFiles(); 147 | for(File f : files){ 148 | deleteHelp(f); 149 | } 150 | } 151 | return file.delete(); 152 | } 153 | 154 | /** 155 | * 删除 156 | * @param fileList 要删除的文件列表 157 | */ 158 | public void delete(List fileList){ 159 | for(File f: fileList){ 160 | defaultListModel.remove(f); 161 | deleteHelp(f); 162 | } 163 | } 164 | 165 | /** 166 | * 加密 167 | * @param files 要加密的文件列表 168 | * @param key 加密密钥 169 | */ 170 | public void enCrypt(List files, int key){ 171 | try { 172 | for(File file : files){ 173 | if(file.isFile()){ 174 | String name = file.getName(); 175 | String parent = file.getParent(); 176 | File tmp = new File(parent + File.separator + "encode_" + name); 177 | FileCode.EncFile(file, tmp, key); 178 | file.delete(); 179 | tmp.renameTo(new File(parent + File.separator + name)); 180 | } 181 | 182 | } 183 | } catch (Exception e) { 184 | e.printStackTrace(); 185 | } 186 | } 187 | 188 | /** 189 | * 解密 190 | * @param files 要解密的文件 191 | * @param key 密钥 192 | */ 193 | public void decrypt(List files, int key){ 194 | try { 195 | for(File file:files){ 196 | File tmp = new File(file.getParent() + File.separator + "decode_" + file.getName()); 197 | FileCode.DecFile(file, tmp, key); 198 | if(!defaultListModel.contains(tmp)){ 199 | defaultListModel.add(tmp); 200 | } 201 | 202 | } 203 | } catch (Exception e) { 204 | e.printStackTrace(); 205 | } 206 | } 207 | 208 | /** 209 | * 打开文件 210 | * @param file 文件 211 | * @return 是否打开成功 212 | */ 213 | public boolean openFile(File file){ 214 | if(file.isFile()){ 215 | try { 216 | Desktop.getDesktop().open(file); 217 | } catch (IOException ex) { 218 | return false; 219 | // ex.printStackTrace(); 220 | } 221 | }else{ 222 | updateModels(file); 223 | redoStack.clear(); 224 | } 225 | return true; 226 | } 227 | 228 | /** 229 | * 后退操作 230 | */ 231 | public void undo(){ 232 | File file = currentFile; 233 | // 如果是根目录 234 | if(file == null){ 235 | initRoots(); 236 | currentFile = null; 237 | } 238 | // 如果父级目录是根目录 239 | if(file.getParentFile() == null){ 240 | initRoots(); 241 | currentFile = null; 242 | }else{ 243 | updateModels(file.getParentFile()); 244 | } 245 | redoStack.push(file); 246 | } 247 | 248 | /** 249 | * 前进操作 250 | */ 251 | public void redo(){ 252 | if(redoStack.isEmpty()){ 253 | return; 254 | } 255 | File file = redoStack.pop(); 256 | updateModels(file); 257 | } 258 | } 259 | -------------------------------------------------------------------------------- /src/main/model/FileTreeModel.java: -------------------------------------------------------------------------------- 1 | package main.model; 2 | 3 | import java.io.File; 4 | 5 | /** 6 | * 文件目录树数据模型 7 | */ 8 | public class FileTreeModel{ 9 | public File[] getRoots(){ 10 | return File.listRoots(); 11 | } 12 | 13 | /** 14 | * 得到文件列表 15 | * @param currentFile 文件 16 | * @return 文件目录下的文件列表 17 | */ 18 | public File[] getFiles(File currentFile) { 19 | return currentFile.listFiles(); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/main/view/App.java: -------------------------------------------------------------------------------- 1 | package main.view; 2 | 3 | import main.controller.FileController; 4 | import main.model.FileModel; 5 | import main.model.FileTreeModel; 6 | 7 | import javax.swing.*; 8 | import javax.swing.border.EmptyBorder; 9 | import java.awt.*; 10 | import java.awt.event.ActionListener; 11 | 12 | /** 13 | * App 启动类 14 | */ 15 | public class App extends JFrame{ 16 | private JScrollPane jspList; // 文件列表的滑动块 17 | private JScrollPane jspTree; // 文件树的滑动块 18 | private JButton undoButton; // 后退 19 | private JButton redoButton; // 前进 20 | private FileDisplay fileDisplay; // 21 | private JToolBar toolBar; // 工具栏 22 | private JTextField urlBar; // 地址栏 23 | private FilesTree filesTree; // 文件树 24 | private JButton goButton; // 跳转 25 | public FileDisplay getFileDisplay() { 26 | return fileDisplay; 27 | } 28 | 29 | public App(){ 30 | init(); 31 | } 32 | 33 | /** 34 | * 得到url地址栏信息 35 | * @return url 36 | */ 37 | public String getUrlInfo() { 38 | return urlBar.getText(); 39 | } 40 | 41 | /** 42 | * 初始化 43 | */ 44 | public void init(){ 45 | setTitle("file manager"); 46 | setSize(500, 400); 47 | setLocationRelativeTo(null); 48 | 49 | // toolbar 50 | toolBar = new JToolBar(); 51 | toolBar.setAlignmentY(10.5F); 52 | toolBar.setBorder(null); 53 | toolBar.setFloatable(false); 54 | toolBar.setEnabled(false); 55 | toolBar.setFocusCycleRoot(true); 56 | toolBar.addSeparator(new Dimension(30, 10)); 57 | add(toolBar, BorderLayout.NORTH); 58 | // undoButton 后退按钮 59 | undoButton = new JButton(); 60 | undoButton.setText("undo"); 61 | undoButton.setBorderPainted(false); 62 | undoButton.setBackground(new Color(203, 205, 205)); 63 | toolBar.add(undoButton); 64 | 65 | // redoButton 前进按钮 66 | redoButton = new JButton(); 67 | redoButton.setText("redo"); 68 | redoButton.setBorderPainted(false); 69 | redoButton.setBackground(new Color(148, 152, 154)); 70 | 71 | toolBar.add(redoButton); 72 | toolBar.addSeparator(new Dimension(20, 10)); 73 | 74 | // 文件 List 滑动块 75 | fileDisplay = new FileDisplay(); 76 | jspList = new JScrollPane(fileDisplay); 77 | jspList.setBorder(new EmptyBorder(0, 0, 0, 0)); 78 | add(jspList, BorderLayout.CENTER); 79 | 80 | // url bar 81 | urlBar = new JTextField(); 82 | toolBar.add(urlBar); 83 | 84 | // goButton 跳转按钮 85 | goButton = new JButton(); 86 | goButton.setText("go"); 87 | goButton.setBorderPainted(false); 88 | goButton.setBackground(new Color(203, 205, 205)); 89 | toolBar.add(goButton); 90 | 91 | 92 | // filesTree 文件树 93 | filesTree = new FilesTree(); 94 | jspTree = new JScrollPane(filesTree); 95 | jspTree.setPreferredSize(new Dimension(200, 0)); 96 | add(jspTree, BorderLayout.WEST); 97 | 98 | setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 99 | } 100 | 101 | /** 102 | * 更新URL地址栏 103 | * @param info 地址栏信息 104 | */ 105 | public void updateUrl(String info){ 106 | urlBar.setText(info); 107 | } 108 | 109 | /** 110 | * 添加undo 监听 111 | * @param undo undoListener 112 | */ 113 | public void addUndoListener(ActionListener undo){ 114 | undoButton.addActionListener(undo); 115 | } 116 | 117 | /** 118 | * 添加redo监听 119 | * @param redo redoListener 120 | */ 121 | public void addRedoListener(ActionListener redo){ 122 | redoButton.addActionListener(redo); 123 | } 124 | 125 | /** 126 | * 添加go监听 127 | * @param go goListener 128 | */ 129 | public void addGoListener(ActionListener go){goButton.addActionListener(go);} 130 | 131 | /** 132 | * 得到内置model 133 | * @return 134 | */ 135 | public FilesTree getFilesTree() { 136 | return filesTree; 137 | } 138 | 139 | /** 140 | * 启动入口 141 | * @param args 命令行参数 142 | */ 143 | public static void main(String[] args) { 144 | App app = new App(); // main.view 145 | FileModel fileModel = new FileModel(); // file main.model 146 | FileTreeModel treeModel = new FileTreeModel(); // file tree main.model 147 | FileController fileController = new FileController(app, fileModel, treeModel); // file main.controller 148 | app.setVisible(true); // 可见 149 | } 150 | 151 | 152 | } 153 | -------------------------------------------------------------------------------- /src/main/view/FileDisplay.java: -------------------------------------------------------------------------------- 1 | package main.view; 2 | 3 | import javax.swing.*; 4 | import javax.swing.border.EmptyBorder; 5 | import javax.swing.plaf.FontUIResource; 6 | import java.awt.*; 7 | import java.awt.event.ActionListener; 8 | import java.awt.event.MouseAdapter; 9 | import java.awt.event.MouseEvent; 10 | import java.io.File; 11 | import java.util.Enumeration; 12 | import java.util.List; 13 | 14 | public class FileDisplay extends JList { 15 | 16 | private JPopupMenu filePopupMenu; // 文件菜单 17 | private JPopupMenu dictPopupMenu; // 目录菜单 18 | private JMenuItem copyItem; // 拷贝菜单项 19 | private JMenuItem encodeItem; // 加密菜单项 20 | private JMenuItem decodeItem; // 解密菜单项 21 | private JMenuItem zipItem; // 压缩菜单项 22 | private JMenuItem unzipItem; // 解压菜单项 23 | private JMenuItem createItem; // 创建文件夹菜单项 24 | private JMenuItem pasteItem; // 粘贴菜单项 25 | private JMenuItem deleteItem; // 删除菜单项 26 | 27 | /** 28 | * 更新视图 29 | * @param files 文件列表 30 | */ 31 | public void updateView(List files){ 32 | DefaultListModel defaultListModel = new DefaultListModel<>(); 33 | for(File file : files){ 34 | defaultListModel.addElement(file); 35 | } 36 | setModel(defaultListModel); 37 | } 38 | 39 | @Override 40 | public int locationToIndex(Point location) { 41 | int index = super.locationToIndex(location); 42 | if (index != -1 && !getCellBounds(index, index).contains(location)) { 43 | return -1; 44 | } else { 45 | return index; 46 | } 47 | } 48 | 49 | /** 50 | * 初始化UI,全部字体默认fnt 51 | * @param fnt 字体 52 | */ 53 | private void initGlobalFontSetting(Font fnt){ 54 | FontUIResource fontRes = new FontUIResource(fnt); 55 | for(Enumeration keys = UIManager.getDefaults().keys(); keys.hasMoreElements();){ 56 | Object key = keys.nextElement(); 57 | Object value = UIManager.get(key); 58 | if(value instanceof FontUIResource) 59 | UIManager.put(key, fontRes); 60 | } 61 | } 62 | 63 | /** 64 | * 构造函数 65 | */ 66 | public FileDisplay() { 67 | /** 68 | * 初始化UI, 采用windows10样式 69 | */ 70 | try { 71 | UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); 72 | } catch (ClassNotFoundException | InstantiationException | IllegalAccessException 73 | | UnsupportedLookAndFeelException e) { 74 | e.printStackTrace(); 75 | } 76 | Font f = new Font("微软雅黑", Font.PLAIN,12); 77 | initGlobalFontSetting(f); 78 | 79 | // pop menu 80 | filePopupMenu = new JPopupMenu(); 81 | dictPopupMenu = new JPopupMenu(); 82 | copyItem = new JMenuItem(); 83 | encodeItem = new JMenuItem(); 84 | decodeItem = new JMenuItem(); 85 | zipItem = new JMenuItem(); 86 | unzipItem = new JMenuItem(); 87 | createItem = new JMenuItem(); 88 | pasteItem = new JMenuItem(); 89 | deleteItem = new JMenuItem(); 90 | 91 | filePopupMenu.setBorderPainted(false); 92 | //---- styleMenuitem ---- 93 | setMenuItemStyle(copyItem, "copy"); 94 | filePopupMenu.add(copyItem); 95 | 96 | setMenuItemStyle(encodeItem, "encode"); 97 | filePopupMenu.add(encodeItem); 98 | 99 | setMenuItemStyle(decodeItem, "decode"); 100 | filePopupMenu.add(decodeItem); 101 | 102 | setMenuItemStyle(zipItem, "zip"); 103 | filePopupMenu.add(zipItem); 104 | 105 | setMenuItemStyle(unzipItem, "unzip"); 106 | filePopupMenu.add(unzipItem); 107 | 108 | setMenuItemStyle(createItem, "create"); 109 | dictPopupMenu.add(createItem); 110 | 111 | dictPopupMenu.setBorderPainted(false); 112 | setMenuItemStyle(pasteItem, "paste"); 113 | dictPopupMenu.add(pasteItem); 114 | 115 | setMenuItemStyle(deleteItem, "delete"); 116 | filePopupMenu.add(deleteItem); 117 | // 渲染样式 118 | setCellRenderer(new FileListCellRenderer()); 119 | setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION); 120 | } 121 | 122 | /** 123 | * 添加监听 124 | * @param copy copyListener 125 | */ 126 | public void addCopyListener(ActionListener copy){ 127 | copyItem.addActionListener(copy); 128 | } 129 | public void addPasteListener(ActionListener paste){ 130 | pasteItem.addActionListener(paste); 131 | } 132 | public void addCreateListener(ActionListener create){ 133 | createItem.addActionListener(create); 134 | } 135 | public void addZipListener(ActionListener zip){ 136 | zipItem.addActionListener(zip); 137 | } 138 | public void addUnzipListener(ActionListener unzip){ 139 | unzipItem.addActionListener(unzip); 140 | } 141 | public void addDeleteListener(ActionListener del){ 142 | deleteItem.addActionListener(del); 143 | } 144 | public void addMyMouseListener(MouseAdapter mouseAdapter){ 145 | addMouseListener(mouseAdapter); 146 | } 147 | public void showFileMenu(MouseEvent e){ 148 | filePopupMenu.show(e.getComponent(), e.getX(), e.getY()); 149 | } 150 | public void showDicMenu(MouseEvent e){ 151 | dictPopupMenu.show(e.getComponent(), e.getX(), e.getY()); 152 | } 153 | 154 | public void addEncodeListener(ActionListener encode) {encodeItem.addActionListener(encode);} 155 | public void addDecodeListener(ActionListener decode){decodeItem.addActionListener(decode);} 156 | private void setMenuItemStyle(JMenuItem styleMenuitem, String text){ 157 | styleMenuitem.setText(text); 158 | styleMenuitem.setBorderPainted(false); 159 | } 160 | 161 | /** 162 | * 启动入口 163 | * @param args 命令参数,测试用 164 | */ 165 | public static void main(String[] args) { 166 | JFrame frame = new JFrame("File tree"); 167 | frame.setSize(500, 400); 168 | frame.setLocationRelativeTo(null); 169 | final JScrollPane jsp = new JScrollPane(new FileDisplay()); 170 | jsp.setBorder(new EmptyBorder(0, 0, 0, 0)); 171 | frame.add(jsp, BorderLayout.CENTER); 172 | frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 173 | frame.setVisible(true); 174 | } 175 | 176 | 177 | } 178 | -------------------------------------------------------------------------------- /src/main/view/FileListCellRenderer.java: -------------------------------------------------------------------------------- 1 | package main.view; 2 | 3 | import main.Util.FileIconCreate; 4 | 5 | import javax.swing.*; 6 | import javax.swing.border.Border; 7 | import java.awt.*; 8 | import java.io.File; 9 | 10 | /** 11 | * 继承默认样式, 自定义 12 | */ 13 | public class FileListCellRenderer extends DefaultListCellRenderer { 14 | Border lineBorder = BorderFactory.createLineBorder(new Color(0, 0, 0, 0),1, true); 15 | 16 | @Override 17 | public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { 18 | File file = (File) value; 19 | setBorder(lineBorder); 20 | Icon ico = FileIconCreate.getSmallIcon(file); 21 | setIcon(ico); 22 | if(file.getName().equals("")){ 23 | setText(file.getPath()); // 根路径 24 | }else{ 25 | setText(file.getName()); // 非根路径 26 | } 27 | // 保存原始的颜色 28 | int color = getBackground().getRGB(); 29 | if(isSelected){ 30 | setBackground(new Color(19, 175, 137)); 31 | setForeground(Color.white); 32 | }else{ 33 | setBackground(Color.white); 34 | setForeground(Color.black); 35 | } 36 | return this; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/main/view/FilesTree.java: -------------------------------------------------------------------------------- 1 | package main.view; 2 | 3 | 4 | import javax.swing.*; 5 | import javax.swing.border.EmptyBorder; 6 | import javax.swing.tree.DefaultMutableTreeNode; 7 | import javax.swing.tree.DefaultTreeModel; 8 | import java.awt.*; 9 | import java.io.File; 10 | 11 | public class FilesTree extends JTree { 12 | class DefaultFileNodes extends DefaultMutableTreeNode{ 13 | public DefaultFileNodes(){ 14 | // 内部类 默认结点为系统盘 15 | for(File file : File.listRoots()){ 16 | add(new DefaultMutableTreeNode(file)); 17 | } 18 | } 19 | } 20 | 21 | /** 22 | * 默认结点 23 | */ 24 | public FilesTree(){ 25 | setModel(new DefaultTreeModel(new DefaultFileNodes())); 26 | } 27 | 28 | /** 29 | * 得到当前的文件路径 30 | * @return file 31 | */ 32 | public File getCurrentFile(){ 33 | DefaultMutableTreeNode defaultMutableTreeNode = (DefaultMutableTreeNode) getLastSelectedPathComponent(); 34 | File file = (File) defaultMutableTreeNode.getUserObject(); 35 | return file; 36 | } 37 | 38 | /** 39 | * 更新视图, 加载文件 40 | * @param node 加载的结点 41 | * @param files 要加载在结点下的文件 42 | */ 43 | public void loadingTree(DefaultMutableTreeNode node, File[] files) { 44 | File file = (File) node.getUserObject(); // 得到文件 45 | // 获得用户选择的节点 46 | if (file.isDirectory()) {// 如果File类型对象是文件夹 47 | if(files == null) return; 48 | for (File f : files) {// 将符合条件的File类型对象增加到用户选择的节点中 49 | if(f.isDirectory()){ 50 | node.add(new DefaultMutableTreeNode(f)); 51 | } 52 | } 53 | } else{ 54 | return; 55 | } 56 | } 57 | 58 | /** 59 | * 做测试 60 | * @param args 命令行参数 61 | */ 62 | public static void main(String[] args) { 63 | JFrame frame = new JFrame("File tree"); 64 | frame.setSize(500, 400); 65 | frame.setLocationRelativeTo(null); 66 | JScrollPane jsp = new JScrollPane(new FilesTree()); 67 | jsp.setBorder(new EmptyBorder(0, 0, 0, 0)); 68 | frame.add(jsp, BorderLayout.CENTER); 69 | frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 70 | frame.setVisible(true); 71 | } 72 | 73 | } 74 | -------------------------------------------------------------------------------- /src/res/ui.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ifrozenwhale/file_manager/b586afaf635af00a435acc639250c65718ffe46c/src/res/ui.png --------------------------------------------------------------------------------