Blob列SQlite Adobe AIR中的第一个字节是什么?Blob Sizeinfo



当通过Adobe AIR操作数据库时,我已经确定了插入任何blob字段的一系列随机字节。(从我的结果来看,它似乎总是以字节[12,…]开始,但我不确定)

我认为这是字节的大小信息,让我解释一下我是如何得出这个结论的。

首先是我的上下文:我通过AdobeAIR(客户端)和System.data.sqlite(C#服务器端)操作sqlite数据库

使用System.data.sqlite,如果我读到一个由Adobe AIR填充了BLOB的sqlite数据库,我必须通过AIR在开头附加这些字节,然后我就可以很好地处理二进制数据了。完美!

使用Adobe AIR,如果我试图读取由System.data.sqlite填充了BLOB的sqlite数据库,则数据已损坏,我会收到错误!很明显,因为我没有AIR研究的丢失字节。

当然,我试图通过复制我在第一种情况下删除的一系列3个字节来添加这些字节,但后来它部分返回了数据,在图像的情况下,最后一行像素都是灰色的,在一些图像中,我或多或少有灰色线。因为这些数据对应于一系列相同~4k大小的图像,我从其中一个图像中添加了3个字节,得到了这个结果。

Air有时也会抛出这个错误:

错误:错误#2030:文件结尾为遇到。

很明显,这些字节提供了关于大小的信息,但我真的不知道它是怎么回事!?!

我尝试添加一个长度为4k的字节数组,它倾向于添加3个字节,但我尝试添加4M,它最多可以添加5个字节。

我发现了这个问题:如何在C#中将3个字节转换为24位数字?我想这可能就是尺寸信息的存储方式。

但我仍然不明白…

感谢FluorineFX(AMF for.NET)开源项目,这里是答案。

因为在我的Adobe AIR项目中,我必须通过我的空气。ByteArray对象作为一个参数,用于存储sqlite Blob字段中的所有内容;AIR将所有内容序列化为AMF,这是一种紧凑的二进制操作脚本消息格式。

第11页第3.14节字节数组类型

http://opensource.adobe.com/wiki/download/attachments/1114283/amf3_spec_05_05_08.pdf

文件规定:

AMF3使用可变长度编码29位字节长度前缀的整数后跟的原始字节ByteArray。

但这还不是全部,我搜索了一个AMF.NET开源项目并创建了FluorineFX。通过查看代码,我发现每个AMF二进制文件都以字节TypeCode为前缀,对于ByteArray:,该字节TypeCode是12

   /// <summary>
   /// AMF ByteArray data type.
   /// </summary>
   public const byte ByteArray = 12;

进一步搜索,我再次在FluorineFX源中找到AMFReader.ReadAMF3ByteArray()AMFWriter.WriteByteArray()

这帮助我快速构建我需要的东西:

private static byte[] RemoveAMF3ByteArrayPrefixBytes(byte[] ar)
    {
        var ms = new MemoryStream(ar);
        var br = new BinaryReader(ms);
        // if first byte is AMF TypeCode for ByteArray
        if (br.Read() != 12)
            return ar;
        int handle = ReadAMF3IntegerData(br);
        bool inline = ((handle & 1) != 0);
        handle = handle >> 1;
        if (inline)
        {
            int length = handle;
            byte[] buffer = br.ReadBytes(length);
            return buffer;
        }
        return ar;
    }
    private static byte[] AddAMF3ByteArrayPrefixBytes(byte[] ar)
    {
        var ms = new MemoryStream();
        var bw = new BinaryWriter(ms);
        bw.Write((byte)12); // AMF TypeCode for ByteArray
        var handle = (int)ar.Length;
        handle = handle << 1;
        handle = handle | 1;
        WriteAMF3IntegerData(bw, handle);
        bw.Write(ar);
        return ms.ToArray();
    }
    /// <summary>
    /// Handle decoding of the variable-length representation which gives seven bits of value per serialized byte by using the high-order bit 
    /// of each byte as a continuation flag.
    /// </summary>
    /// <returns></returns>
    private static int ReadAMF3IntegerData(BinaryReader br)
    {
        int acc = br.ReadByte();
        if(acc < 128)
            return acc;
        else
        {
            acc = (acc & 0x7f) << 7;
            int tmp = br.ReadByte();
            if(tmp < 128)
                acc = acc | tmp;
            else
            {
                acc = (acc | tmp & 0x7f) << 7;
                tmp = br.ReadByte();
                if(tmp < 128)
                    acc = acc | tmp;
                else
                {
                    acc = (acc | tmp & 0x7f) << 8;
                    tmp = br.ReadByte();
                    acc = acc | tmp;
                }
            }
        }
        //To sign extend a value from some number of bits to a greater number of bits just copy the sign bit into all the additional bits in the new format.
        //convert/sign extend the 29bit two's complement number to 32 bit
        int mask = 1 << 28; // mask
        int r = -(acc & mask) | acc;
        return r;
        //The following variation is not portable, but on architectures that employ an 
        //arithmetic right-shift, maintaining the sign, it should be fast. 
        //s = 32 - 29;
        //r = (x << s) >> s;
    }
    private static void WriteAMF3IntegerData(BinaryWriter bw, int value)
    {
        //Sign contraction - the high order bit of the resulting value must match every bit removed from the number
        //Clear 3 bits 
        value &= 0x1fffffff;
        if (value < 0x80)
            bw.Write((byte)value);
        else
            if (value < 0x4000)
            {
                bw.Write((byte)(value >> 7 & 0x7f | 0x80));
                bw.Write((byte)(value & 0x7f));
            }
            else
                if (value < 0x200000)
                {
                    bw.Write((byte)(value >> 14 & 0x7f | 0x80));
                    bw.Write((byte)(value >> 7 & 0x7f | 0x80));
                    bw.Write((byte)(value & 0x7f));
                }
                else
                {
                    bw.Write((byte)(value >> 22 & 0x7f | 0x80));
                    bw.Write((byte)(value >> 15 & 0x7f | 0x80));
                    bw.Write((byte)(value >> 8 & 0x7f | 0x80));
                    bw.Write((byte)(value & 0xff));
                }
    }

我希望这能帮助其他人。

非常感谢,这帮我解决了这个问题。以下是如何使用Java代码:

将org.granite花岗岩核心依赖项添加到您的项目中

使用样板代码初始化GDS

GraniteConfig graniteConfig = new GraniteConfig(null, null, null, null);
ServicesConfig servicesConfig = new ServicesConfig(null, null, false);
Map<String, Object> applicationMap = new HashMap<String, Object>();
SimpleGraniteContext.createThreadIntance(graniteConfig, servicesConfig, applicationMap);

在我的示例中,我读取了一个图像文件,该文件将插入BLOB:

fis = new FileInputStream(file);
ps = connection.prepareStatement(INSERT_PICTURE);
ps.setString(1, key);
byte[] fileBytes = FileUtils.readFileToByteArray(file);
ByteArrayOutputStream out = new ByteArrayOutputStream();
AMF3Serializer ser = new AMF3Serializer(out);
ser.writeObject(fileBytes);
ser.flush();
ps.setBytes(2, out.toByteArray());

工作起来很有魅力,谢谢提示:)

Fabien

最新更新