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

news2024/9/21 22:50:29

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 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);
        }
    }
}

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

代码仓库

  • 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.coloradmin.cn/o/2153515.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

fastadmin 部署后前台会员中心出现404错误

访问前台会员中心出现404错误。 解决&#xff1a;nginx访问站点增加伪静态 location / {if (!-e $request_filename){rewrite ^(.*)$ /index.php?s$1 last; break;} }在phpstydy中增加伪静态&#xff0c;如图&#xff1a;

保姆级教程教你如何安装Hadoop,实现Hadoop单机(非分布式)配置

下载Ubutu镜像 前往阿里镜像站https://mirrors.aliyun.com/oldubuntu-releases/releases/16.04.0/?spma2c6h.25603864.0.0.6be57ff3u2zMGR 选择下载ubuntu-16.04-desktop-amd64.iso 下载时间较久&#xff0c;请耐心等待 创建实验用虚拟机 选择创建新的虚拟机 选择典型并下一…

伊犁-linux 硬盘添加,分区,格式化

主要是linux 下操作硬盘分区&#xff0c;格式化 这样1个sata 盘就添加成功了 &#xff01;  继续添加三块 sata1 hda sata hdb sata hdc sata hdd scsi sda 作为启动盘 进行操作系统的引导 如果scsi 往下调整 先敲enter 在用&#xff0d; 号往下 如果是往上调整敲…

win7自带壁纸丢失主题丢失

有时候盗版破解或者其他美化工具会导致win7自带的壁纸丢失&#xff0c;从个性化管理里面无法恢复原始的壁纸&#xff08;如下图&#xff09;&#xff0c;但是由于工作原因公司的电脑又不方便设置第三方的壁纸&#xff0c;所以找了一下解决方案。 经典问题&#xff0c;百度找到的…

华为全联接大会HUAWEI Connect 2024印象(二):昇腾AI端侧推理

此次参加HUAWEI Connect 2024最主要目标是了解昇腾AI端侧推理技术&#xff0c;希望将其融合到我现在嵌入式系统课程中&#xff0c;不过刚开始在一楼找到一个小展台&#xff0c;看到了香橙派Orange Pi。香橙派是深圳迅龙的一个品牌&#xff0c;他们和很多芯片厂商都合作过&#…

创建Application(Qt)模板项目时的 Base class选择

在Qt中&#xff0c;当你使用Qt Creator新建一个Qt Widgets Application项目时&#xff0c;选择Base class是一个重要的步骤&#xff0c;因为它决定了你的主窗口或对话框将继承自哪个类&#xff0c;从而决定了你的应用程序将具有哪些基本的功能和外观。以下是一些常见的Base cla…

毕业设计选题:基于ssm+vue+uniapp的校园商铺系统小程序

开发语言&#xff1a;Java框架&#xff1a;ssmuniappJDK版本&#xff1a;JDK1.8服务器&#xff1a;tomcat7数据库&#xff1a;mysql 5.7&#xff08;一定要5.7版本&#xff09;数据库工具&#xff1a;Navicat11开发软件&#xff1a;eclipse/myeclipse/ideaMaven包&#xff1a;M…

动手学深度学习(李沐)PyTorch 第 1 章 引言

在线电子书 深度学习介绍 安装 使用conda环境 conda create -n d2l-zh python3.8 pip安装需要的包 pip install jupyter d2l torch torchvision下载代码并执行 wget https://zh-v2.d2l.ai/d2l-zh.zip unzip d2l-zh.zip jupyter notebookpip install rise如果不想使用jupyt…

NAT和代理服务

文章目录 NAT和代理服务1、NAT技术背景2、NAT 技术 IP 转换过程3、NAPT4、NAT 技术的缺陷5、代理服务器5.1、正向代理5.1.1、概念5.1.2、工作原理5.1.3、功能特点5.1.4、应用场景 5.2、反向代理5.2.1、概念5.2.2、工作原理5.2.3、应用场景 6、NAT 和代理服务器 NAT和代理服务 …

实景三维+耕地保护:构建耕地资源管理的全闭环新模式

在耕地资源日益珍贵的今天&#xff0c;如何高效、精准地实施耕地保护&#xff0c;成为了我国农业可持续发展与生态文明建设的关键课题。“实景三维耕地保护”的创新模式&#xff0c;能够为这一挑战提供突破性的解决方案&#xff0c;打造一个从前端监测到后端管理的全闭环耕地保…

