Java實(shí)現(xiàn)文件切割拼接的實(shí)現(xiàn)代碼
單線程實(shí)現(xiàn)
文件分割
在老的FAT32文件系統(tǒng)中,最大的單個(gè)文件大小必須保存在4G內(nèi),對(duì)于經(jīng)??措娪暗奈疫@個(gè)是不能允許的。不過(guò)現(xiàn)在Windows有NTFS文件系統(tǒng),Linux大部分發(fā)行版為Ext4文件系統(tǒng),最大單個(gè)文件大小能大于4G。不過(guò)這二者并不能兼容。。格式化NTFS的U盤Linux不能識(shí)別,格式化Ext4的U盤Windows不能識(shí)別,只能用老的FAT32兼容二者。所以將文件分割,再進(jìn)行拼接就很重要,文件經(jīng)過(guò)分割了在網(wǎng)絡(luò)上傳輸就十分方便,也能開多線程對(duì)每部分進(jìn)行HASH提高處理效率。

最近看的BradPitt的《狂怒》
首先:對(duì)文件進(jìn)行分割需要確定每一部分的大小,假設(shè)上面的 Fury.mkv 文件大小為 280M ,分割每一塊設(shè)置默認(rèn)大小為 64M ,所以:

對(duì)于最后一塊,一般小于等于設(shè)定好的每塊默認(rèn)大小。 每塊大小設(shè)置好了,接下來(lái),就需要將文件的路徑獲取,代碼中搭建輸入流,將文件讀入內(nèi)存緩沖區(qū)中,再搭建輸出流,將緩沖區(qū)輸出到新的分割文件中。 再接下來(lái)實(shí)現(xiàn)就很簡(jiǎn)單了。 新建一個(gè) FileSlice
類:有切割方法,拼接方法。
public class FileSlice {
/**
* 分割文件
* @param filePath 文件路徑
* @param filePieceSize 文件每塊大小,單位為字節(jié),為-1則默認(rèn)為每塊64M
* @return 成功返回True,出錯(cuò)則返回False
*/
public static boolean slice(Path filePath, int filePieceSize){
return true;
}
/**
* 將分割好的文件重新鏈接
* @param filePath 被分割好的其中之一文件路徑,默認(rèn)其他塊與其在同一目錄下
* @param howManyParts 一共有多少塊
* @return 成功返回True,出錯(cuò)則返回False
*/
public static boolean glue(Path filePath, int howManyParts){
return true;
}
}
接下來(lái)實(shí)現(xiàn)單線程的分割方法: 用圖解的話應(yīng)該是這樣:

代碼實(shí)現(xiàn): 進(jìn)入函數(shù)首先判斷文件是否存在:
if (!Files.exists(filePath)){
return false;
}
接下來(lái)判斷每塊大小是否使用默認(rèn)值:
if(filePieceSize == -1){
filePieceSize = 1024*1024*64;
}
將路徑轉(zhuǎn)換為文件對(duì)象,再計(jì)算將分割多少塊:
File file = filePath.toFile(); int howManyParts = (int) Math.ceil(file.length() / (double)filePieceSize);
初始化輸入輸出流,出錯(cuò)輸出錯(cuò)誤信息,返回false,獲得當(dāng)前目錄:
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();
接下來(lái)讀取文件,并且分別輸出到各個(gè)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)輸出完畢,關(guān)閉流
fileWriter.close();
}
//讀取完畢,關(guān)閉輸入流
fileReader.close();
} catch (IOException e) {
e.printStackTrace();
System.out.println("IO錯(cuò)誤!");
return false;
}
該函數(shù)已經(jīng)實(shí)現(xiàn)完畢,接下來(lái)測(cè)試(由于電影Fury有14G。。太大了。。還是換個(gè)吧):

我是大哥大第5集,有729M,大概能分個(gè)12個(gè)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é),總用時(shí)" + (after - before) + "ms" );
}
運(yùn)行結(jié)果:
分割文件我是大哥大.Kyou.kara.Ore.wa.Ep05.Chi_Jap.HDTVrip.1280X720.mp4,765321889字節(jié),總用時(shí)16335.0ms

