[NIO.2] 第十八篇 用户自定义属性视图

本文介绍如何使用NIO.2中的UserDefinedFileAttributeView接口来自定义文件属性,包括检查文件系统支持情况、定义属性、获取属性值、删除属性等操作。
如果你发现 NIO.2 内置的属性视图不能满足你的要求,或者你想给文件设置某些特殊的属性,那么你可以使用自定义属性视图。NIO.2 提供了 UserDefinedFileAttributeView 接口来支持这个功能。使用这个视图,你可以添加任何你认为有用的文件属性。

[b][size=x-large]检查是否支持自定义文件属性[/size][/b]

在你要创建自己的文件属性之前,要先检验文件系统是否支持这个功能。可以调用 FileStore.supportsFileAttributeView() 方法来进行检验,这个方法接受一个 String 类型的参数,可以传入视图名称进行检验,也接受 Class 类型的参数,可传入视图的 Class 进行检验。下面看看样例:

import java.io.IOException; 
import java.nio.file.FileStore;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.attribute.UserDefinedFileAttributeView;

Path path = Paths.get("C:/rafaelnadal/tournaments/2009", "BNP.txt");

try {
FileStore store = Files.getFileStore(path);
if (!store.supportsFileAttributeView(UserDefinedFileAttributeView.class)) {
System.out.println("The user defined attributes are not supported on: " + store);
} else {
System.out.println("The user defined attributes are supported on: " + store);
}
} catch (IOException e) {
System.err.println(e);
}


当然,你也可以通过 FileSystem 来获取 FileStore,这样可以检验所有的 FileStore 是否支持某个属性视图。

[b][size=x-large]自定义属性的操作[/size][/b]

如果你的文件系统支持自定义属性。那么就可以进行添加属性、查询属性、删除属性、更新属性的操作。

[b][size=large]定义属性[/size][/b]

首先我们定义一个名为 file.description 值为 This file contains
private information! 的属性。当你调用 Files.getFileAttributeView() 方法得到文件视图后,可以通过下面的方法写出属性:

import java.io.IOException; 
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.attribute.UserDefinedFileAttributeView;

Path path = Paths.get("C:/rafaelnadal/tournaments/2009", "BNP.txt");
UserDefinedFileAttributeView udfav = Files.getFileAttributeView(path,
UserDefinedFileAttributeView.class);
try {
int written = udfav.write("file.description", Charset.defaultCharset().
encode("This file contains private information!"));
} catch (IOException e) {
System.err.println(e);
}


可以看到 write() 方法接受两个参数,第一个参数表示属性名,第二个参数是 ByteBuffer 类型,表示属性值。如果属性名已经存在,则会被替换。这个方法还返回一个 int 类型的返回值,表示写出的字节数,可以为 0。

也可以通过 Files.setAttribute() 来设置属性,属性值可用 ByteBuffer 类型或 byte 数组类型(byte[])。

[b][size=large]获取属性名称和值的大小[/size][/b]

在任何时候,你都可以调用 UserDefinedFileAttributeView.list() 方法来列出自定义属性的名称。这个方法返回 List 类型的返回值,List 中用 String 类型存储自定义的属性名。将属性名传入 UserDefinedFileAttributeView.size() 方法可得到属性值的大小,下面看例子:

import java.io.IOException; 
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.attribute.UserDefinedFileAttributeView;

Path path = Paths.get("C:/rafaelnadal/tournaments/2009", "BNP.txt");
UserDefinedFileAttributeView udfav = Files.getFileAttributeView(path,
UserDefinedFileAttributeView.class);

try {
for (String name : udfav.list()) {
System.out.println(udfav.size(name) + " " + name);
}
} catch (IOException e) {
System.err.println(e);
}


[size=large]获取自定义属性值[/size]

可以通过 UserDefinedFileAttributeView.read() 方法来读取自定义属性值,这个方法接受两个参数,类型分别为 String 和 ByteBuffer,Sting 参数传入属性名,ByteBuffer 参数用来存放属性值。下面看看例子:

import java.io.IOException; 
import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.attribute.UserDefinedFileAttributeView;

Path path = Paths.get("C:/rafaelnadal/tournaments/2009", "BNP.txt");
UserDefinedFileAttributeView udfav = Files.getFileAttributeView(path,
UserDefinedFileAttributeView.class);

try {
int size = udfav.size("file.description");
ByteBuffer bb = ByteBuffer.allocateDirect(size);
udfav.read("file.description", bb);
bb.flip();
System.out.println(Charset.defaultCharset().decode(bb).toString());
} catch (IOException e) {
System.err.println(e);
}


