Java實現(xiàn)文件切割拼接的實現(xiàn)代碼
單線程實現(xiàn)
文件分割
在老的FAT32文件系統(tǒng)中,最大的單個文件大小必須保存在4G內(nèi),對于經(jīng)??措娪暗奈疫@個是不能允許的。不過現(xiàn)在Windows有NTFS文件系統(tǒng),Linux大部分發(fā)行版為Ext4文件系統(tǒng),最大單個文件大小能大于4G。不過這二者并不能兼容。。格式化NTFS的U盤Linux不能識別,格式化Ext4的U盤Windows不能識別,只能用老的FAT32兼容二者。所以將文件分割,再進行拼接就很重要,文件經(jīng)過分割了在網(wǎng)絡上傳輸就十分方便,也能開多線程對每部分進行HASH提高處理效率。
最近看的BradPitt的《狂怒》
首先:對文件進行分割需要確定每一部分的大小,假設上面的 Fury.mkv 文件大小為 280M ,分割每一塊設置默認大小為 64M ,所以:
對于最后一塊,一般小于等于設定好的每塊默認大小。 每塊大小設置好了,接下來,就需要將文件的路徑獲取,代碼中搭建輸入流,將文件讀入內(nèi)存緩沖區(qū)中,再搭建輸出流,將緩沖區(qū)輸出到新的分割文件中。 再接下來實現(xiàn)就很簡單了。 新建一個 FileSlice
類:有切割方法,拼接方法。
public class FileSlice { /** * 分割文件 * @param filePath 文件路徑 * @param filePieceSize 文件每塊大小,單位為字節(jié),為-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; } }
接下來實現(xiàn)單線程的分割方法: 用圖解的話應該是這樣:
代碼實現(xiàn): 進入函數(shù)首先判斷文件是否存在:
if (!Files.exists(filePath)){ return false; }
接下來判斷每塊大小是否使用默認值:
if(filePieceSize == -1){ filePieceSize = 1024*1024*64; }
將路徑轉(zhuǎn)換為文件對象,再計算將分割多少塊:
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的文件已經(jīng)輸出完畢,關閉流 fileWriter.close(); } //讀取完畢,關閉輸入流 fileReader.close(); } catch (IOException e) { e.printStackTrace(); System.out.println("IO錯誤!"); return false; }
該函數(shù)已經(jīng)實現(xiàn)完畢,接下來測試(由于電影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) + "字節(jié),總用時" + (after - before) + "ms" ); }
運行結(jié)果:
分割文件我是大哥大.Kyou.kara.Ore.wa.Ep05.Chi_Jap.HDTVrip.1280X720.mp4,765321889字節(jié),總用時16335.0ms
速度還是挺慢的。。 下次還是換成多線程來實現(xiàn),再來測試下速度。在單線程情況下一個普通的40分鐘日劇都要15-30s左右,要是mkv格式的電影都要好久了。。不過其實極限應該不在CPU中執(zhí)行的速度,而是在硬盤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; } //初始化緩沖區(qū) byte [] buffer = new byte[1024 * 8]; //獲取文件存儲的路徑 Path dir = filePath.getParent(); try { DataInputStream fileReader = null; //創(chuàng)建原始文件 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的文件已經(jīng)讀入完畢,關閉流 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"); }
結(jié)果輸出,用12s左右,還行。
拼接12個part,用時12147.0ms
打開播放毫無問題,最后截張圖。
未完待續(xù)。。下次來使用多線程進行實現(xiàn)。
以上就是本文的全部內(nèi)容,希望對大家的學習有所幫助,也希望大家多多支持腳本之家。
相關文章
Spring Cache整合Redis實現(xiàn)方法詳解
這篇文章主要介紹了Spring Cache整合Redis實現(xiàn)方法詳解,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下2020-08-08springboot使用war包部署到外部tomcat過程解析
這篇文章主要介紹了springboot使用war包部署到外部tomcat過程解析,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下2020-01-01APT?注解處理器實現(xiàn)?Lombok?常用注解功能詳解
這篇文章主要為大家介紹了使用APT?注解處理器實現(xiàn)?Lombok?常用注解功能詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-09-09在已經(jīng)使用mybatis的項目里引入mybatis-plus,結(jié)果不能共存的解決
這篇文章主要介紹了在已經(jīng)使用mybatis的項目里引入mybatis-plus,結(jié)果不能共存的解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-03-03SpringBoot項目docker容器部署實現(xiàn)
本文主要介紹了SpringBoot項目docker容器部署實現(xiàn),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2022-03-03ExecutorService實現(xiàn)獲取線程返回值
這篇文章主要介紹了ExecutorService實現(xiàn)獲取線程返回值,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2022-08-08