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://www.rhkb.cn/news/428772.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

界面控件Telerik UI for WinForms 2024 Q3概览 - 支持合并单元格等

Telerik UI for WinForms拥有适用Windows Forms的110多个令人惊叹的UI控件。所有的UI for WinForms控件都具有完整的主题支持&#xff0c;可以轻松地帮助开发人员在桌面和平板电脑应用程序提供一致美观的下一代用户体验。 本文将介绍界面组件Telerik UI for WinForms在今年第一…

LeetCode2414题: 最长的字母序连续子字符串的长度(原创)

【题目描述】 字母序连续字符串 是由字母表中连续字母组成的字符串。换句话说&#xff0c;字符串 "abcdefghijklmnopqrstuvwxyz" 的任意子字符串都是 字母序连续字符串 。 例如&#xff0c;"abc" 是一个字母序连续字符串&#xff0c;而 "acb" 和…

vscode中如何配置c/c++环境

“批判他人总是想的太简单 剖析自己总是想的太困难” 文章目录 前言文章有误敬请斧正 不胜感恩&#xff01;一、准备工作二、安装 VSCode 插件三、配置 VSCode1. 配置编译任务&#xff08;tasks.json&#xff09;2. 配置调试器&#xff08;launch.json&#xff09; 四、运行和调…

Gitee Pipeline 从入门到实战【详细步骤】

文章目录 Gitee Pipeline 简介Gitee Pipeline 实战案例 1 - 前端部署输入源NPM 构建Docker 镜像构建Shell 命令执行案例 2 - 后端部署全局参数输入源Maven 构建Docker 镜像构建Shell 命令执行参考🚀 本文目标:快速了解 Gitee Pipeline,并实现前端及后端打包部署。 Gitee Pi…

Fabric:布匹纺织缺陷检测数据集(猫脸码客 第195期)

布匹数据集在纺织工业中的应用与探索&#xff1a;从布匹检索到纹理检测 引言 在快速发展的纺织工业中&#xff0c;信息技术的深度融合正逐步推动产业向智能化、精细化转型。其中&#xff0c;布匹数据集作为连接传统制造与数字技术的桥梁&#xff0c;其在布匹检索、纹理检测等…

【数据结构与算法 | 灵神题单 | 二叉搜索树篇】力扣99, 1305, 230, 897

1. 力扣99&#xff1a;恢复二叉搜索树 1.1 题目&#xff1a; 给你二叉搜索树的根节点 root &#xff0c;该树中的 恰好 两个节点的值被错误地交换。请在不改变其结构的情况下&#xff0c;恢复这棵树 。 示例 1&#xff1a; 输入&#xff1a;root [1,3,null,null,2] 输出&…

Python “函数” ——Python面试100道实战题目练习,巩固知识、检查技术、成功就业

本文主要是作为Python中函数的一些题目&#xff0c;方便学习完Python的函数之后进行一些知识检验&#xff0c;感兴趣的小伙伴可以试一试&#xff0c;含选择题、判断题、实战题、填空题&#xff0c;答案在第五章。 在做题之前可以先学习或者温习一下Python的函数&#xff0c;推荐…

第一次安装Pytorch

1、新版本的Anaconda内置的python版本是3.12&#xff0c; 目前 Windows 上的 PyTorch 仅支持 Python 3.8-3.11;不支持 Python 2.x。 1、创建运行环境 在不创建虚拟环境的情况下&#xff0c;不建议使用最新的Python和Anaconda。 在几次失败后&#xff0c;我使用的是Anaconda3-2…

Java反序列化利用链篇 | CC6链分析(通用版CC链)

文章目录 CC6和CC1之间的区别CC6的调用链构造CC6的payload完成TiedMapEntry.getValue()完成TiedMapEntry.hashCode()完成HashMap.hash()及HashMap.readObject()解决hash()方法提前触发的问题 系列篇其他文章&#xff0c;推荐顺序观看~ Java反序列化利用链篇 | JdbcRowSetImpl利…

【python计算机视觉编程——10.OpenCV】

