当通过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