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文件合并和拆分的资料请关注代码网其它相关文章!
发表评论