1. 程式人生 > >Java實現檔案切割拼接

Java實現檔案切割拼接

Java實現檔案分割和拼接

單執行緒實現

檔案分割

在老的FAT32檔案系統中,最大的單個檔案大小必須儲存在4G內,對於經常看電影的我這個是不能允許的。不過現在Windows有NTFS檔案系統,Linux大部分發行版為Ext4檔案系統,最大單個檔案大小能大於4G。不過這二者並不能相容。。格式化NTFS的U盤Linux不能識別,格式化Ext4的U盤Windows不能識別,只能用老的FAT32相容二者。所以將檔案分割,再進行拼接就很重要,檔案經過分割了在網路上傳輸就十分方便,也能開多執行緒對每部分進行HASH提高處理效率。

最近看的BradPitt的《狂怒》

首先:對檔案進行分割需要確定每一部分的大小,假設上面的Fury.mkv

檔案大小為280M,分割每一塊設定預設大小為64M,所以:

對於最後一塊,一般小於等於設定好的每塊預設大小。 每塊大小設定好了,接下來,就需要將檔案的路徑獲取,程式碼中搭建輸入流,將檔案讀入記憶體緩衝區中,再搭建輸出流,將緩衝區輸出到新的分割檔案中。 再接下來實現就很簡單了。 新建一個 FileSlice類:有切割方法,拼接方法。

public class FileSlice {
    /**
     * 分割檔案
     * @param filePath 檔案路徑
     * @param filePieceSize 檔案每塊大小,單位為位元組,為-1則預設為每塊64M
     * @return 成功返回True,出錯則返回False
     */
    public static boolean slice(Path filePath, int filePieceSize){
        return true;
    }

    /**
     * 將分割好的檔案重新連結
     * @param filePath 被分割好的其中之一檔案路徑,預設其他塊與其在同一目錄下
     * @param howManyParts 一共有多少塊
     * @return 成功返回True,出錯則返回False
     */
    public static boolean glue(Path filePath, int howManyParts){
        return true;
    }
}
複製程式碼

接下來實現單執行緒的分割方法: 用圖解的話應該是這樣:

程式碼實現: 進入函式首先判斷檔案是否存在:

if (!Files.exists(filePath)){
    return false;
}
複製程式碼

接下來判斷每塊大小是否使用預設值:

if(filePieceSize == -1){
    filePieceSize = 1024*1024*64;
}
複製程式碼

將路徑轉換為檔案物件,再計算將分割多少塊:

File file = filePath.toFile();
int howManyParts = (int) Math.ceil(file.length() / (double)filePieceSize);
複製程式碼

初始化輸入輸出流,出錯輸出錯誤資訊,返回false,獲得當前目錄:

DataInputStream fileReader = null;
try {
    fileReader = new DataInputStream(new FileInputStream(file));
} catch (FileNotFoundException e) {
    e.printStackTrace();
    System.out.println("檔案找不到!");
    return false;
}
DataOutputStream fileWriter;
Path dir = filePath.getParent();
複製程式碼

接下來讀取檔案,並且分別輸出到各個part檔案中:

int readLength = -1;
long total = 0;

try {
    for (int i = 1; i <= howManyParts ; i++){
        //新建檔案part i
        Path temp = Files.createFile(dir.resolve(filePath.getFileName() + ".part" + i));
        //搭建輸出流
        fileWriter = new DataOutputStream(new FileOutputStream(temp.toFile()));
        //讀取檔案並輸出
        while ( (readLength = fileReader.read(buffer)) != -1){
            fileWriter.write(buffer,0,readLength);
            fileWriter.flush();
            total += readLength;
            if (total == filePieceSize){
                total = 0;
                break;
            }
        }
        //part i的檔案已經輸出完畢,關閉流
        fileWriter.close();
    }
    //讀取完畢,關閉輸入流
    fileReader.close();
} catch (IOException e) {
    e.printStackTrace();
    System.out.println("IO錯誤!");
    return false;
}
複製程式碼

該函式已經實現完畢,接下來測試(由於電影Fury有14G。。太大了。。還是換個吧):

我是大哥大第5集,有729M,大概能分個12個part吧。

public static void main(String[] args) throws IOException {
    double before = System.currentTimeMillis();

    Path bigboss = Paths.get("D:\\Video\\我是大哥大\\我是大哥大.Kyou.kara.Ore.wa.Ep05.Chi_Jap.HDTVrip.1280X720.mp4");

    FileSlice.slice(bigboss,-1);

    double after = System.currentTimeMillis();

    System.out.println("分割檔案我是大哥大.Kyou.kara.Ore.wa.Ep05.Chi_Jap.HDTVrip.1280X720.mp4," + Files.size(bigboss) + "位元組,總用時" + (after - before) + "ms" );

}
複製程式碼

執行結果:

分割檔案我是大哥大.Kyou.kara.Ore.wa.Ep05.Chi_Jap.HDTVrip.1280X720.mp4,765321889位元組,總用時16335.0ms
複製程式碼

速度還是挺慢的。。 下次還是換成多執行緒來實現,再來測試下速度。在單執行緒情況下一個普通的40分鐘日劇都要15-30s左右,要是mkv格式的電影都要好久了。。不過其實極限應該不在CPU中執行的速度,而是在硬碟IO中,如果是普通硬碟那麼就算是多執行緒也應該提速不了多少。。

檔案拼接

這個就很簡單了,和分割相反就OK。 直接上完整程式碼:

public static boolean glue(Path filePath, int howManyParts){
    if (!Files.exists(filePath)){
        return false;
    }
    //獲取原始檔名
    String filename = getOriginalFileName(filePath.getFileName().toString());

    if (filename == null){
        System.out.println("傳入part檔名解析出錯!");
        return false;
    }
    //初始化緩衝區
    byte [] buffer = new byte[1024 * 8];
    //獲取檔案儲存的路徑
    Path dir = filePath.getParent();

    try {
        DataInputStream fileReader  = null;
        //建立原始檔案
        Files.createFile(dir.resolve(filename));
        //搭建原始檔案輸出流
        DataOutputStream fileWriter = new DataOutputStream(new FileOutputStream(dir.resolve(filename).toFile()));

        int readLength = -1;
        for (int i = 1; i <= howManyParts ; i++){
            //得到part i檔案路徑
            Path temp = dir.resolve(filename + ".part" + i);
            //搭建輸入流
            fileReader = new DataInputStream(new FileInputStream(temp.toFile()));
            //讀取檔案並輸出
            while ( (readLength = fileReader.read(buffer)) != -1){
                fileWriter.write(buffer,0,readLength);
                fileWriter.flush();
            }
            //part i的檔案已經讀入完畢,關閉流
            fileReader.close();
        }
        //寫入完畢,關閉輸出流
        fileWriter.close();
    } catch (IOException e) {
        e.printStackTrace();
        System.out.println("IO錯誤!");
        return false;
    }
    return true;
}
複製程式碼

再測試剛剛分割好的我是大哥大第5集

public static void main(String[] args) throws IOException {
    double before = System.currentTimeMillis();

    Path bigboss = Paths.get("D:\\Video\\我是大哥大\\我是大哥大.Kyou.kara.Ore.wa.Ep05.Chi_Jap.HDTVrip.1280X720.mp4.part1");

    FileSlice.glue(bigboss,12);

    double after = System.currentTimeMillis();

    System.out.println("拼接12個part,用時" + (after - before) + "ms");

}
複製程式碼

結果輸出,用12s左右,還行。

拼接12個part,用時12147.0ms
複製程式碼

開啟播放毫無問題,最後截張圖。

未完待續。。下次來使用多執行緒進行實現。