Global Attention Decoder for Chinese Spelling Error Correction(ACL2021)

Global Attention Decoder for Chinese Spelling Error Correction(ACL2021) 一.概述 作者认为现有的纠错方法大多是基于局部上下文信息进行纠错&#xff0c;没有考虑句子中错词的影响。将注意力放在错误上下文信息上可能会误导并降低CSC(Chinese Spelling Correction)的整体性…

【计算机网络】网络层协议解析

网络层的两种服务IPv4分类编址划分子网无分类地址 IPv4地址应用IP数据报的发送和转发过程主机发送IP数据报路由器转发IP数据报 IPv4数据报首部格式ICMP网际控制报文协议虚拟专用网VPN与网络地址转换NAT 网络层主要任务是实现网络互连&#xff0c;进而实现数据包在各网络之间的传…

快速掌握Matlab R2016a安装,就是这么简单

Matlab R2016a下载方法&#xff1a; Matlab R2016a安装教程&#xff1a; 1、右击下载好的压缩包&#xff0c;选择解压到Matlab R2016a 2、打开文件夹【R2016a_win64】&#xff0c;右击下面的setup.exe&#xff0c;选择【以管理员身份运行】 3、点击选择【使用文件安装密钥】&a…

2024华为杯研究生数学建模,代码思路和参考文章

F题X射线脉冲星光子到达时间建模&#xff0c; E题高速公路应急车道紧急启用模型&#xff0c; D题大数据驱动的地理综合问題&#xff0c; C题数据驱动下磁性元件的磁芯损耗建模&#xff0c; B题W LAN 组网中网络吞吐量建模&#xff0c; A题风电场有功功率优化分配&#xff…

ProtoBuf序列化框架介绍

文章目录 ProtoBuf介绍使用流程 QUICK START创建.proto文件注释语法编译部分代码展示使用接口运行结果 ProtoBuf介绍 ProtoBuf全称是Protocol Buffer&#xff0c;是一个数据结构的序列化和反序列化框架 他又很多好处&#xff0c;首先是他支持跨平台&#xff0c;支持Java、C、…

【Geoserver使用】启动方法与端口、跨域配置

文章目录 前言一、Windows和Linux下的启动方式1.Windows2.Linux 二、启动端口修改三、跨域配置总结 前言 之前写了一篇Geoserver下载页的文章&#xff0c; 【Geoserver使用】Geoserver下载页介绍&#xff08;稳定版维护版开发版历史版本&#xff09; 今天讲一下Geoserver启动方…

Hadoop的安装和使用

1. Hadoop简介 Hadoop是一个能够对大量数据进行分布式处理的软件框架&#xff0c;并且是以一种可靠、高效、可伸缩的方式进行处理的&#xff0c;它具有以下几个方面的特性。 高可靠性。高效性。高可扩展性。高容错性。成本低。运行在Linux平台上。支持多种编程语言。 2. 分布…

【Linux课程学习】make/Makefile:Linux项目自动化构建工具

&#x1f381;个人主页&#xff1a;我们的五年 &#x1f50d;系列专栏&#xff1a;Linux课程学习 &#x1f337;追光的人&#xff0c;终会万丈光芒 &#x1f389;欢迎大家点赞&#x1f44d;评论&#x1f4dd;收藏⭐文章 &#x1f349;一.make/Makefile的理解&#xff1a; …

【我的 PWN 学习手札】House Of Karui —— tcache key 绕过手法

目录 前言 一、House of Karui 二、测试与模板 前言 早期版本的 tcachebin 由于毫无保护&#xff0c;导致攻击利用非常容易&#xff0c;成为重灾区。tcache dup&#xff0c;也即 tcachebin 中的 double free 利用手法&#xff0c;是攻击者常常选用的攻击方式。然而&#xf…

直通滤波-PassThrough Filter-原理-代码实现

前言 对坐标轴上的上下限进行约束&#xff0c;选取其中符合范围的点云区域使用场景&#xff1a;去除噪声点&#xff0c;关注特定区域&#xff0c;减小计算量 工作流程 假设我们要在 d d d 轴&#xff08; d ∈ { x , y , z } d \in \{x, y, z\} d∈{x,y,z} &#xff09;上…