如何通过JList在JFileChooser中同时保存多个MIDI文件?
实现批量保存选中的MIDI文件
首先,咱们先拆解你现有代码的局限:当前saveMidiFileAction里只通过playlistModel.getSelectedSequenceModel()获取了单个选中的序列,这就是只能存一个文件的核心原因。接下来我基于你提到的思路,给出两种可行的实现方案。
方案一:逐个保存选中的MIDI文件(重复执行保存操作)
这个思路是获取所有选中的序列模型,然后逐个处理保存逻辑。为了提升用户体验,我们可以让用户先选择一个目标目录,再将所有选中的MIDI文件按原文件名保存到该目录下。
修改后的代码如下:
private class MidiFileChooser extends JFileChooser { { setFileFilter(new FileNameExtensionFilter("MIDI sequence (*.mid)", "mid")); setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); // 先让用户选择保存目录 } /** * 批量保存MIDI文件操作 */ public Action saveMidiFileAction = getModel().new SelectedSequenceAction( "保存选中项", "将所有选中的MIDI序列保存到指定目录" ) { @Override public void actionPerformed(ActionEvent event) { PlaylistTableModel playlistModel = getModel(); // 获取所有选中的序列模型,需要你的PlaylistTableModel提供多选获取方法 List<SequenceTrackListTableModel> selectedSequences = playlistModel.getSelectedSequenceModels(); if (selectedSequences.isEmpty()) { showMessage("未选中任何MIDI序列!"); return; } // 让用户选择保存目录 if (showSaveDialog((Component)event.getSource()) != JFileChooser.APPROVE_OPTION ) { return; } File targetDir = getSelectedFile(); // 逐个保存每个选中的文件 for (SequenceTrackListTableModel sequenceModel : selectedSequences) { String fn = sequenceModel.getFilename(); if (fn == null || fn.isEmpty()) { continue; // 跳过无文件名的序列 } File saveFile = new File(targetDir, fn); // 处理文件已存在的确认 if (saveFile.exists()) { if (!confirm("是否覆盖文件 " + saveFile.getName() + " ?")) { continue; // 用户取消则跳过该文件,继续保存下一个 } } // 写入MIDI数据到文件 try (FileOutputStream out = new FileOutputStream(saveFile)) { out.write(sequenceModel.getMIDIdata()); sequenceModel.setModified(false); playlistModel.fireSequenceModified(sequenceModel, false); } catch (IOException ex) { ex.printStackTrace(); showError("保存 " + fn + " 失败:" + ex.getMessage()); } } showMessage("选中的MIDI文件已成功保存!"); } }; }
注意:你需要给PlaylistTableModel添加getSelectedSequenceModels()方法,用来返回所有选中的序列模型列表,示例实现如下:
public List<SequenceTrackListTableModel> getSelectedSequenceModels() { List<SequenceTrackListTableModel> selected = new ArrayList<>(); // 假设你用JTable的选中行索引来获取对应模型 int[] selectedRows = playlistTable.getSelectedRows(); for (int row : selectedRows) { selected.add(getSequenceModelAt(row)); } return selected; }
方案二:打包成Zip文件保存
如果希望用户一次性将所有选中文件保存为一个压缩包,这个方案更便捷,也能避免用户重复操作。
修改后的代码示例:
private class MidiFileChooser extends JFileChooser { { setFileFilter(new FileNameExtensionFilter("ZIP压缩包 (*.zip)", "zip")); } /** * 批量保存为Zip压缩包操作 */ public Action saveMidiZipAction = getModel().new SelectedSequenceAction( "保存为Zip", "将所有选中的MIDI序列打包保存为Zip压缩包" ) { @Override public void actionPerformed(ActionEvent event) { PlaylistTableModel playlistModel = getModel(); List<SequenceTrackListTableModel> selectedSequences = playlistModel.getSelectedSequenceModels(); if (selectedSequences.isEmpty()) { showMessage("未选中任何MIDI序列!"); return; } if (showSaveDialog((Component)event.getSource()) != JFileChooser.APPROVE_OPTION ) { return; } File zipFile = getSelectedFile(); // 确保文件名以.zip结尾 if (!zipFile.getName().toLowerCase().endsWith(".zip")) { zipFile = new File(zipFile.getParent(), zipFile.getName() + ".zip"); } // 处理压缩包已存在的确认 if (zipFile.exists()) { if (!confirm("是否覆盖压缩包 " + zipFile.getName() + " ?")) { return; } } // 创建Zip输出流并写入数据 try (ZipOutputStream zipOut = new ZipOutputStream(new FileOutputStream(zipFile))) { for (SequenceTrackListTableModel sequenceModel : selectedSequences) { String fn = sequenceModel.getFilename(); if (fn == null || fn.isEmpty()) { continue; } // 添加Zip条目 ZipEntry entry = new ZipEntry(fn); zipOut.putNextEntry(entry); // 写入MIDI数据 zipOut.write(sequenceModel.getMIDIdata()); zipOut.closeEntry(); // 更新模型状态 sequenceModel.setModified(false); playlistModel.fireSequenceModified(sequenceModel, false); } showMessage("选中的MIDI文件已成功打包为Zip!"); } catch (IOException ex) { ex.printStackTrace(); showError("创建Zip压缩包失败:" + ex.getMessage()); } } }; }
额外建议
如果你想同时保留“单个保存”和“批量保存”功能,可以添加两个独立的Action,分别绑定到不同的按钮或菜单选项上,让用户自主选择保存方式。
内容的提问来源于stack exchange,提问作者Dan
相关产品推荐
相关产品推荐

