sqlite boolean
¿Cuáles son los primeros bytes en una columna de Blob SQlite Adobe AIR? Blob Sizeinfo? (2)
He identificado una serie aleatoria de bytes insertados en cualquier campo blob cuando la base de datos se manipula a través de Adobe AIR. (De mis resultados parece que siempre comienza con bytes [12, ...] pero no estoy seguro de eso)
Creo que es una información de tamaño de los bytes, déjame explicarte cómo llegué a esta conclusión.
Primero mi contexto: manipulo las bases de datos sqlite a través de Adobe AIR (lado del cliente) y System.data.sqlite (lado del servidor C #)
Con System.data.sqlite si leo un archivo Sqlite db lleno de BLOB por Adobe AIR, tengo que obtener esos bytes añadidos al principio por AIR y luego tengo todos los datos binarios bien formados. ¡PERFECTO!
Con Adobe AIR, si traté de leer un archivo sqlite db lleno de BLOB por System.data.Sqlite, los datos están corruptos. ¡Recibo un error! Obviamente porque no tengo los bytes perdidos investigados por AIR.
Por supuesto, traté de agregar esos bytes copiando una serie de 3 bytes que eliminé en mi primer caso, pero luego devolvió los datos parcialmente y en los casos de imágenes, las últimas filas de píxeles se volvieron todas grises y en algunas imágenes tengo más o menos líneas grises. Debido a que los datos correspondían a una serie de imágenes del mismo tamaño ~ 4k, agregué 3 bytes de uno de ellos y obtuve este resultado.
Y Air también lanzará a veces este error:
Error: Error # 2030: se encontró el final del archivo.
Entonces, obviamente, esos bytes brindan información sobre el tamaño pero realmente no entiendo cómo funciona.
Traté de agregar un bytearray con 4k de longitud, tiende a agregar 3 bytes, pero traté de agregar 4M y sube a 5 bytes.
Encontré esta pregunta ¿Cómo se convierten 3 bytes en un número de 24 bits en C #? y pensé que podría ser cómo se almacena la información de tamaño.
Pero todavía no lo entiendo ...
Gracias a FluorineFX (AMF for .NET) el proyecto de código abierto aquí es la respuesta.
Porque en mi proyecto Adobe AIR tengo que pasar mi objeto air.ByteArray como parámetro para almacenar todo en el campo sqlite Blob; AIR serializará todo en AMF, un formato compacto de mensaje de acción y binario.
Página 11 Secion 3.14 tipo ByteArray
http://opensource.adobe.com/wiki/download/attachments/1114283/amf3_spec_05_05_08.pdf
El documento estipula:
AMF 3 serializa este tipo utilizando un entero de 29 bits de codificación de longitud variable para el prefijo de longitud de byte seguido de los bytes sin formato de ByteArray.
Pero eso no es todo, busqué un proyecto open source AMF .NET y fundé FluorineFX. Al examinar el código, identifiqué que cada binario AMF está prefijado con un byte TypeCode que es 12 para un ByteArray:
/// <summary>
/// AMF ByteArray data type.
/// </summary>
public const byte ByteArray = 12;
Más búsqueda y nuevamente encontré en las fuentes de FluorineFX AMFReader.ReadAMF3ByteArray () y AMFWriter.WriteByteArray ()
Que me ayudan a construir rápidamente lo que necesito:
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));
}
}
Espero que eso ayude a alguien más.
Muchas gracias, eso me ayudó a resolver el problema. Aquí es cómo hacerlo con código Java:
Agregue la dependencia org.granite granite-core a su proyecto
GDS Init con código repetitivo
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);
En mi ejemplo, leo un archivo de imagen que insertaré en un 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());
Funciona como un encanto, gracias por la sugerencia :)
Fabien