python计算机视觉编程——10.OpenCV 10.OpenCV10.2 OpenCV基础知识10.2.1 读取和写入图像10.2.2 颜色空间10.2.3 显示图像及结果 10.3 处理视频10.3.1 视频输入10.3.2 将视频读取到NumPy数组中 10.4 跟踪10.4.1 光流10.4.2 Lucas-Kanade算法使用跟踪器使用发生器 10.5 更多示例…

Jmeter进行http接口测试,这一篇就搞定

jmeter-http接口测试脚本 jmeter进行http接口测试的主要步骤&#xff08;1.添加线程组 2.添加http请求 3.在http请求中写入接口的URL&#xff0c;路径&#xff0c;请求方式&#xff0c;参数 4.添加查看结果树 5.调用接口&#xff0c;查看返回值&#xff09; 针对接口添加heade…

2025年最新大数据毕业设计选题-基于Hive分析相关

选题思路 回忆学过的知识(Python、Java、Hadoop、Hive、Sqoop、Spark、算法等等。。。) 结合学过的知识确定大的方向 a. 确定技术方向&#xff0c;比如基于Hadoop、基于Hive、基于Spark 等等。。。 b. 确定业务方向&#xff0c;比如民宿分析、电商行为分析、天气分析等等。。。…

09年408考研真题解析-计算机网络

[题34]在无噪声情况下&#xff0c;若某通信链路的带宽为3kHz&#xff0c;采用4个相位&#xff0c;每个相位具有4种振幅的QAM调制技术,则该通信链路的最大数据传输速率是&#xff08;B&#xff09; A.12 kbps B.24 kbps C.48 kbps D.96 kbps 解析&#xff…

基于协同过滤+SpringBoot+Vue的剧本杀服务平台系统

作者&#xff1a;计算机学姐 开发技术&#xff1a;SpringBoot、SSM、Vue、MySQL、JSP、ElementUI、Python、小程序等&#xff0c;“文末源码”。 专栏推荐&#xff1a;前后端分离项目源码、SpringBoot项目源码、SSM项目源码 系统展示 【2025最新】基于协同过滤JavaSpringBootV…

Java 技巧 如何在IDEA2024 中快速打出System.out.println();

1.基本用法 键入sout回车 回车后变成&#xff1a; 2.打印变量 快速打印变量,以打印变量名为set为例&#xff0c;set.sout回车&#xff0c; 回车后变成

Java 每日一刊(第13期):this super static

“优秀的代码不仅仅是给机器看的&#xff0c;更是给人看的。” 前言 这里是分享 Java 相关内容的专刊&#xff0c;每日一更。 本期将为大家带来以下内容&#xff1a; this 关键字super 关键字static 关键字 this 关键字 this 关键字是 Java 中最常见的关键字之一&#xf…

pg入门18—如何使用pg gis

1. 下载postgre gis镜像 2. 运行镜像 docker run -p 15432:5432 -d -e POSTGRES_PASSWORDAb123456! postgis/postgis:12-3.4-alpine 3. 使用gis # 进入容器&#xff0c;登录pgdocker exec -it bash# 登录数据库psql -U postgres# 创建数据库CREATE DATABASE mygeotest;# 使用…

计算机毕业设计之:教学平台微信小程序(

博主介绍&#xff1a; ✌我是阿龙&#xff0c;一名专注于Java技术领域的程序员&#xff0c;全网拥有10W粉丝。作为CSDN特邀作者、博客专家、新星计划导师&#xff0c;我在计算机毕业设计开发方面积累了丰富的经验。同时&#xff0c;我也是掘金、华为云、阿里云、InfoQ等平台…

Linux —— 多线程

一、本篇重点 1.了解线程概念&#xff0c;理解线程与进程区别与联系 2.理解和学会线程控制相关的接口和操作 3.了解线程分离与线程安全的概念 4.学会线程同步。 5.学会互斥量&#xff0c;条件变量&#xff0c;posix信号量&#xff0c;以及读写锁 6.理解基于读写锁的读者写…

用 HTML + JavaScript DIY 一个渐进式延迟法定退休年龄测算器

为减轻社会和个人因退休年龄变化带来的冲击&#xff0c;近日&#xff0c;全国人民代表大会常务委员会正式发布了关于实施渐进式延迟法定退休年龄的重要决定。 根据该决定&#xff0c;我国将同步启动对男、女职工法定退休年龄的延迟计划。这一调整将采取渐进式的方式进行&#…