当前位置: 首页 > news >正文

java使用ByteBuffer进行多文件合并和拆分

1.背景

因为验证证书的需要,需要把证书文件和公钥给到客户,考虑到多个文件交互的不便性,所以决定将2个文件合并成一个文件交互给客户。刚开始采用字符串拼接2个文件内容,但是由于是加密文件,采用字符串形式合并后,拆分后文件不可用,最后采用基于二进制流拆分和合并文件,效果不错!

2.代码工程

实验目的

对文件进行二进制流合并和拆分

合并代码

ByteBuffer.allocate(4).putInt(publicCertsContent.length).array() 的作用是将 publicCertsContent(公钥证书内容)的长度转换为 4 个字节的整数,并写入到字节缓冲区中。具体作用如下:

  1. ByteBuffer.allocate(4): 创建一个大小为 4 字节的 ByteBuffer,因为 Java 中的整数(int)是 4 个字节。
  2. putInt(publicCertsContent.length): 将 publicCertsContent 的字节数组长度(即证书文件的字节长度)存入 ByteBuffer。这样就把证书内容的长度存储为一个 4 字节的整数。
  3. 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 outputStreamByteArrayOutputStream outputStream = new ByteArrayOutputStream();// write keystore length(4 byte)outputStream.write(ByteBuffer.allocate(4).putInt(privateKeysContent.length).array());// write keystore contentoutputStream.write(privateKeysContent);// witer license content(4 byte int )outputStream.write(ByteBuffer.allocate(4).putInt(publicCertsContent.length).array());// write license contentoutputStream.write(publicCertsContent);// write merge content to fileFileUtils.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 contentbyte[] mergedContent = FileUtils.readBinaryFile(mergedFile);// use ByteBuffer parseByteBuffer buffer = ByteBuffer.wrap(mergedContent);// read privateKeys content lengthint privateKeysLength = buffer.getInt();// read privateKeys contentbyte[] privateKeysContent = new byte[privateKeysLength];buffer.get(privateKeysContent);// read publicCerts content lengthint publicCertsLength = buffer.getInt();// read publicCerts contentbyte[] publicCertsContent = new byte[publicCertsLength];buffer.get(publicCertsContent);// write privateKeys and publicCerts content to fileFileUtils.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 markerbeginIndex += beginMarker.length;// Adjust endIndex to exclude the end markerint adjustedEndIndex = endIndex;// Extract contentreturn 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 linesList<byte[]> lines = splitIntoLines(content);// Filter out empty lineslines = lines.stream().filter(line -> line.length > 0).collect(Collectors.toList());// Reassemble contentreturn 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 breaklines.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);}}
}

以上只是一些关键代码,所有代码请参见下面代码仓库

代码仓库

  • https://github.com/Harries/Java-demo(file)

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

拆分后文件于原文件大小一模一样

file

4.引用

  •  java使用ByteBuffer记录文件大小并进行多文件合并和拆分 | Harries Blog™

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 深入理解前端拖拽:从基础实现到事件冒泡与委托的应用【面试真题】
  • HTTP常见状态码 HTTP的逐步发展(通俗易懂版)
  • 大数据新视界 --大数据大厂之JavaScript在大数据前端展示中的精彩应用
  • 【论文笔记】Are Large Kernels Better Teacheres than Transformers for ConvNets
  • 一文入门生成式AI(理解ChatGPT的原理)
  • C语言的一些小知识(四)
  • C++ | Leetcode C++题解之第414题第三大的数
  • docker基础学习
  • 2024 研究生数学建模竞赛(D题)建模秘籍|大数据驱动的地理综合问题|文章代码思路大全
  • Git常用指令整理【新手入门级】【by慕羽】
  • Matlab simulink建模与仿真 第十五章(信号源库)
  • EmguCV学习笔记 VB.Net 12.1 二维码解析
  • wsl2桥接网络 ubuntu到弃坑到又跳坑
  • Java 多态(难)
  • Python语言基础教程(下)4.0
  • [分享]iOS开发-关于在xcode中引用文件夹右边出现问号的解决办法
  • Facebook AccountKit 接入的坑点
  • Github访问慢解决办法
  • JavaScript HTML DOM
  • Java多态
  • Java基本数据类型之Number
  • Lucene解析 - 基本概念
  • Mac 鼠须管 Rime 输入法 安装五笔输入法 教程
  • ReactNative开发常用的三方模块
  • spring学习第二天
  • vue-cli3搭建项目
  • 测试如何在敏捷团队中工作?
  • 番外篇1:在Windows环境下安装JDK
  • 给自己的博客网站加上酷炫的初音未来音乐游戏?
  • 开放才能进步!Angular和Wijmo一起走过的日子
  • 浅谈Kotlin实战篇之自定义View图片圆角简单应用(一)
  • 如何设计一个比特币钱包服务
  • 手写一个CommonJS打包工具(一)
  • 自制字幕遮挡器
  • Java数据解析之JSON
  • Python 之网络式编程
  • 京东物流联手山西图灵打造智能供应链,让阅读更有趣 ...
  • ​Java基础复习笔记 第16章:网络编程
  • ​数据结构之初始二叉树(3)
  • (1)Nginx简介和安装教程
  • (10)工业界推荐系统-小红书推荐场景及内部实践【排序模型的特征】
  • (14)Hive调优——合并小文件
  • (3)医疗图像处理:MRI磁共振成像-快速采集--(杨正汉)
  • (5)STL算法之复制
  • (Redis使用系列) Springboot 实现Redis 同数据源动态切换db 八
  • (大众金融)SQL server面试题(1)-总销售量最少的3个型号的车及其总销售量
  • (附源码)springboot教学评价 毕业设计 641310
  • (附源码)基于SpringBoot和Vue的厨到家服务平台的设计与实现 毕业设计 063133
  • (转)JAVA中的堆栈
  • (转)可以带来幸福的一本书
  • .Net的C#语言取月份数值对应的MonthName值
  • .net中应用SQL缓存(实例使用)
  • /tmp目录下出现system-private文件夹解决方法
  • @Validated和@Valid校验参数区别
  • [ai笔记9] openAI Sora技术文档引用文献汇总