You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Rust编译时嵌入目录下所有文件(含二级目录)并实现跨平台无依赖的MP3随机播放功能

如何在Rust编译时嵌入目录下所有文件(含二级目录)并实现跨平台无依赖的MP3随机播放功能

嗨,我来帮你搞定这个需求!你现在的代码只能播放单个嵌入的MP3,要实现多级目录MP3的编译时嵌入+随机播放,咱们可以分三步走:用build.rs批量嵌入文件、构建文件名到内容的映射、实现随机循环播放逻辑。下面是具体步骤和代码:

第一步:添加必要依赖

先在Cargo.toml里补充需要的库:

[package]
name = "mp3_player"
version = "0.1.0"
edition = "2021"

[dependencies]
rodio = "0.17"
rand = "0.8"
walkdir = "2"  # 用于build.rs里遍历目录

[build-dependencies]
walkdir = "2"

第二步:编写build.rs实现批量文件嵌入

在项目根目录创建build.rs脚本,它会在编译前遍历你的MP3目录(比如./music,你可以改成自己的目录路径),自动生成包含所有文件嵌入代码的embedded_mp3s.rs文件:

use std::fs::File;
use std::io::Write;
use std::path::{Path, PathBuf};
use walkdir::WalkDir;

fn main() {
    // 要遍历的MP3目录,改成你自己的路径
    let music_dir = "./music";
    // 生成的代码文件路径
    let out_file = Path::new(&std::env::var("OUT_DIR").unwrap()).join("embedded_mp3s.rs");
    let mut file = File::create(out_file).unwrap();

    // 写入HashMap的定义和初始化代码
    writeln!(file, "use std::collections::HashMap;").unwrap();
    writeln!(file, "pub fn get_embedded_mp3s() -> HashMap<String, &'static [u8]> {{").unwrap();
    writeln!(file, "    let mut map = HashMap::new();").unwrap();

    // 遍历目录下所有.mp3文件(包含二级目录)
    for entry in WalkDir::new(music_dir)
        .into_iter()
        .filter_map(|e| e.ok())
        .filter(|e| e.file_type().is_file())
        .filter(|e| {
            e.path()
                .extension()
                .map(|ext| ext.to_ascii_lowercase() == "mp3")
                .unwrap_or(false)
        })
    {
        let file_path = entry.path();
        // 获取相对路径作为HashMap的键(避免文件名重复的问题)
        let relative_path = file_path
            .strip_prefix(music_dir)
            .unwrap()
            .to_str()
            .unwrap();
        // 把文件路径转成适合include_bytes的格式(处理Windows路径分隔符)
        let include_path = file_path.to_str().unwrap().replace("\\", "/");

        // 写入HashMap的插入语句
        writeln!(
            file,
            "    map.insert(\"{}\".to_string(), include_bytes!(\"{}\"));",
            relative_path, include_path
        )
        .unwrap();
    }

    writeln!(file, "    map").unwrap();
    writeln!(file, "}}").unwrap();

    // 告诉Cargo目录变化时重新编译
    println!("cargo:rerun-if-changed={}", music_dir);
}

这个脚本会遍历指定目录下所有MP3文件,生成一个get_embedded_mp3s函数,返回一个HashMap<String, &'static [u8]>,键是文件的相对路径(比如subdir/song1.mp3),值是嵌入的文件内容。

第三步:编写主程序实现随机播放

修改你的src/main.rs,引入生成的代码,然后实现随机循环播放:

use rand::seq::IteratorRandom;
use rodio::{Decoder, OutputStream, source::Source};
use std::collections::HashMap;
use std::io::Cursor;

// 引入build.rs生成的代码
include!(concat!(env!("OUT_DIR"), "/embedded_mp3s.rs"));

fn main() -> Result<(), Box<dyn std::error::Error>> {
    // 获取所有嵌入的MP3文件
    let mp3_map: HashMap<String, &[u8]> = get_embedded_mp3s();
    if mp3_map.is_empty() {
        eprintln!("Error: No MP3 files found in the directory!");
        return Ok(());
    }

    // 初始化音频输出流
    let (_stream, stream_handle) = OutputStream::try_default()?;
    let mut rng = rand::thread_rng();

    println!("Playing random MP3s... Press Ctrl+C to stop.");

    loop {
        // 随机选择一个MP3文件
        let (file_name, file_data) = mp3_map.iter().choose(&mut rng).unwrap();

        println!("Now playing: {}", file_name);

        // 解码MP3并播放
        match Decoder::new_mp3(Cursor::new(file_data)) {
            Ok(mut source) => {
                // 播放音频,等待播放完成(不用固定sleep)
                stream_handle.play_raw(source.convert_samples())?;
                while source.next().is_some() {
                    std::thread::sleep(std::time::Duration::from_millis(100));
                }
            }
            Err(e) => {
                eprintln!("Failed to decode {}: {}", file_name, e);
            }
        }
    }
}

关键说明:

  • 编译时嵌入:通过build.rs批量处理多级目录,避免手动写无数个include_bytes,而且目录内容变化时Cargo会自动重新编译。
  • 跨平台支持:rodio本身是无依赖的音频库,walkdir支持跨平台目录遍历,整个程序不需要安装外部播放器。
  • 随机播放逻辑:用rand库的choose方法随机选文件,播放时等待音频源结束(而不是固定休眠时间),确保一首歌放完再换下一首。
  • 文件名处理:用相对路径作为HashMap的键,避免二级目录里文件名重复导致覆盖的问题。

你只需要把你的MP3文件放到指定的目录(比如./music),然后运行cargo run就能看到效果啦!

备注:内容来源于stack exchange,提问作者Laurent FAVOLE

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.15 10:03:01