速度還是挺慢的。。 下次還是換成多線程來(lái)實(shí)現(xiàn),再來(lái)測(cè)試下速度。在單線程情況下一個(gè)普通的40分鐘日劇都要15-30s左右,要是mkv格式的電影都要好久了。。不過(guò)其實(shí)極限應(yīng)該不在CPU中執(zhí)行的速度,而是在硬盤IO中,如果是普通硬盤那么就算是多線程也應(yīng)該提速不了多少。。
文件拼接
這個(gè)就很簡(jiǎn)單了,和分割相反就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文件名解析出錯(cuò)!");
return false;
}
//初始化緩沖區(qū)
byte [] buffer = new byte[1024 * 8];
//獲取文件存儲(chǔ)的路徑
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)讀入完畢,關(guān)閉流
fileReader.close();
}
//寫入完畢,關(guān)閉輸出流
fileWriter.close();
} catch (IOException e) {
e.printStackTrace();
System.out.println("IO錯(cuò)誤!");
return false;
}
return true;
}
再測(cè)試剛剛分割好的我是大哥大第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個(gè)part,用時(shí)" + (after - before) + "ms");
}
結(jié)果輸出,用12s左右,還行。
拼接12個(gè)part,用時(shí)12147.0ms
打開播放毫無(wú)問(wèn)題,最后截張圖。

未完待續(xù)。。下次來(lái)使用多線程進(jìn)行實(shí)現(xiàn)。
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
Spring Cache整合Redis實(shí)現(xiàn)方法詳解
這篇文章主要介紹了Spring Cache整合Redis實(shí)現(xiàn)方法詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-08-08
Java利用位運(yùn)算實(shí)現(xiàn)乘法運(yùn)算詳解
這篇文章主要為大家詳細(xì)介紹了Java如何用位運(yùn)算實(shí)現(xiàn)乘法運(yùn)算,在實(shí)現(xiàn)乘法時(shí)要用位運(yùn)算實(shí)現(xiàn),并且不能出現(xiàn)加減乘除任何符號(hào),感興趣的可以了解一下2023-04-04
springboot使用war包部署到外部tomcat過(guò)程解析
這篇文章主要介紹了springboot使用war包部署到外部tomcat過(guò)程解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-01-01
APT?注解處理器實(shí)現(xiàn)?Lombok?常用注解功能詳解
這篇文章主要為大家介紹了使用APT?注解處理器實(shí)現(xiàn)?Lombok?常用注解功能詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-09-09
在已經(jīng)使用mybatis的項(xiàng)目里引入mybatis-plus,結(jié)果不能共存的解決
這篇文章主要介紹了在已經(jīng)使用mybatis的項(xiàng)目里引入mybatis-plus,結(jié)果不能共存的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-03-03
JAVA使用quartz添加定時(shí)任務(wù),并依賴注入對(duì)象操作
這篇文章主要介紹了JAVA使用quartz添加定時(shí)任務(wù),并依賴注入對(duì)象操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-09-09
Java中easypoi的使用之導(dǎo)入校驗(yàn)
因工作需要,使用easypoi導(dǎo)入表格,并進(jìn)行校驗(yàn),將表格中有問(wèn)題的地方,給出提示信息,以表格形式返回,下面這篇文章主要給大家介紹了關(guān)于Java中easypoi的使用之導(dǎo)入校驗(yàn)的相關(guān)資料,需要的朋友可以參考下2023-03-03
SpringBoot項(xiàng)目docker容器部署實(shí)現(xiàn)
本文主要介紹了SpringBoot項(xiàng)目docker容器部署實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2022-03-03
ExecutorService實(shí)現(xiàn)獲取線程返回值
這篇文章主要介紹了ExecutorService實(shí)現(xiàn)獲取線程返回值,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-08-08

