ThriftRPC——二进制文件传输

       公司最近有个新的需求,需要和设备之间进行文件传输,字符型文件传输很好解决,但是二进制类型的文件传输存在比较多的坑,现在将存在的坑进行归纳整理:

       注意:阅读这篇文章的读者需要自行去了解thritrpc的相关知识,这里我们不做过多的介绍。

1. 准备工作

        文件格式分别为:py和bit两种文件,其中py文件较小,是一个类xml类型的文件,而bit则为一个纯二进制的文件

        因为涉及到的文件不多,我们这里使用mysql进行文件存储,这里我们遇到第一个坑,mysql的value存储上限

        这里我使用的是mysql 5.7 版本,在my.ini配置文件中我们可以看到如下的配置:

        max_allowed_packet = 4M

        这个4M是默认的上限,因为文件大小很有可能超过4M,这里我们根据文件的大小将这个值设置为自己需要的大小,具体的设置方式网上有很多资料,这里我们不加赘述;

       这里存储字段的存储类型我选择mediumblob,与其相关的字段类型如下,开发人员可以根据自己的需求选择合适的字段类型。

        ①TinyBlob类型  最大能容纳255B的数据

        ②Blob类型  最大能容纳65KB的

        ③MediumBlob类型  最大能容纳16MB的数据

        ④LongBlob类型  最大能容纳4GB的数据

2. thriftRPC描述文件设置:

struct ReqWaveLoad{
	1:string verFlag,
	2:i16 dataType,
	3:string ip,
	4:i32    waveId,
	5:string waveName,
	6:string waveXml,    //字符型文件我们可以使用string类型直接传输,亲测没有问题
	7:binary waveFpga    //二进制类型文件需要使用binary类型传输
}

        从上面的配置我们可以看到,我们在传输文件的时候针对不同类型的文件的选择方式,爬坑之路由此开始

3. 爬坑1——java中byte[]和String

        最开始,因为mysql blob类型对应的java字段类型为byte[],为了传输的准确性,我决定将两个文件的byte[]都转换为String类型进行传输,此时因为文件中内容的不一样,二进制文件出现问题:

        存入数据库的文件差不多3.9M,而转换成String类型后,写入到本地的文件大小变为4.5M

        因为二进制文件没有办法打开,我将生成的文件放到设备时运行,确认文件中内容无法正常使用,经过多番测试,本人认为应该是String在转换二进制文件内容时,因为编码的问题导致文件乱码,但此结论有待进一步验证。

        基于此原因,如果你是传输字符型文件(一般编辑器都能打开,可以正常显示),那么此方式没有任何的问题,

 

4. 爬坑2——byte[]和json

       在我将数据类型转换为binary时,在生成的thriftRPC文件中我们看到的是,waveFpga 对应的数据类型为ByteBuffer,这个数据类型用的比较少,同时我发现ByteBuffer类型的数据在json中并不被支持,发送的数据接受方没有办法解析,应对方式有两种:

        - 在JSON中添加ByteBuffer的序列化方式,实现ByteBuffer类型的序列化和反序列化

        - 将从数据库中拿出来的byte数组转换为String类型,然后传递到接收方

  public String verFlag; // required
  public short dataType; // required
  public String ip; // required
  public int waveId; // required
  public String waveName; // required
  public String waveXml; // required
  public ByteBuffer waveFpga; // required

4.1 序列化方式

      参考博客:https://blog.csdn.net/10km/article/details/78151366

       首先添加序列化工具类

package com.hjkj.nms_satellitecom_manager.util;

import com.alibaba.fastjson.JSONException;
import com.alibaba.fastjson.parser.DefaultJSONParser;
import com.alibaba.fastjson.parser.JSONToken;
import com.alibaba.fastjson.parser.deserializer.ObjectDeserializer;
import com.alibaba.fastjson.serializer.JSONSerializer;
import com.alibaba.fastjson.serializer.ObjectSerializer;
import com.alibaba.fastjson.serializer.PrimitiveArraySerializer;
import com.alibaba.fastjson.serializer.SerializerFeature;

import java.io.IOException;
import java.lang.reflect.Type;
import java.nio.ByteBuffer;

public class ByteBufferCodec implements ObjectSerializer, ObjectDeserializer {
    public static ByteBufferCodec instance = new ByteBufferCodec();
    @SuppressWarnings("unchecked")
    @Override
    public <T> T deserialze(DefaultJSONParser parser, Type type, Object fieldName) {
        int token = parser.lexer.token();
        if (token == JSONToken.NULL) {
            parser.lexer.nextToken();
            return null;
        } else if(token == JSONToken.HEX  || token == JSONToken.LITERAL_STRING){
            byte[] bytes = parser.lexer.bytesValue();
            parser.lexer.nextToken();
            return (T) ByteBuffer.wrap(bytes);
        }
        throw new JSONException(String.format("invalid '%s' for ByteBuffer",JSONToken.name(token)));
    }