从上面的例子可以看到,使用 UserDefinedFileAttributeView.size() 方法,可以很容易地设置存放属性值的 ByteBuffer 的长度。

另外,也可以通过 Files.getAttribute() 来获取属性值,这会返回 byte 类型的数组(byte[])。

[b][size=large]删除自定义属性[/size][/b]

当用户自定义的属性不再有用的时候,可以调用 UserDefinedFileAttributeView.delete() 方法来删除属性值。只需传入属性名称即可,下面看看例子:

import java.io.IOException; 
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.attribute.UserDefinedFileAttributeView;

Path path = Paths.get("C:/rafaelnadal/tournaments/2009", "BNP.txt");
UserDefinedFileAttributeView udfav = Files.getFileAttributeView(path,
UserDefinedFileAttributeView.class);
try {
udfav.delete("file.description");
} catch (IOException e) {
System.err.println(e);
}


文章来源:[url]http://www.aptusource.org/2014/03/nio-2-user-defined-file-attribute-view/[/url]
package play; import javafx.application.Application; import javafx.collections.FXCollections; import javafx.collections.ObservableList; import javafx.scene.Scene; import javafx.scene.control.*; import javafx.scene.layout.BorderPane; import javafx.scene.layout.HBox; import javafx.scene.layout.VBox; import javafx.scene.media.Media; import javafx.scene.media.MediaPlayer; import javafx.stage.FileChooser; import javafx.stage.Stage; import java.io.File; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.util.ArrayList; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; public class MusicPlayerApp extends Application { private MediaPlayer mediaPlayer; private Slider progressSlider; private ListView<String> playlistView; private TextArea lyricsArea; private ObservableList<String> playlist; private List<File> musicFiles; private List<File> lrcFiles; // 新增:用于存储歌词内容及对应的时间戳,结构为:时间戳(毫秒) -> 歌词文本 private List<LyricEntry> lyricEntries = new ArrayList<>(); // 辅助类:封装歌词的时间戳和文本内容 private static class LyricEntry { long timeMillis; String lyric; public LyricEntry(long timeMillis, String lyric) { this.timeMillis = timeMillis; this.lyric = lyric; } } @Override public void start(Stage primaryStage) { // 提前导入音乐文件(项目文件夹中的music_files) musicFiles = new ArrayList<>(); lrcFiles = new ArrayList<>(); File initDir = new File("src/music_files"); loadInitialFiles(initDir); // 初始化播放列表数据 playlist = FXCollections.observableArrayList(); for (File file : musicFiles) { playlist.add(file.getName()); } // 设置播放列表视图 playlistView = new ListView<>(playlist); playlistView.setPrefHeight(150); // 双击列表项也可触发播放 playlistView.setOnMouseClicked(event -> { if (event.getClickCount() == 2) { playMusic(); } }); // 创建控制按钮 Button playButton = new Button("播放"); Button pauseButton = new Button("暂停"); Button stopButton = new Button("停止"); Button addButton = new Button("导入文件"); // 进度条设置 progressSlider = new Slider(0, 100, 0); progressSlider.setPrefWidth(400); // 拖动进度条可跳转播放位置 progressSlider.setOnMouseReleased(event -> { if (mediaPlayer != null && mediaPlayer.getMedia() != null) { double progress = progressSlider.getValue() / 100; mediaPlayer.seek(mediaPlayer.getMedia().getDuration().multiply(progress)); } }); // 歌词显示区 lyricsArea = new TextArea("歌词将在此显示..."); lyricsArea.setEditable(false); lyricsArea.setPrefHeight(100); // 布局安排 VBox centerBox = new VBox(10, playlistView, lyricsArea, progressSlider); HBox controlBox = new HBox(10, playButton, pauseButton, stopButton, addButton); BorderPane root = new BorderPane(); root.setTop(controlBox); root.setCenter(centerBox); Scene scene = new Scene(root, 600, 400); primaryStage.setTitle("Java音乐播放器"); primaryStage.setScene(scene); primaryStage.show(); // 按钮事件绑定 playButton.setOnAction(e -> playMusic()); pauseButton.setOnAction(e -> { if (mediaPlayer != null) { mediaPlayer.pause(); } }); stopButton.setOnAction(e -> { if (mediaPlayer != null) { mediaPlayer.stop(); // 停止后重置进度条 progressSlider.setValue(0); } }); addButton.setOnAction(e -> importFiles(primaryStage)); playlistView.getSelectionModel().selectedItemProperty().addListener((obs, oldVal, newVal) -> playMusic()); } // 抽取加载初始文件的逻辑为单独方法 private void loadInitialFiles(File initDir) { if (initDir.exists() && initDir.isDirectory()) { File[] files = initDir.listFiles(); if (files != null) { for (File file : files) { String fileName = file.getName().toLowerCase(); if (fileName.endsWith(".mp3") || fileName.endsWith(".wav") || fileName.endsWith(".ogg")) { musicFiles.add(file); } else if (fileName.endsWith(".lrc")) { lrcFiles.add(file); } } } } } private void importFiles(Stage stage) { FileChooser fileChooser = new FileChooser(); // 文件选择器导入 fileChooser.setTitle("选择音乐文件"); fileChooser.getExtensionFilters().addAll( new FileChooser.ExtensionFilter("音频文件", "*.mp3", "*.wav", "*.ogg"), new FileChooser.ExtensionFilter("歌词文件", "*.lrc") ); List<File> selectedFiles = fileChooser.showOpenMultipleDialog(stage); if (selectedFiles != null) { for (File file : selectedFiles) { String fileName = file.getName().toLowerCase(); if (fileName.endsWith(".lrc")) { lrcFiles.add(file); } else { musicFiles.add(file); playlist.add(file.getName()); } } } } private void playMusic() { int selectedIdx = playlistView.getSelectionModel().getSelectedIndex(); if (selectedIdx >= 0 && selectedIdx < musicFiles.size()) { if (mediaPlayer != null) { mediaPlayer.dispose(); } Media media = new Media(musicFiles.get(selectedIdx).toURI().toString()); mediaPlayer = new MediaPlayer(media); mediaPlayer.play(); // 绑定进度条更新 mediaPlayer.currentTimeProperty().addListener((obs, oldTime, newTime) -> { if (mediaPlayer.getMedia() != null) { double progress = (newTime.toSeconds() / media.getDuration().toSeconds()) * 100; progressSlider.setValue(progress); // 播放时同步更新歌词 updateLyrics(newTime.toMillis()); } }); // 歌词同步处理 syncLyrics(selectedIdx); } } private void syncLyrics(int songIndex) { String currentSongName = musicFiles.get(songIndex).getName().replaceFirst("[.][^.]+$", ""); lyricEntries.clear(); // 清空之前的歌词数据 for (File lrcFile : lrcFiles) { if (lrcFile.getName().contains(currentSongName)) { try { // 原生Java实现文件读取 byte[] bytes = Files.readAllBytes(lrcFile.toPath()); String lrcContent = new String(bytes, StandardCharsets.UTF_8); // 解析歌词内容 parseLyrics(lrcContent); } catch (IOException e) { lyricsArea.setText("歌词加载失败: " + e.getMessage()); } break; } } } // 解析歌词内容,提取时间戳和歌词文本 private void parseLyrics(String lrcContent) { lyricEntries.clear(); String regex = "\\[(\\d{2}):(\\d{2})\\.(\\d{2,3})\\](.*)"; Pattern pattern = Pattern.compile(regex); Matcher matcher = pattern.matcher(lrcContent); while (matcher.find()) { int minute = Integer.parseInt(matcher.group(1)); int second = Integer.parseInt(matcher.group(2)); int millisecond = Integer.parseInt(matcher.group(3)); // 计算总毫秒数 long totalMillis = minute * 60 * 1000 + second * 1000 + millisecond; String lyric = matcher.group(4).trim(); lyricEntries.add(new LyricEntry(totalMillis, lyric)); } // 按时间戳排序,确保顺序正确 lyricEntries.sort((a, b) -> Long.compare(a.timeMillis, b.timeMillis)); } // 根据当前播放时间更新歌词显示 private void updateLyrics(double d) { for (int i = 0; i < lyricEntries.size(); i++) { LyricEntry entry = lyricEntries.get(i); if (entry.timeMillis <= d) { if (i == lyricEntries.size() - 1 || lyricEntries.get(i + 1).timeMillis > d) { lyricsArea.setText(entry.lyric); break; } } } } @Override public void stop() { if (mediaPlayer != null) { mediaPlayer.dispose(); } } public static void main(String[] args) { launch(args); } }这是一个简单的音乐播放器代码,我想要求他能选择几种格式的歌曲播放,显示歌词和进度条,支持播放列表;并且不仅能在程序运行之后弹出串口选择添加进去音乐,也可以选择在代码还没运行的时候加入一部分音乐,后期代码运行完之后可以直接选择某个歌曲播放,请帮我把代码修正完善
最新发布
06-14
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值