Java,将照片变为byte[]封装进对象进行socket传输

本文介绍了如何在Java中将照片转换为byte数组,并封装到对象中,通过Socket进行传输。在客户端,图片数据被转换为byte[],而在服务器端,可以通过查询数据库获取图片的byte[]信息,并使用setBinaryStream方法插入图片到数据库。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

阅读前可以先了解通过流的方式传输图片信息;
https://blog.youkuaiyun.com/everything002/article/details/106392551

简单说,计算机的文件都是以二进制的形式保存在电脑上,所以将图片信息转换为二进制就很容易进行操作了。

Client端:

 /*根据图片路径(String)转化成byte[]*/
    public static byte[] PicToByte(String pic_path){
        byte[] pic_data = new byte[0];
        File file = new File(pic_path);
        try {
            BufferedImage bi = ImageIO.read(file);
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            ImageIO.write(bi, "jpg", baos);
            pic_data = baos.toByteArray();
        } catch (IOException e) {
            e.printStackTrace();
        }

        return pic_data;
    }

在domain(package)中的Product(class)定义图片为byte[]类型

public class Product implements Serializable {

    private int id; //id
    private int number; //商品数量
    private String name;    //名称
    private String pinyin;	//拼音名称
    private double price;	//价格
    private String unit;	//单位
    private byte[] picData;
    private String oper;    //请求的操作
    …………
}

