Java-Io-RandomAccessFile任意位置讀寫數(shù)據(jù)的操作小結(jié)
介紹
RandomAccessFile是java語(yǔ)言中最豐富的文件訪問(wèn)類。RandomAccessFile類支持隨機(jī)訪問(wèn)方式,可以跳轉(zhuǎn)到文件的任意位置讀寫數(shù)據(jù),這個(gè)類在文件隨機(jī)讀取時(shí)有很大的優(yōu)勢(shì),可利用多線程完成對(duì)一個(gè)大文件的讀寫,利用seek對(duì)文件進(jìn)行切分,從大文件的不同位置開(kāi)線程進(jìn)行讀寫。
構(gòu)造,模式,方法
RandomAccessFile他的構(gòu)造函數(shù)只有兩種,所以僅限于操作文件,不能訪問(wèn)其他io設(shè)備,比如網(wǎng)絡(luò),內(nèi)存映像等。

而底層是使用c/c++進(jìn)行操作的,所以無(wú)法進(jìn)行自定義操作

如果RandomAccessFile作為輸出流時(shí),寫出到的文件如果不存在,則在執(zhí)行過(guò)程中自動(dòng)創(chuàng)建 如果寫出到的文件存在,則會(huì)對(duì)原有文件內(nèi)容進(jìn)行覆蓋。(默認(rèn)情況下,從頭覆蓋)
| 模 式 | 作 用 |
|---|---|
| r | 表示以只讀方式打開(kāi),調(diào)用結(jié)果對(duì)象的任何write方法都將導(dǎo)致拋出IOException |
| rw | 打開(kāi)以便讀取和寫入,如果該文件尚不存在,則嘗試創(chuàng)建該文件 |
| rws | 打開(kāi)以便讀取和寫入,相對(duì)于"rw",還要求對(duì)文件內(nèi)容或元數(shù)據(jù)的每個(gè)更新都同步寫入到底層存儲(chǔ)設(shè)備 |
| rwd | 打開(kāi)以便讀取和寫入,相對(duì)于"rw",還要求對(duì)文件內(nèi)容的每個(gè)更新都同步寫入到底層存儲(chǔ)設(shè)備 |
| 方 法 | 作 用 |
|---|---|
void close() | 重要,關(guān)閉此隨機(jī)訪問(wèn)文件流并釋放與該流關(guān)聯(lián)的所有系統(tǒng)資源 |
| FileChannel getChannel() | 返回與此文件關(guān)聯(lián)的唯一FileChannel對(duì)象,NIO用到 |
long getFilePointer() | 返回此文件中的當(dāng)前偏移量(實(shí)時(shí)) |
long length() | 返回此文件的長(zhǎng)度 |
| int read() | 從此文件中讀取一個(gè)數(shù)據(jù)字節(jié) |
int read(byte[] b) | 將最多b.length個(gè)數(shù)據(jù)字節(jié)從此文件讀入byte數(shù)組,返回讀入的總字節(jié)數(shù),如果由于已經(jīng)達(dá)到文件末尾而不再有數(shù)據(jù),則返回-1。 |
| int read(byte[] b, int off, int len) | 將最多l(xiāng)en個(gè)數(shù)據(jù)字節(jié)從此文件的指定初始偏移量off讀入byte數(shù)組 |
| boolean readBoolean() | 從此文件讀取一個(gè)boolean,其余readByte()、readChar()、readDouble()等類似 |
String readLine() | 從此文件讀取文本的下一行 (會(huì)亂碼) |
void seek(long pos) | 重要,設(shè)置到此文件開(kāi)頭測(cè)量到的文件指針偏移量,在該位置發(fā)生下一個(gè)讀取或?qū)懭氩僮?字節(jié)單位) |
| int skipBytes(int n) | 重要,嘗試跳過(guò)輸入的n個(gè)字節(jié)以丟棄跳過(guò)的字節(jié),返回跳過(guò)的字節(jié)數(shù) |
void write(byte[] b) | 將b.length個(gè)字節(jié)從指定byte數(shù)組寫入到此文件中 |
| void write(byte[] b, int off, int len) | 將len個(gè)字節(jié)從指定byte數(shù)組寫入到此文件,并從偏移量off處開(kāi)始 |
| void write(int b) | 向此文件寫入指定的字節(jié) |
| void writeBoolean(boolean v) | 按單字節(jié)值將boolean寫入該文件,其余writeByte(int v)、writeBytes(String s)、writeChar(int v)等都類似 |
問(wèn)題解決
文件覆蓋改追加
在RandomAccessFile作為輸入流時(shí)候,第一次寫的時(shí)候會(huì)將文件從頭覆蓋,后面在寫就是追加(同一個(gè)對(duì)象) ,如果想在第一次就追加的話那么就設(shè)置寫的位置w.seek(w.length());這樣就會(huì)從文件的最后一個(gè)字節(jié)之后開(kāi)始寫了
try (
RandomAccessFile r = new RandomAccessFile(new File(readFile), "r");
RandomAccessFile w = new RandomAccessFile(new File(writeFile), "rw");
){
w.seek(w.length()); // 將指針指向文件最后,進(jìn)行追加
byte[] bytes = new byte[1024];
int len = -1;
while ((len = r.read(bytes)) != -1) {
w.write(bytes);
}
} catch (IOException e) {
e.printStackTrace();
}案例文件讀寫
CodeStartAndStopTimeUtil.creator(
() -> {
File file = ResourceFileUtil.getFile("mp4/mp4_2.zip");
String readFile = file.getAbsolutePath();
String writeFile = file.getParent() + File.separator + "mp4_11112.zip";
byte[] bytes = new byte[2048];
try (RandomAccessFile r = new RandomAccessFile(new File(readFile), "r");
RandomAccessFile w = new RandomAccessFile(new File(writeFile), "rw"); ) {
int len = -1;
while ((len = r.read(bytes)) != -1) {
w.write(bytes);
}
}
});
}案例多線程文件讀寫
CodeStartAndStopTimeUtil.creator(
() -> {
File file = ResourceFileUtil.getFile("mp4/mp4.zip");
String readFile = file.getAbsolutePath();
String writeFile = file.getParent() + File.separator + "mp4_11112.zip";
long length = new File(readFile).length(); // 文件一共大小
int num = (int)length/8; // 每個(gè)線程讀寫多少字節(jié) 20971520(20mb) 2.5個(gè)g4~5秒左右
List<long[]> longs = DataGroup.dataGroupOnce(length, num);
List<Future<?>> futures = new ArrayList<>();
for (long[] aLong : longs) {
long l = aLong[0]; // 起始位置
// 創(chuàng)建線程并運(yùn)行
Future<?> randomAccessFile =
ExecutorUtils.createFuture(
"RandomAccessFile",
() -> {
byte[] bytes = new byte[num];
try (
// 在線程內(nèi)部創(chuàng)RandomAccessFile對(duì)象
RandomAccessFile r = new RandomAccessFile(new File(readFile), "r");
RandomAccessFile w = new RandomAccessFile(new File(writeFile), "rw"); ) {
r.seek(l);
int len = r.read(bytes);
if (len < num) {
// 調(diào)整數(shù)組
bytes = ArrayByteUtil.getActualBytes(bytes);
}
// 寫入文件
w.seek(l);
w.write(bytes);
System.out.println(l );
} catch (IOException e) {
e.printStackTrace();
}
});
futures.add(randomAccessFile);
}
// 阻塞全部線程執(zhí)行完畢
ExecutorUtils.waitComplete(futures);
});問(wèn)題
多線程使用RandomAccessFile
常見(jiàn)的使用多線程的場(chǎng)景: 斷點(diǎn)續(xù)傳和斷點(diǎn)下載,或者文件加密解密等
斷點(diǎn)續(xù)傳原理就是:
- 前端將文件安裝百分比進(jìn)行計(jì)算,每次上傳文件的百分之一(文件分片),給文件分片做上序號(hào)
- 后端將前端每次上傳的文件,放入到緩存目錄
- 等待前端將全部的文件內(nèi)容都上傳完畢后,發(fā)送一個(gè)合并請(qǐng)求
- 后端使用RandomAccessFile進(jìn)多線程讀取所有的分片文件,一個(gè)線程一個(gè)分片
- 后端每個(gè)線程按照序號(hào)將分片的文件寫入到目標(biāo)文件中,
- 在上傳文件的過(guò)程中發(fā)生斷網(wǎng)了或者手動(dòng)暫停了,下次上傳的時(shí)候發(fā)送續(xù)傳請(qǐng)求,讓后端刪除最后一個(gè)分片
- 前端重新發(fā)送上次的文件分片
解決readLine亂碼的問(wèn)題
RandomAccessFile 讀寫文件時(shí),不管文件中保存的數(shù)據(jù)編碼格式是什么 使用 RandomAccessFile對(duì)象方法的 readLine() 都會(huì)將編碼格式轉(zhuǎn)換成 ISO-8859-1 所以 輸出顯示是還要在進(jìn)行一次轉(zhuǎn)碼
//需要重新轉(zhuǎn)碼才能正常顯示 System.out.println( new String(r.readLine().getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8) );
多線程讀寫亂碼的問(wèn)題
RandomAccessFile對(duì)象是不能線程共享的,一個(gè)RandomAccessFile對(duì)象的文件指針只有一個(gè),多個(gè)線程如果都去操作這個(gè)指針進(jìn)行seek的話,那么讀寫就會(huì)被來(lái)回的跳轉(zhuǎn),導(dǎo)致多個(gè)線程的讀寫混亂,所以每個(gè)線程必須在線程內(nèi)部創(chuàng)建RandomAccessFile,這樣每個(gè)線程都有一個(gè)自己的文件指針了, 正確案例

