Java使用ByteBuffer進行多文件合并和拆分的代碼實現(xiàn)
1.背景
因為驗證證書的需要,需要把證書文件和公鑰給到客戶,考慮到多個文件交互的不便性,所以決定將2個文件合并成一個文件交互給客戶。剛開始采用字符串拼接2個文件內(nèi)容,但是由于是加密文件,采用字符串形式合并后,拆分后文件不可用,最后采用基于二進制流拆分和合并文件,效果不錯!
2.代碼工程
實驗目的
對文件進行二進制流合并和拆分
合并代碼
ByteBuffer.allocate(4).putInt(publicCertsContent.length).array() 的作用是將 publicCertsContent(公鑰證書內(nèi)容)的長度轉(zhuǎn)換為 4 個字節(jié)的整數(shù),并寫入到字節(jié)緩沖區(qū)中。具體作用如下:
ByteBuffer.allocate(4): 創(chuàng)建一個大小為 4 字節(jié)的ByteBuffer,因為 Java 中的整數(shù)(int)是 4 個字節(jié)。putInt(publicCertsContent.length): 將publicCertsContent的字節(jié)數(shù)組長度(即證書文件的字節(jié)長度)存入ByteBuffer。這樣就把證書內(nèi)容的長度存儲為一個 4 字節(jié)的整數(shù)。array(): 將ByteBuffer轉(zhuǎn)換為字節(jié)數(shù)組。這個數(shù)組包含了公鑰證書內(nèi)容長度的 4 字節(jié)表示形式。
這個表達式的作用是將 publicCertsContent 數(shù)組的長度轉(zhuǎn)換為二進制的 4 字節(jié)表示形式,并寫入輸出流(outputStream)中。這樣在以后讀取合并文件時,代碼可以知道該讀取多少字節(jié)屬于 publicCertsContent。
有小伙伴這里可能有疑問,4字節(jié)夠存多大數(shù)字呢?
4 個字節(jié)(即 32 位)在計算機中用于存儲整數(shù),能表示的整數(shù)范圍如下:
- 有符號整數(shù)(
int類型):- 范圍是:-2,147,483,648 到 2,147,483,647
- 其中 1 位用于符號(正負),31 位用于數(shù)值。
- 無符號整數(shù)(
unsigned int,Java 中沒有直接支持,但可以通過轉(zhuǎn)換處理):- 范圍是:0 到 4,294,967,295
通常 Java 中的 int 類型是有符號的,因此 4 個字節(jié)可以存儲的整數(shù)范圍為 -2^31 到 2^31 - 1,即 -2,147,483,648 到 2,147,483,647。所以只要不超過這個限制都能存下來
package com.et;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
public class FileMerger {
public static void main(String[] args) {
String privateKeys = "D:/IdeaProjects/Java-demo/file/src/main/resources/privateKeys.keystore";
String publicCerts = "D:/IdeaProjects/Java-demo/file/src/main/resources/publicCerts.keystore";
merger(privateKeys,publicCerts);
}
public static String merger(String privateKeys, String publicCerts) {
String directoryPath = FileUtils.extractDirectoryPath(privateKeys);
String mergedFile =directoryPath+"merge.keystore";
try {
byte[] privateKeysContent = FileUtils.readBinaryFile(privateKeys);
byte[] publicCertsContent = FileUtils.readBinaryFile(publicCerts);
// create outputStream
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
// write keystore length(4 byte)
outputStream.write(ByteBuffer.allocate(4).putInt(privateKeysContent.length).array());
// write keystore content
outputStream.write(privateKeysContent);
// witer license content(4 byte int )
outputStream.write(ByteBuffer.allocate(4).putInt(publicCertsContent.length).array());
// write license content
outputStream.write(publicCertsContent);
// write merge content to file
FileUtils.writeBinaryFile(mergedFile, outputStream.toByteArray());
System.out.println("merge success " + mergedFile);
} catch (IOException e) {
e.printStackTrace();
}
return mergedFile;
}
}
拆分代碼
拆分邏輯就很簡單了,先讀取4字節(jié),獲取文件大小,然后依次獲取文件內(nèi)容就可以了 buffer.getInt() 可以正確讀取文件中的大小信息。具體來說,它會從 ByteBuffer 中讀取 4 個字節(jié),并將這些字節(jié)解釋為一個整數(shù)。 如果你按照之前的代碼將文件內(nèi)容合并時,將文件大小以 4 字節(jié)整數(shù)形式寫入到文件中,那么你可以使用 buffer.getInt() 來讀取這個大小。例如,假設(shè)你在合并文件時使用了如下方式寫入大?。?/p>
outputStream.write(ByteBuffer.allocate(4).putInt(content.length).array());
在讀取合并文件時,你可以這樣做:
ByteBuffer buffer = ByteBuffer.wrap(fileContent); // 假設(shè) fileContent 是讀取的字節(jié)數(shù)組 int size = buffer.getInt(); // 讀取前 4 個字節(jié),獲取文件大小
這里的 buffer.getInt() 會正確讀取到前 4 個字節(jié),并將其轉(zhuǎn)換為整數(shù),這樣你就得到了文件內(nèi)容的大小。 請確保在讀取文件時,字節(jié)順序(字節(jié)序)與寫入時一致,默認情況下是大端序(Big Endian)。如果你的應(yīng)用需要小端序(Little Endian),你可以使用 buffer.order(ByteOrder.LITTLE_ENDIAN) 來設(shè)置字節(jié)序。
package com.et;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
public class FileSplitter {
public static void main(String[] args) {
String mergedFile = "D:/IdeaProjects/Java-demo/file/src/main/resources/merge.keystore";
split(mergedFile);
}
private static void debugContent(byte[] content, String fileName) {
System.out.printf("File: %s%n", fileName);
System.out.printf("Length: %d%n", content.length);
System.out.print("Content: ");
for (byte b : content) {
System.out.printf("%02X ", b);
}
System.out.println();
}
static String[] split(String mergedFile) {
String directoryPath = FileUtils.extractDirectoryPath(mergedFile);
String privateKeysFile = directoryPath + ".privateKeys.keystore";
String publicCertsFile = directoryPath + ".publicCerts.keystore";
String[] filePaths = new String[]{privateKeysFile, publicCertsFile};
try {
// read merge content
byte[] mergedContent = FileUtils.readBinaryFile(mergedFile);
// use ByteBuffer parse
ByteBuffer buffer = ByteBuffer.wrap(mergedContent);
// read privateKeys content length
int privateKeysLength = buffer.getInt();
// read privateKeys content
byte[] privateKeysContent = new byte[privateKeysLength];
buffer.get(privateKeysContent);
// read publicCerts content length
int publicCertsLength = buffer.getInt();
// read publicCerts content
byte[] publicCertsContent = new byte[publicCertsLength];
buffer.get(publicCertsContent);
// write privateKeys and publicCerts content to file
FileUtils.writeBinaryFile(privateKeysFile, privateKeysContent);
FileUtils.writeBinaryFile(publicCertsFile, publicCertsContent);
System.out.println("merge file split " + privateKeysFile + " and " + publicCertsFile);
} catch (IOException e) {
e.printStackTrace();
}
return filePaths;
}
private static byte[] extractContent(byte[] mergedContent, byte[] beginMarker, byte[] endMarker) {
int beginIndex = indexOf(mergedContent, beginMarker);
int endIndex = indexOf(mergedContent, endMarker, beginIndex);
if (beginIndex != -1 && endIndex != -1) {
// Move past the start marker
beginIndex += beginMarker.length;
// Adjust endIndex to exclude the end marker
int adjustedEndIndex = endIndex;
// Extract content
return Arrays.copyOfRange(mergedContent, beginIndex, adjustedEndIndex);
} else {
return new byte[0]; // Return empty array if markers are not found
}
}
private static byte[] removeEmptyLines(byte[] content) {
// Convert byte array to list of lines
List<byte[]> lines = splitIntoLines(content);
// Filter out empty lines
lines = lines.stream()
.filter(line -> line.length > 0)
.collect(Collectors.toList());
// Reassemble content
return mergeLines(lines);
}
private static List<byte[]> splitIntoLines(byte[] content) {
List<byte[]> lines = new ArrayList<>();
int start = 0;
for (int i = 0; i < content.length; i++) {
if (content[i] == '\n') { // Line break
lines.add(Arrays.copyOfRange(content, start, i));
start = i + 1;
}
}
if (start < content.length) {
lines.add(Arrays.copyOfRange(content, start, content.length));
}
return lines;
}
private static byte[] mergeLines(List<byte[]> lines) {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
for (byte[] line : lines) {
try {
outputStream.write(line);
outputStream.write('\n'); // Re-add line break
} catch (IOException e) {
e.printStackTrace();
}
}
return outputStream.toByteArray();
}
private static int indexOf(byte[] array, byte[] target) {
return indexOf(array, target, 0);
}
private static int indexOf(byte[] array, byte[] target, int start) {
for (int i = start; i <= array.length - target.length; i++) {
if (Arrays.equals(Arrays.copyOfRange(array, i, i + target.length), target)) {
return i;
}
}
return -1; // Return -1 if target not found
}
}
工具類
package com.et;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class FileUtils {
static byte[] readBinaryFile(String filePath) throws IOException {
File file = new File(filePath);
try (FileInputStream fis = new FileInputStream(file)) {
byte[] fileBytes = new byte[(int) file.length()];
fis.read(fileBytes);
return fileBytes;
}
}
public static String extractDirectoryPath(String filePath) {
File file = new File(filePath);
return file.getParent()+File.separator; // 獲取文件所在的目錄
}
static void writeBinaryFile(String filePath, byte[] content) throws IOException {
try (FileOutputStream fos = new FileOutputStream(filePath)) {
fos.write(content);
}
}
}
以上只是一些關(guān)鍵代碼,所有代碼請參見下面代碼倉庫
代碼倉庫
3.測試
測試類
package com.et;
public class Main {
public static void main(String[] args) {
System.out.println("Hello world!");
String privateKeys = "D:/IdeaProjects/Java-demo/file/src/main/resources/privateKeys.keystore";
String publicCerts = "D:/IdeaProjects/Java-demo/file/src/main/resources/publicCerts.keystore";
FileMerger.merger(privateKeys,publicCerts);
String mergedFile = "D:/IdeaProjects/Java-demo/file/src/main/resources/merge.keystore";
FileSplitter.split(mergedFile);
}
}
運行main方法,日志顯示如下
merge success D:\IdeaProjects\Java-demo\file\src\main\resources\merge.keystore merge file split D:\IdeaProjects\Java-demo\file\src\main\resources\.privateKeys.keystore and D:\IdeaProjects\Java-demo\file\src\main\resources\.publicCerts.keystore
拆分后文件于原文件大小一模一樣

