關於Redis 二進制內容的 可視化嘗試


 二進制內容的 能否可視化?  網上的資料比較少啊!

----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

 

有時候通過 可視化工具,比如redis DesktopManager 查看 redis 的key 的值的時候,發現是 二進制內容, 如下:

 

 

 

出現二進制的內容, 絲毫不奇怪,因為我set的時候value就是key。怪的是, 為什么有的 英文字母 能夠展示出來, 其他就都是\x ,可能是 這個工具本身做了一些處理吧。  但其實不然, 如果命令行登錄進去一看,發現也是一樣的 結果:

127.0.0.1:6379[2]> get DISCUSS:TOPIC:3d28016e7cb34119aab718da2d4d1fe5::count
"\xac\xed\x00\x05sr\x00\x11java.lang.Integer\x12\xe2\xa0\xa4\xf7\x81\x878\x02\x00\x01I\x00\x05valuexr\x00\x10java.lang.Number\x86\xac\x95\x1d\x0b\x94\xe0\x8b\x02\x00\x00xp\x00\x00\x00\x00"

可以看到其中 java.lang.Integer java.lang.Number 都是 類名。

另外注意到 如果 notepad++打開一個二進制文件, 比如class文件,那么也會看到這樣的 亂碼和 英文夾雜的情況。 為什么會這樣 ?

 

寫個程序測試下:

package com.lkk;

import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.plugins.Page;
import com.lkk.ppm.discuss.domain.entity.ELComment;
import io.lettuce.core.RedisClient;
import io.lettuce.core.RedisURI;
import org.apache.commons.codec.binary.Hex;
import org.junit.Assert;
import org.junit.Test;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.connection.RedisStringCommands;
import org.springframework.data.redis.connection.lettuce.LettuceConnection;
import org.springframework.data.redis.serializer.JdkSerializationRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.util.ByteUtils;

import javax.xml.bind.DatatypeConverter;
import java.io.ByteArrayInputStream;
import java.io.FileInputStream;
import java.net.URL;
import java.nio.ByteBuffer;

/**
 * @author Administrator
 * @date 2019/9/5 0005 12:40
 * @Description
 */
public class TestRedisSerializer {

    private static final int CURRENT = 111;
    private static final int SIZE = 222;

    static RedisSerializer serial;
    static RedisSerializationContext.SerializationPair serializationPair;
    static RedisStringCommands redisStringCommands;

    static {
        RedisURI redisURI = RedisURI.create("192.168.11.200", 6380);
        redisURI.setPassword("Redis!123");
        long jedis = 100000L;
        RedisConnection connection = new LettuceConnection(jedis, RedisClient.create(redisURI));
        connection.select(2);

        redisStringCommands = connection.stringCommands();
        // 切換 序列化組件
        serial = new JdkSerializationRedisSerializer();// 默認就是 JdkSerializationRedisSerializer

//          StringRedisSerializer 只能夠序列化字符串, 不能序列化 對象。 好像沒啥用!
//       serial = new StringRedisSerializer();

        // GenericFastJsonRedisSerializer 可以將對象序列化為 json格式字符串
//        serial = new GenericFastJsonRedisSerializer();

        serializationPair = RedisSerializationContext.SerializationPair.fromSerializer(serial);
    }

    @Test
    public void testHex() throws Exception {
//        ByteUtils.getBytes(this.cacheConfig.getValueSerializationPair().write(value)
//         ByteUtils.getBytes(this.cacheConfig.getValueSerializationPair().write(value)
//        org.apache.commons.compress.utils.ByteUtils.

        String foo = "hello";
        byte[] bytes = foo.getBytes("unicode");
        bytes = foo.getBytes();
//        bytes = foo.getBytes("gb2312");

        /**
         * 雖然foo 是純英文字母,但 下面的方法都 無法將bytes 還原到上面的hello 了。。 TODO
         */
        System.out.println( Hex.encodeHexString( bytes ) );// 結果是 純16進制的內容 68656c6c6f,不帶 \ x 無法查看
        String s = DatatypeConverter.printHexBinary(bytes);;// 結果是 純16進制的內容 68656c6c6f, 無法查看
        System.out.println("s = " + s);
//        byte[] decoded = Hex.decodeHex("00A0BF");
//        System.out.println("decoded = " + new String(decoded));

        /**
         *  還原到上面的hello
         */
        dumpBytesToHex(bytes);

        // 嘗試讀取class文件
        // 當前文件
        String fileFullName = "E:\\dev\\erdp2\\erdp_discuss\\erdp_discuss_service\\target\\test-classes\\com\\lkk\\TestRedisSerializer.class";
        FileInputStream fis = new FileInputStream(fileFullName);
//        fis.getChannel()
//        ByteArrayInputStream
//        fis.read()
        int available = fis.available();
        byte[] classBytes = new byte[available];
        int read = fis.read(classBytes);

        String s1 = dumpBytesToHex(classBytes);// 除了換行符,基本上 得到了 和 notepad++ 一樣的效果。
        Assert.assertTrue(s1.contains("TestRedisSerializer"));

        ClassLoader classLoader = TestRedisSerializer.class.getClassLoader();


    }

    @Test
    public void testWrite() throws Exception {
        String key = "RESOURCE:ITEM:FILE::9fd04b83e33844b1a21ffa1c05978fc1_content.js";
//        key = "RESOURCE:ITEM:FILE::3fa4bbdff660490092ba9af971980838_template.html";
//        key = "DISCUSS:TOPIC:3d28016e7cb34119aab718da2d4d1fe5::count";
        key = "DISCUSS:TOPIC:xxx::count";
        write(key);
    }
    