到此這篇關(guān)于Java-Io-RandomAccessFile(任意位置讀寫數(shù)據(jù))的文章就介紹到這了,更多相關(guān)Java-Io-RandomAccessFile內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
SpringCloud?OpenFeign?服務(wù)調(diào)用傳遞?token的場(chǎng)景分析
這篇文章主要介紹了SpringCloud?OpenFeign?服務(wù)調(diào)用傳遞?token的場(chǎng)景分析,本篇文章簡(jiǎn)單介紹?OpenFeign?調(diào)用傳遞?header?,以及多線程環(huán)境下可能會(huì)出現(xiàn)的問(wèn)題,其中涉及到?ThreadLocal?的相關(guān)知識(shí),需要的朋友可以參考下2022-07-07
Nacos配置文件使用經(jīng)驗(yàn)及CAP原則詳解
這篇文章主要為大家介紹了Nacos配置文件使用經(jīng)驗(yàn)及CAP規(guī)則詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2024-02-02
Java中遞歸構(gòu)建樹(shù)形結(jié)構(gòu)的算法解讀
該文章介紹了如何使用Java遞歸算法構(gòu)建樹(shù)形結(jié)構(gòu),通過(guò)定義樹(shù)節(jié)點(diǎn)類,遍歷扁平數(shù)據(jù)列表,將節(jié)點(diǎn)加入對(duì)應(yīng)父節(jié)點(diǎn)的子節(jié)點(diǎn)列表中,實(shí)現(xiàn)從扁平數(shù)據(jù)到樹(shù)形結(jié)構(gòu)的轉(zhuǎn)換2025-03-03
網(wǎng)易Java程序員兩輪面試 請(qǐng)問(wèn)你能答對(duì)幾個(gè)?
為大家分享網(wǎng)易Java程序員兩輪面試題,考考大家,這些問(wèn)題你能答對(duì)幾個(gè)?2017-11-11
idea使用帶provide修飾依賴導(dǎo)致ClassNotFound
程序打包到Linux上運(yùn)行時(shí),若Linux上也有這些依賴,為了在Linux上運(yùn)行時(shí)避免依賴沖突,可以使用provide修飾,本文主要介紹了idea使用帶provide修飾依賴導(dǎo)致ClassNotFound,下面就來(lái)介紹一下解決方法,感興趣的可以了解一下2024-01-01
Spring boot如何快速的配置多個(gè)Redis數(shù)據(jù)源
這篇文章主要介紹了Spring boot如何快速的配置多個(gè)Redis數(shù)據(jù)源,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-06-06
Spring Boot如何通過(guò)CORS處理跨域問(wèn)題
這篇文章主要介紹了Spring Boot如何通過(guò)CORS處理跨域問(wèn)題,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-04-04