以上就是Java使用ByteBuffer進行多文件合并和拆分的代碼實現(xiàn)的詳細內(nèi)容,更多關(guān)于Java ByteBuffer文件合并和拆分的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
springboot應(yīng)用訪問zookeeper的流程
這篇文章主要介紹了springboot應(yīng)用訪問zookeeper的流程,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-01-01
spring無法讀取properties文件數(shù)據(jù)問題詳解
這篇文章主要介紹了spring無法讀取properties文件數(shù)據(jù)問題詳解,需要的朋友可以參考下2020-02-02
Spring Boot中的SpringSecurity基礎(chǔ)教程
Spring Security是一個功能強大且高度可定制的身份驗證和訪問控制框架。它實際上是保護基于spring的應(yīng)用程序的標準Spring Security是一個框架,側(cè)重于為Java應(yīng)用程序提供身份驗證和授權(quán),這篇文章主要介紹了Spring Boot中的SpringSecurity學習,需要的朋友可以參考下2023-01-01
SpringBoot通過自定義注解實現(xiàn)配置類的自動注入的實現(xiàn)
本文主要介紹了SpringBoot通過自定義注解實現(xiàn)配置類的自動注入的實現(xiàn),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2023-04-04
如何用Netty實現(xiàn)高效的HTTP服務(wù)器
這篇文章主要介紹了如何用Netty實現(xiàn)高效的HTTP服務(wù)器,對HTTP感興趣的同學可以參考一下2021-04-04

