首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Java 合併 MP3 文件實戰:遞歸掃描 + 字節流拼接完整方案

     編輯:Java 相關知識

需求場景

將目錄及子目錄下所有 .mp3 文件按文件名排序,拼接為單一 MP3 文件,適用於有聲書章節合併、會議錄音整理等場景。

核心思路

  1. 遞歸遍歷目錄,收集 .mp3 絕對路徑
  2. 按自然排序(或自定義 Comparator)排序
  3. 以 追加模式 打開輸出流,順序讀取每個源文件字節流寫入

關鍵點:MP3 由幀組成,直接字節拼接在多數播放器中可正常播放,但元數據(ID3 標籤)會殘留在文件頭/尾,建議合併後用工具重寫標籤。

完整可運行代碼


package com.example.mp3;

import java.io.*;
import java.nio.file.*;
import java.util.*;

public class Mp3Merger {

    public static void main(String[] args) throws IOException {
        if (args.length < 2) {
            System.err.println("用法: java Mp3Merger <輸入目錄> <輸出文件.mp3>");
            return;
        }
        Path inputDir = Paths.get(args[0]);
        Path outputFile = Paths.get(args[1]);

        List mp3Files = collectMp3Files(inputDir);
        if (mp3Files.isEmpty()) {
            System.out.println("未發現 .mp3 文件");
            return;
        }

        // 自然排序:1.mp3, 2.mp3, 10.mp3 (而非 1, 10, 2)
        mp3Files.sort(Comparator.comparing(Path::getFileName, 
            Comparator.comparing(String::toString, 
                (a, b) -> naturalCompare(a, b))));

        mergeFiles(mp3Files, outputFile);
        System.out.printf("合併完成:%d 個文件 → %s%n", mp3Files.size(), outputFile);
    }

    /** 遞歸收集 .mp3 文件 */
    private static List collectMp3Files(Path dir) throws IOException {
        List result = new ArrayList<>();
        Files.walkFileTree(dir, new SimpleFileVisitor() {
            @Override
            public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {
                if (file.toString().toLowerCase().endsWith(".mp3")) {
                    result.add(file.toAbsolutePath());
                }
                return FileVisitResult.CONTINUE;
            }
        });
        return result;
    }

    /** 自然排序比較器(簡易版) */
    private static int naturalCompare(String a, String b) {
        int i = 0, j = 0;
        while (i < a.length() && j < b.length()) {
            char ca = a.charAt(i), cb = b.charAt(j);
            if (Character.isDigit(ca) && Character.isDigit(cb)) {
                int numA = 0, numB = 0;
                while (i < a.length() && Character.isDigit(a.charAt(i))) numA = numA * 10 + (a.charAt(i++) - '0');
                while (j < b.length() && Character.isDigit(b.charAt(j))) numB = numB * 10 + (b.charAt(j++) - '0');
                if (numA != numB) return Integer.compare(numA, numB);
            } else {
                if (ca != cb) return Character.compare(ca, cb);
                i++; j++;
            }
        }
        return Integer.compare(a.length(), b.length());
    }

    /** 字節流拼接(1MB 緩衝) */
    private static void mergeFiles(List files, Path output) throws IOException {
        try (FileOutputStream fos = new FileOutputStream(output.toFile(), true)) {  // 追加模式
            byte[] buffer = new byte[1024 * 1024];
            for (Path file : files) {
                try (FileInputStream fis = new FileInputStream(file.toFile())) {
                    int len;
                    while ((len = fis.read(buffer)) != -1) {
                        fos.write(buffer, 0, len);
                    }
                }
                System.out.println("已合併:" + file.getFileName());
            }
        }
    }
}

進階優化方向

痛點優化方案
ID3 標籤重複/錯亂合併前用 jaudiotagger 清除標籤,合併後重寫統一標籤
大文件內存壓力已使用 1MB 緩衝流式讀寫,常數內存
合併順序不可控文件名前綴數字(001_, 002_)或外部清單文件控制
格式不一致(采樣率/碼率)合併前用 FFmpeg 統一轉碼:ffmpeg -i in.mp3 -ar 44100 -b:a 128k out.mp3

FFmpeg 一鍵合併(生產推薦)


# 1. 生成文件列表 filelist.txt
for f in *.mp3; do echo "file '$f'" >> filelist.txt; done

# 2. 無損合併(極快,不重新編碼)
ffmpeg -f concat -safe 0 -i filelist.txt -c copy merged.mp3

Java 方案適合「嵌入應用、跨平台、無外部依賴」場景;追求極致性能與音質,FFmpeg 才是終極答案。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved