單線程實現(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
類:有切割方法,拼接方法。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
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ù)首先判斷文件是否存在:
1
2
3
|
if (!files.exists(filepath)){ return false ; } |
接下來判斷每塊大小是否使用默認值:
1
2
3
|
if (filepiecesize == - 1 ){ filepiecesize = 1024 * 1024 * 64 ; } |
將路徑轉(zhuǎn)換為文件對象,再計算將分割多少塊:
1
2
|
file file = filepath.tofile(); int howmanyparts = ( int ) math.ceil(file.length() / ( double )filepiecesize); |
初始化輸入輸出流,出錯輸出錯誤信息,返回false,獲得當前目錄:
1
2
3
4
5
6
7
8
9
10
|
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文件中:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
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錯誤!" ); return false ; } |
該函數(shù)已經(jīng)實現(xiàn)完畢,接下來測試(由于電影fury有14g。。太大了。。還是換個吧):
我是大哥大第5集,有729m,大概能分個12個part吧。
1
2
3
4
5
6
7
8
9
10
11
12
|
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。 直接上完整代碼:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
|
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)讀入完畢,關(guān)閉流 filereader.close(); } //寫入完畢,關(guān)閉輸出流 filewriter.close(); } catch (ioexception e) { e.printstacktrace(); system.out.println( "io錯誤!" ); return false ; } return true ; } |
再測試剛剛分割好的我是大哥大第5集
1
2
3
4
5
6
7
8
9
10
11
12
|
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)容,希望對大家的學習有所幫助,也希望大家多多支持服務器之家。
原文鏈接:https://juejin.im/post/5bf3f63c6fb9a049f570c158