    private void write(Object key) throws Exception {
        /**
         * CURRENT SIZE 測試read 要用到
         */
        Page<ELComment> page = new Page<>(CURRENT, SIZE);
        String s2 = JSONObject.toJSONString(page);
//        byte[] bytes = ByteUtils.getBytes(serializationPair.write(key));

        //如果是 JdkSerializationRedisSerializer : write 1 結果是  "\xac\xed\x00\x05sr\x00\x11java.lang.Integer\x12\xe2\xa0\xa4\xf7\x81\x878\x02\x00\x01I\x00\x05valuexr\x00\x10java.lang.Number\x86\xac\x95\x1d\x0b\x94\xe0\x8b\x02\x00\x00xp\x00\x00\x00\x01"
//        byte[] bytes = ByteUtils.getBytes(serializationPair.write(1));

        byte[] bytes = ByteUtils.getBytes(serializationPair.write(page));
        Boolean set = redisStringCommands.set(key.toString().getBytes(), bytes);
//        System.out.println("set = " + set);
//        byte[] bytes = ByteUtils.getBytes(RedisSerializationContext.SerializationPair.fromSerializer(serial).write(new String("abc阿斯蒂芬")));
//        System.out.println("bytes = " + bytes.length);
        String s = dumpBytesToHex(bytes);
        Assert.assertTrue(s.contains("com.baomidou.mybatisplus.plugins.Page"));
    }
    
    @Test
    public void testRead() throws Exception {
        String key = "DISCUSS:TOPIC:xxx::count";
        read(key);
    }

    private void read(String key) {
        byte[] bytes1 = redisStringCommands.get(key.getBytes());
        System.out.println("TestRedis.aaa");
        System.out.println("bytes1 === " + new String(bytes1));// 直接打印 二進制內容
        System.out.println("TestRedis.bbb");
        dumpBytesToHex(bytes1);// 打印 16 進制內容

        Page<ELComment> read = (Page<ELComment>) serializationPair.read(ByteBuffer.wrap(bytes1));
        System.out.println("read = " + read);
        int pages = read.getPages();
        int size = read.getSize();
        System.out.println("size = " + size);
        Assert.assertEquals(read.getSize(), SIZE);
        Assert.assertEquals(read.getCurrent(), CURRENT);
    }

    /**
     * 轉換為16進制 再打印
     * @param bytes
     * @return
     */
    public static String dumpBytesToHex(byte[] bytes) {
        String s = bytesToHex(bytes);
        System.out.println("Hex String:\n" + s);
        return s;
    }

    private static final char[] HEX_ARRAY = "0123456789ABCDEF".toCharArray();

    public static String bytesToHex66(byte[] bytes) {
        StringBuffer sb = new StringBuffer();
        char[] hexChars = new char[bytes.length * 2];
        for (int j = 0; j < bytes.length; j++) {
            byte aByte = bytes[j];
            if (aByte > 33 && aByte < 128) {
                sb.append((char) aByte);
                continue;
            }
            int v = bytes[j] & 0xFF;
            hexChars[j * 2] = HEX_ARRAY[v >>> 4];
            hexChars[j * 2 + 1] = HEX_ARRAY[v & 0x0F];

            String hex = Integer.toHexString(aByte & 0xFF);
            sb.append("\\x");
            sb.append(hex);
            if (hex.length() < 2) {
                if (aByte == 0) {
//                    continue; // Fixme ,
                } else {
                }
                sb.append(0); // Fixme ,
            }
        }
//        return new String(hexChars);
        return sb.toString();
    }

    /**
     * @param bytes
     * @return
     */
    public static String bytesToHex(byte[] bytes) {
        StringBuffer sb = new StringBuffer();
        for (int i = 0; i < bytes.length; i++) {
            byte aByte = bytes[i];

            /**
             * 這一段是做 ascii碼字符 轉換
             */
            if (aByte > 33 && aByte < 128) {
                sb.append((char) aByte);
                continue;
            }
            String hex = Integer.toHexString(aByte & 0xFF);
            sb.append("\\x");
            sb.append(hex);
            if (hex.length() < 2) {
                if (aByte == 0) {
//                    continue;
                } else {
                }
                sb.append(0);
            }
        }
        return sb.toString();
    }

}

 

Binary Viewer 查看的 結果是:

 

 

 

 

 

 

-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

總結:

二進制的內容, 可以用hex 查看/編輯器 進行查看, 一般情況下 是無法直接轉換為 字符串的!! 二進制的內容 如果 用hex 查看器查看,發現確實有字符串,那么可能就是 它本身就是 剛好被查看器支持直接展示了吧。

 

網上很多的二進制轉 16進制的 所謂工具, 其實 其結果並不是Redis 客戶端展示的結果,不是我想要的。。 因為它對 字符串的不能直接解析出來。。

 

16 進制不能閱讀, 可以嘗試轉換為ascii。 二進制其實就是內存的內容,任何文件都可以轉換為二進制。 軟件把它展示出來, 它需要按照特定的 編碼格式。文本內容是 天生可以用 文本編輯器 查看的。 其他的 , 比如 圖片, 需要圖片查看器, 依次類推。 class文件 呢?   其實也有專門的 查看器。。

 

注意到 其中 \ x 其實是一個 方便終端展示的一個 程序添加的 字符。 並不是 二進制內容自帶的。

 

StringRedisTemplate默認使用的是StringRedisSerializer, 默認只能用來存儲value類型為 string 的值。。

 

Redis 自帶的序列化器是的JdkSerializationRedisSerializer,也就是我們 生成我們 class 文件的 二進制序列化器。 其生成的結果很臃腫, 效率是比較低的!

 

 

參考:

https://www.jianshu.com/p/23f2c4c92093

https://blog.csdn.net/Eric_Blog_CSDN/article/details/78904679

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM