1.背景
因为验证证书的需要,需要把证书文件和公钥给到客户,考虑到多个文件交互的不便性,所以决定将2个文件合并成一个文件交互给客户。刚开始采用字符串拼接2个文件内容,但是由于是加密文件,采用字符串形式合并后,拆分后文件不可用,最后采用基于二进制流拆分和合并文件,效果不错!
2.代码工程
实验目的
对文件进行二进制流合并和拆分
合并代码
bytebuffer.allocate(4).putint(publiccertscontent.length).array() 的作用是将 publiccertscontent(公钥证书内容)的长度转换为 4 个字节的整数,并写入到字节缓冲区中。具体作用如下:
bytebuffer.allocate(4): 创建一个大小为 4 字节的bytebuffer,因为 java 中的整数(int)是 4 个字节。putint(publiccertscontent.length): 将publiccertscontent的字节数组长度(即证书文件的字节长度)存入bytebuffer。这样就把证书内容的长度存储为一个 4 字节的整数。array(): 将bytebuffer转换为字节数组。这个数组包含了公钥证书内容长度的 4 字节表示形式。
这个表达式的作用是将 publiccertscontent 数组的长度转换为二进制的 4 字节表示形式,并写入输出流(outputstream)中。这样在以后读取合并文件时,代码可以知道该读取多少字节属于 publiccertscontent。
有小伙伴这里可能有疑问,4字节够存多大数字呢?
4 个字节(即 32 位)在计算机中用于存储整数,能表示的整数范围如下:
- 有符号整数(
int类型):- 范围是:-2,147,483,648 到 2,147,483,647
- 其中 1 位用于符号(正负),31 位用于数值。
- 无符号整数(
unsigned int,java 中没有直接支持,但可以通过转换处理):- 范围是:0 到 4,294,967,295
通常 java 中的 int 类型是有符号的,因此 4 个字节可以存储的整数范围为 -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字节,获取文件大小,然后依次获取文件内容就可以了 buffer.getint() 可以正确读取文件中的大小信息。具体来说,它会从 bytebuffer 中读取 4 个字节,并将这些字节解释为一个整数。 如果你按照之前的代码将文件内容合并时,将文件大小以 4 字节整数形式写入到文件中,那么你可以使用 buffer.getint() 来读取这个大小。例如,假设你在合并文件时使用了如下方式写入大小:
outputstream.write(bytebuffer.allocate(4).putint(content.length).array());
在读取合并文件时,你可以这样做:
bytebuffer buffer = bytebuffer.wrap(filecontent); // 假设 filecontent 是读取的字节数组 int size = buffer.getint(); // 读取前 4 个字节,获取文件大小
这里的 buffer.getint() 会正确读取到前 4 个字节,并将其转换为整数,这样你就得到了文件内容的大小。 请确保在读取文件时,字节顺序(字节序)与写入时一致,默认情况下是大端序(big endian)。如果你的应用需要小端序(little endian),你可以使用 buffer.order(byteorder.little_endian) 来设置字节序。
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);
}
}
}
以上只是一些关键代码,所有代码请参见下面代码仓库
代码仓库
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进行多文件合并和拆分的代码实现的详细内容,更多关于java bytebuffer文件合并和拆分的资料请关注代码网其它相关文章!
发表评论