Pregunta

Recibo algunos archivos XML con imágenes codificados en base64 incrustados, que necesito para decodificar y guardar como archivos.

Un ejemplo no modificada (que no sea en zip) de dicho archivo se puede descargar a continuación:

20091123-125320.zip (60KB)

Sin embargo, consigo errores como "Longitud no válida para una matriz de caracteres Base-64" y "Carácter no válido en una cadena Base-64". Me marcó la línea en el código donde se produce el error en el código.

Un archivo podría tener este aspecto:

<?xml version="1.0" encoding="windows-1252"?>
<mediafiles>
    <media media-type="image">
      <media-reference mime-type="image/jpeg"/>
      <media-object encoding="base64"><![CDATA[/9j/4AAQ[...snip...]P4Vm9zOR//Z=]]></media-object>
      <media.caption>What up</media.caption>
    </media>
</mediafiles>

Y el código para procesar la siguiente manera:

var xd = new XmlDocument();
xd.Load(filename);
var nodes = xd.GetElementsByTagName("media");

foreach (XmlNode node in nodes)
        {
            var mediaObjectNode = node.SelectSingleNode("media-object");
            //The line below is where the errors occur
            byte[] imageBytes = Convert.FromBase64String(mediaObjectNode.InnerText);
            //Do stuff with the bytearray to save the image
        }

El XML de datos es un sistema periódico de la empresa, así que estoy bastante seguro de que los archivos están bien - y tiene que haber algo en la forma de procesarlos, que es simplemente incorrecto. Tal vez un problema con la codificación?

He tratado de escribir el contenido de mediaObjectNode.InnerText, y es los datos codificados en base64 - por lo que la navegación por el xml-doc no es el tema

.

He estado buscando en Google, los atracones, stackoverflowing y llorando - y no encontró ninguna solución ... Help

Editar:

Se ha añadido un archivo de ejemplo real (y una recompensa). Tenga en cuenta el archivo descargable está en un diferente esquema de bits, ya que he simplificado que en el ejemplo anterior, la eliminación de material irrelevante ...

¿Fue útil?

Solución

En un primer disparo no me usar cualquier lenguaje de programación, simplemente Notepad ++

Abrí el archivo XML dentro y copiar y pegar el contenido de base 64 en bruto en un nuevo archivo (sin corchetes).

Después me seleccionaron todo (Ctrl-A) y utiliza las extensiones de opciones - Mime - Herramientas de decodificación Base64. Esto arrojó un error sobre la longitud del texto incorrecto (debe ser mod 4). Así que acaba de agregar dos signos de igual ( '=') como marcador de posición en el extremo para obtener la longitud correcta.

Otra reintento y se decodifica con éxito en 'algo'. Sólo tiene que guardar el archivo como .jpg y se abre como un encanto en cualquier visor de imágenes.

Así que yo diría, hay algo mal con los datos que se obtiene. Ellos simplemente no tienen el número correcto de los signos igual al final para llenar a una serie de señales que se pueden romper en paquetes de 4.

La forma 'fácil' sería añadir el signo igual hasta que la decodificación no generará un error. La mejor manera sería para contar el número de caracteres (CR / menos LFS!) Y añadir los necesitados en un solo paso.

Otras investigaciones

Después de una cierta codificación y lectura de la función de conversión , el problema es una fijación incorrecta de un signo igual desde el productor. ++ Bloc de notas no tiene problemas con toneladas de signos iguales, pero la función de conversión de MS sólo funciona con cero, uno o dos signos. Así que si de llenar el uno ya existente con signos iguales adicionales obtiene un error también! Para obtener esta maldita cosa que trabajar, hay que cortar todas las señales existentes, calcular cuánto se necesita y añadirlos de nuevo.

Sólo por la generosidad, aquí está mi código (no absoluta perfecta, pero suficiente para un buen punto de partida):; -)

    static void Main(string[] args)
    {
        var elements = XElement
            .Load("test.xml")
            .XPathSelectElements("//media/media-object[@encoding='base64']");
        foreach (XElement element in elements)
        {
            var image = AnotherDecode64(element.Value);
        }
    }

    static byte[] AnotherDecode64(string base64Decoded)
    {
        string temp = base64Decoded.TrimEnd('=');
        int asciiChars = temp.Length - temp.Count(c => Char.IsWhiteSpace(c));
        switch (asciiChars % 4)
        {
            case 1:
                //This would always produce an exception!!
                //Regardless what (or what not) you attach to your string!
                //Better would be some kind of throw new Exception()
                return new byte[0];
            case 0:
                asciiChars = 0;
                break;
            case 2:
                asciiChars = 2;
                break;
            case 3:
                asciiChars = 1;
                break;
        }
        temp += new String('=', asciiChars);

        return Convert.FromBase64String(temp);
    }

Otros consejos

La cadena base64 no es válido como Oliver ya ha dicho, la longitud de cadena debe ser múltiplos de 4 después de la eliminación de caracteres de espacio en blanco. Si nos fijamos en continuación, final de la cadena base 64 (véase más adelante), verá la línea es más corta que el resto.

RRRRRRRRRRRRRRRRRRRRRRRRRRRRX//Z=

Si se quita esta línea, el programa va a funcionar, pero la imagen resultante tendrá una sección que falta en la esquina inferior derecha. Es necesario para rellenar esta línea por lo que la longitud total de cadena es corecta. De mis cálculos si tuviera 3 caracteres que debería funcionar.

RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRX//Z=

quitar 2 últimos caracteres mientras que la imagen no recibe adecuada

public Image Base64ToImage(string base64String)
    {
        // Convert Base64 String to byte[]
        byte[] imageBytes=null;
        bool iscatch=true;
        while(iscatch)
        {
            try 
                {           
         imageBytes = Convert.FromBase64String(base64String);
         iscatch = false;

            }
            catch 
            {
                int length=base64String.Length;
                base64String=base64String.Substring(0,length-2);
            }
        }
        MemoryStream ms = new MemoryStream(imageBytes, 0,
          imageBytes.Length);

        // Convert byte[] to Image
        ms.Write(imageBytes, 0, imageBytes.Length);
        Image image = Image.FromStream(ms, true);
        pictureBox1.Image = image;
        return image;
    }

Trate de usar LINQ to XML:

using System.Xml.XPath;

class Program
{
    static void Main(string[] args)
    {
        var elements = XElement
            .Load("test.xml")
            .XPathSelectElements("//media/media-object[@encoding='base64']");
        foreach (var element in elements)
        {
            byte[] image = Convert.FromBase64String(element.Value);
        }
    }
}

ACTUALIZACIÓN:

Después de descargar el archivo XML y analizar el valor del nodo media-object está claro que no es una cadena base64 válida:

string value = "PUT HERE THE BASE64 STRING FROM THE XML WITHOUT THE NEW LINES";
byte[] image = Convert.FromBase64String(value);

lanza una System.FormatException diciendo que la longitud no es una cadena válida de base 64. Evento cuando quito la \n de la cadena no funciona:

var elements = XElement
    .Load("20091123-125320.xml")
    .XPathSelectElements("//media/media-object[@encoding='base64']");
foreach (var element in elements)
{
    string value = element.Value.Replace("\n", "");
    byte[] image = Convert.FromBase64String(value);
}

También tiros System.FormatException.

También he tenido un problema con la base 64 de decodificación cadena de documento XML (documento específicamente paquete de Office OpenXML) codificados.

Resultó que la cadena había aplicado codificación adicional: la codificación HTML, por lo que hacer primero decodificación HTML y luego decodificación Base64 hizo el truco:

private static byte[] DecodeHtmlBase64String(string value)
{
    return System.Convert.FromBase64String(System.Net.WebUtility.HtmlDecode(value));
}

Sólo en caso de que alguien se tropieza en la misma edición.

Bueno, es todo muy sencillo. CDATA es un nodo en sí, por lo mediaObjectNode.InnerText produce realmente <![CDATA[/9j/4AAQ[...snip...]P4Vm9zOR//Z=]]>, que obviamente no es de datos codificado en base 64 válidos.

Para hacer funcionar las cosas, el uso mediaObjectNode.ChildNodes[0].Value y pasar ese valor a Convert.FromBase64String'.

¿Es la codificación de caracteres correcta? suena el error que hay un problema que hace que los caracteres no válidos aparezcan en la matriz. Intente copiar el texto y decodificación de forma manual para ver si los datos son de hecho válidos.

(Para el registro, windows-1252 no es exactamente el mismo que iso-8859-1, por lo que puede ser la causa de un problema, salvo otras fuentes de corrupción.)

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top