    @Override
    public int getFastMatchToken() {
        return JSONToken.LITERAL_STRING;
    }
    /**
     * 返回buffer中所有字节(position~limit),不改变buffer状态
     * @param buffer
     * @return
     */
    private static final byte[] getAllBytesInBuffer(ByteBuffer buffer){
        int pos = buffer.position();
        try{
            byte[] bytes = new byte[buffer.remaining()];
            buffer.get(bytes);
            return bytes;
        }finally{
            buffer.position(pos);
        }
    }
    /**
     * 直接引用{@link PrimitiveArraySerializer}实现序列化
     */
    @Override
    public void write(JSONSerializer serializer, Object object, Object fieldName, Type fieldType, int features)
            throws IOException {
        if ( (object instanceof ByteBuffer) ) {
            PrimitiveArraySerializer.instance.write(serializer, getAllBytesInBuffer((ByteBuffer)object), fieldName, fieldType, features);
        }else{
            serializer.out.writeNull(SerializerFeature.WriteNullListAsEmpty);
        }

    }
}

        而后看使用:

    @Test
    public void test() {
        ByteBuffer byteBuffer =ByteBuffer.wrap(new byte[]{22,33,3,2,3,1,5,-1});
        // 修改ParserConfig.global全局变量,将ByteBufferCodec加入反序列化器容器
        ParserConfig.global.putDeserializer(ByteBuffer.class, ByteBufferCodec.instance);
        // 修改SerializeConfig.globalInstance全局变量,将ByteBufferCodec加入序列化器容器
        // 对应的Class为 java.nio.HeapByteBuffer
        SerializeConfig.globalInstance.put(ByteBuffer.wrap(new byte[]{}).getClass(), ByteBufferCodec.instance);
        // 待序列化的 ByteBuffer 对象
        ByteBuffer byteBuffer =ByteBuffer.wrap(new byte[]{22,33,3,2,3,1,5,-1});
        // 序列化
        String serString = JSON.toJSONString(group.byteBuffer);
        System.out.println(serString);
        // 反序列化
        ByteBuffer deserialedByteBuffer = JSON.parseObject(serString,ByteBuffer.class);
        System.out.println(JSON.toJSONString(deserialedByteBuffer));
    }

4.2 使用byte数组转String

      这里涉及到的主要是将byte数组转换为基于base64的String类型,因为base64无编码形式的

//byte[]转String
Base64.encodeToString(wave.getBitValue());    //wave.getBitValue()得到的是一个byte[]
//String转byte[]
new BASE64Decoder().decodeBuffer("获取到的String类型的数据");

5. 爬坑3——thrift中的ByteBuffer

        最开始我给ByteBuffer赋值是基于javabean的常规形式赋值的,但是出现在测试中发现,数据并没有被添加到此字段中,我 获取的数据长度一直为0,查看 生成的javabean发现如下代码:

  public byte[] getWaveFpga() {
    setWaveFpga(org.apache.thrift.TBaseHelper.rightSize(waveFpga));
    return waveFpga == null ? null : waveFpga.array();
  }

  public ByteBuffer bufferForWaveFpga() {
    return waveFpga;
  }

  public ReqWaveLoad setWaveFpga(byte[] waveFpga) {
    setWaveFpga(waveFpga == null ? (ByteBuffer)null : ByteBuffer.wrap(waveFpga));
    return this;
  }

  public ReqWaveLoad setWaveFpga(ByteBuffer waveFpga) {
    this.waveFpga = waveFpga;
    return this;
  }

      可以看到的是,setWaveFpga有一个重载的方法,其参数可以是ByteBuffer类型也可以是byte数组类型,理论上来讲,使用这两个方法都可以拿到对应的数据,但是事实上这两种方法单纯的去用来赋值都不能成功,其内部机制和常规的javabean存在不同,首先,返回值就不同,正常的set方法的返回值都是void,但是这个返回的是当前的对象,因为其是未编码的字节流,thrift内部处理方式可能不同,目前还不知道具体原因,有待日后进行调整

       那么我们如何解决这个问题呢?最终测试,我们采用如下方法可以成功的为变量赋值:

 ReqWaveLoad reqWave = new ReqWaveLoad();
 reqWave.waveFpga = ByteBuffer.wrap(waveFpga);;
 reqWave.setDataType(ThriftCMD.THRIFT_EQUIPMENT_WAVE_LOAD);
 reqWave.setVerFlag(ThriftSignal.THRIFT_PROTOCOL_VERSION);
 reqWave.setWaveName(waveName);
 reqWave.setWaveId(waveId);
 reqWave.setIp(ip);
 reqWave.setWaveXml(waveXml);

      经过测试后,使用此方式问题解决!!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值