接收从server端发送来的数据时,通过以下方法将byte[]转换为pic
(注意:byte[]仅代表图片数据,正常情况下一张图片需要有一个名字,如:苹果;你才能确定这张图片进行下载或其他操作,所以方法至少需要两个参数)


    /*byte[]数组转换pic写入本地*/
    public static void ByteToImg(byte[] pic_data, String pic_dame){
        File file = new File("res/"+pic_dame);  //将图片缓存到项目res文件夹路径下
       // System.out.println(file);
        /*file放在第一个方便判断本地是否有该文件*/
        if(file.exists()){  //当文件存在时
            return;
        }
        else {      //文件不存在则创建
            ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(pic_data);
            try {
                BufferedImage bufferedImage = ImageIO.read(byteArrayInputStream);
                ImageIO.write(bufferedImage, "jpg", file);   //中间的参数支持:png, jpg, gif
                /*这里将图片同一转换成jpg的样子缓存————尝试将图片后缀去掉后不会自动保存成.jpg;所以需要传入名字*/

            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                try {
                    byteArrayInputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }/*ByteToImg*/

Server端:
查询所有商品时的操作( product.setPicData(rs.getBytes(“picture”)); 通过byte[]直接获取数据库图片信息)

    //查询所有商品
    public ArrayList<Product> queryAllPros(){

        ArrayList<Product> allPros = new ArrayList<Product>();
        Product product = null;

        //通过数据库查询信息
        conn = JDBCUtil.DbConnect();
        String sql = "select id, picture, number, name, pinyin, price, unit from products";
        try {
            statment = conn.prepareStatement(sql);
            rs = statment.executeQuery();
            while (rs.next()){  //默认指向第一行
                product = new Product();
                //将数据填入对象
                product.setId(rs.getInt("id"));
                product.setNumber(rs.getInt("number"));
                product.setName(rs.getString("name"));
                product.setPinyin(rs.getString("pinyin"));
                product.setPrice(rs.getDouble("price"));
                product.setUnit(rs.getString("unit"));
                product.setPicData(rs.getBytes("picture"));     //数据库blob类型的图片这里通过byte[]存储
                allPros.add(product);   //将对象添加至列表

            }/*while*/

        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            JDBCUtil.DbRelease(rs, statment, conn);
        }
        return allPros;
    }/*queryAllPros*/

插入商品时的操作
( 通过setBinaryStream插入图片
void setBinaryStream(int parameterIndex, java.io.InputStream x,
int length) throws SQLException;)

    //添加商品
    public void addPro(Product product) {
        //通过数据库查询信息
        conn = JDBCUtil.DbConnect();
        String sql = "insert into products(id, picture, number, name, pinyin, price, unit) values(?, ?, ?, ?, ?, ?, ?)";
        try {
            statment = conn.prepareStatement(sql);
            statment.setInt(1, product.getId());

            InputStream picInput = new ByteArrayInputStream(product.getPicData());
            statment.setBinaryStream(2, picInput, picInput.available());    //picInput.available()获取长度
            statment.setInt(3, product.getNumber());
            statment.setString(4, product.getName());
            statment.setString(5, product.getPinyin());
            statment.setDouble(6, product.getPrice());
            statment.setString(7, product.getUnit());

            statment.executeUpdate();
        } catch (SQLException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            JDBCUtil.DbRelease(rs, statment, conn);
        }
    }/*addPro*/
<think>首先,用户的问题是:“我想将实际数据转换为报文传输格式的技术实现 请问如何将实际数据转换为报文传输格式 数据序列化 报文封装”。这涉及到数据序列化和报文封装的技术。 关键点: - **数据序列化**:将数据结构或对象状态转换为可存储或传输的格式。 - **报文封装**:将序列化后的数据包装网络传输的报文格式,包括头部信息(如长度、类型等)和尾部信息。 用户还提到了“实际数据转换为报文传输格式”,所以我需要解释整个过程。 参考系统级指令: - 所有行内数学表达式用$...$,独立公式用$$...$$。 - 使用中文回答。 - 生相关问题。 - 回答结构清晰,帮助用户逐步解决问题。 - 引用站内引用时添加引用标识。 站内引用提供了信息: - 引用[1]:关于表单提交的enctype属性,暗示了数据编码方式(如multipart/form-data)。 - 引用[2]:讨论了解决粘包和拆包问题的方案,使用数据长度前缀(如MsgProto类),这与报文封装相关。 用户的问题核心是技术实现,所以我应该基于这些引用构建回答。 回答结构: 1. **介绍概念**:简要解释数据序列化和报文封装。 2. **步骤分解**: - 数据序列化:使用序列化库(如JSON、XML、Protobuf)。 - 报文封装:添加头部信息(如长度字段)。 3. **技术实现**:给出代码示例(如Python或Java),因为用户提到了数据序列化和报文封装。 4. **引用相关部分**:自然地融入引用[2]的内容,因为它直接涉及报文长度前缀。 5. **生相关问题**:在末尾添加相关问题。 代码示例: - 使用Python,因为它通用。展示序列化和封装。 - 序列化:用json.dumps()。 - 封装:添加长度前缀。 数学表达式:如果涉及公式,比如计算长度,用$...$格式。例如,报文长度可以是$L = \text{len(data)}$。 最终输出格式: - 回答用户问题。 - §§相关问题§§部分。 - 引用标识:[^1], [^2]。 完整回答大纲: 1. 引言:解释数据到报文的转换过程。 2. 数据序列化:定义和常见方法。 3. 报文封装:定义和如何添加头部。 4. 完整实现示例:代码演示。 5. 总结。 6. 相关问题。</think>### 将实际数据转换为报文传输格式的技术实现 在数据传输中,将实际数据转换为报文传输格式涉及两个核心步骤:**数据序列化**和**报文封装**。数据序列化将结构化数据(如对象、数组)转换为字节流或字符串,以便于传输;报文封装则添加必要的元数据(如长度、类型、分隔符),确保数据在传输过程中能被正确解析。下面我将逐步解释这一过程,并提供具体实现示例。整个过程基于解决粘包和拆包问题的方案,例如使用数据长度前缀[^2]。 #### 步骤1: 数据序列化 数据序列化是将实际数据(如字典、列表、对象)转换为可传输的格式(如字节流)。常见序列化格式包括: - **JSON**: 轻量级、易读,适合Web应用。 - **XML**: 结构化强,但体积较大。 - **Protobuf (Protocol Buffers)**: 高效二制格式,适合高性能场景。 - **MessagePack**: 二制JSON替代品,压缩率高。 序列化过程确保数据可逆(即可反序列化)。例如,在Python中使用JSON序列化: ```python import json # 实际数据示例:一个包含用户信息的字典 data = {"id": 1, "name": "Alice", "age": 30} # 序列化:将字典转换为JSON字符串 serialized_data = json.dumps(data) # 输出: '{"id": 1, "name": "Alice", "age": 30}' # 序列化后的数据可一步转换为字节流 byte_data = serialized_data.encode('utf-8') # 转换为字节 ``` 序列化后,数据变为字节流,但直接传输可能因网络问题导致粘包(多个报文粘连)或拆包(一个报文被拆分)。因此,需要报文封装。 #### 步骤2: 报文封装 报文封装添加元数据到序列化数据上,形完整报文。关键元素包括: - **长度字段**: 指定数据内容的字节长度,解决粘包/拆包问题[^2]。长度字段通常用固定字节数(如4字节整数)表示,记为$L$,其中$L = \text{len(data)}$。 - **分隔符或类型字段**: 可选,用于标识报文类型(如用特定字符分隔)。 - **校验字段**: 如CRC校验,确保数据完整性。 引用[^2]中提到的`MsgProto`类方案,就是通过长度字段封装报文: ```python def create_packet(data_bytes): # 计算数据长度 (4字节整数表示) length = len(data_bytes) # 封装报文: 长度字段 + 数据内容 packet = length.to_bytes(4, byteorder='big') + data_bytes # byteorder='big'表示大端序 return packet # 示例: 封装序列化后的字节数据 packet = create_packet(byte_data) print(packet) # 输出类似: b'\x00\x00\x00\x1f{"id": 1, "name": "Alice", "age": 30}' ``` 这里,`length.to_bytes(4, byteorder='big')`将长度转换为4字节大端序格式,与数据内容拼接。接收方先读取4字节长度$L$,再读取$L$字节数据,确保无粘包/拆包[^2]。 #### 完整实现示例 以下是一个端到端的Python示例,展示从实际数据到报文传输的转换: ```python import json import socket def serialize_data(data): """序列化数据为JSON字节流""" return json.dumps(data).encode('utf-8') def create_packet(data_bytes): """报文封装: 添加4字节长度前缀""" length = len(data_bytes) return length.to_bytes(4, byteorder='big') + data_bytes def send_packet(socket_conn, data): """发送报文到网络""" serialized_data = serialize_data(data) packet = create_packet(serialized_data) socket_conn.sendall(packet) # 发送完整报文 # 示例用法 if __name__ == "__main__": # 实际数据 user_data = {"id": 1, "name": "Alice", "age": 30} # 创建socket连接 (示例中省略了服务器端代码) sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.connect(('localhost', 8080)) # 转换为报文并发送 send_packet(sock, user_data) sock.close() ``` 接收方实现: 1. 先读取4字节长度$L$。 2. 再读取$L$字节数据。 3. 反序列化数据(如`json.loads()`)。 这种方法高效可靠,适合HTTP、TCP等协议[^2]。 #### 关键注意事项 - **性能优化**: 对于大数据,使用二制序列化(如Protobuf)减少体积。Protobuf的序列化速度比JSON快$2\times$以上。 - **安全性**: 添加校验字段(如CRC32),防止传输错误。 - **适用场景**: 表单提交常用`enctype`属性指定编码(如`multipart/form-data`),但报文封装更通用[^1]。 - **数学基础**: 长度字段确保报文边界清晰,避免因网络延迟导致的粘包问题[^2]。 通过以上步骤,您可以高效地将实际数据转换为报文格式,确保可靠传输
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值