XMLHttpRequest: Multipart/related POST con XML y la imagen como carga

Estoy tratando de subir una imagen (con Metadatos) a Picasa Webalbums desde dentro de un Cromo-Extensión. Nota de que los post con Contenido de Tipo image/xyz obras, como he descrito aquí. Sin embargo, quiero incluir una descripción y las palabras clave y el la especificación del protocolo describe un multipart/related formato con un XML y parte de los datos.

Me estoy poniendo los Datos a través de HTML5 FileReader y archivo de usuario de entrada. Puedo recuperar un archivo binario
Cadena mediante

FileReader.readAsBinaryString(file);

Asumir este es mi código de devolución de llamada una vez que el FileReader ha cargado la cadena:

function upload_to_album(binaryString, filetype, albumid) {

    var method = 'POST';
    var url = 'http://picasaweb.google.com/data/feed/api/user/default/albumid/' + albumid;
    var request = gen_multipart('Title', 'Description', binaryString, filetype);
    var xhr = new XMLHttpRequest();
    xhr.open(method, url, true);
    xhr.setRequestHeader("GData-Version", '3.0');
    xhr.setRequestHeader("Content-Type",  'multipart/related; boundary="END_OF_PART"');
    xhr.setRequestHeader("MIME-version", "1.0");
    //Add OAuth Token
    xhr.setRequestHeader("Authorization", oauth.getAuthorizationHeader(url, method, ''));
    xhr.onreadystatechange = function(data) {
        if (xhr.readyState == 4) {
            //.. handle response
        }
    };
    xhr.send(request);
}   

La gen_multipart función sólo genera el multipart de los valores de entrada y la plantilla XML y produce exactamente la misma salida como la especificación (aparte de ..imagen binaria de datos..), pero en aras de la exhaustividad, aquí está:

function gen_multipart(title, description, image, mimetype) {
    var multipart = ['Media multipart posting', "   \n", '--END_OF_PART', "\n",
    'Content-Type: application/atom+xml',"\n","\n",
    "<entry xmlns='http://www.w3.org/2005/Atom'>", '<title>', title, '</title>',
    '<summary>', description, '</summary>',
    '<category scheme="http://schemas.google.com/g/2005#kind" term="http://schemas.google.com/photos/2007#photo" />',
    '</entry>', "\n", '--END_OF_PART', "\n",
    'Content-Type:', mimetype, "\n\n",
    image, "\n", '--END_OF_PART--'];
    return multipart.join("");
}

El problema es, que el POST de carga difiere de los datos de imagen raw, y por lo tanto conduce a una Mala Solicitud (Picasa no aceptar la imagen), aunque funcionó bien cuando el uso de

xhr.send(file) //With content-type set to file.type

Mi pregunta es, ¿cómo puedo obtener el real binario de la imagen a incluir en el multipart? Supongo que es destrozado por sólo anexarlo a la cadena xml, pero me parece que no puede ser arreglado.

Tenga en cuenta que debido a un un viejo error en Picasa, base64 no es la solución.

Has probado a cargar sin los metadatos? code.google.com/apis/picasaweb/docs/2.0/…
Como declaró dos veces en mi post, subir la imagen directamente sin metadatos funciona bien. Me pide explícitamente una solución para el envío de con los metadatos.

OriginalEl autor oliverguenther | 2011-11-24

1 Kommentar

  1. 20

    La XMLHttpRequest especificación informa que los datos de envío utilizando el .send() método es convertir a unicode, y codificado como UTF-8.

    La forma recomendada para cargar los datos binarios a través de FormData de la API. Sin embargo, puesto que no se acaba de subir un archivo, pero envolviendo el binario de datos en XML, esta opción no es útil.

    La solución se puede encontrar en el código fuente de la FormData para los Web workers Polyfil, que he escrito cuando me encontré con un problema similar. Para evitar que el Unicode-de la conversión, todos los datos se agregan a una matriz, y, finalmente, se transmite como un ArrayBuffer. Las secuencias de bytes no son tocados en la transmisión, según la especificación.

    El código de abajo es un derivado, basado en la FormData para los Web workers Polyfil:

    function gen_multipart(title, description, image, mimetype) {
        var multipart = [ "..." ].join(''); //See question for the source
        var uint8array = new Uint8Array(multipart.length);
        for (var i=0; i<multipart.length; i++) {
            uint8array[i] = multipart.charCodeAt(i) & 0xff;
        }
        return uint8array.buffer; //<-- This is an ArrayBuffer object!
    }

    El guión se vuelve más eficiente al utilizar .readAsArrayBuffer en lugar de .readAsBinaryString:

    function gen_multipart(title, description, image, mimetype) {
        image = new Uint8Array(image); //Wrap in view to get data
    
        var before = ['Media ... ', 'Content-Type:', mimetype, "\n\n"].join('');
        var after = '\n--END_OF_PART--';
        var size = before.length + image.byteLength + after.length;
        var uint8array = new Uint8Array(size);
        var i = 0;
    
        //Append the string.
        for (; i<before.length; i++) {
            uint8array[i] = before.charCodeAt(i) & 0xff;
        }
    
        //Append the binary data.
        for (var j=0; j<image.byteLength; i++, j++) {
            uint8array[i] = image[j];
        }
    
        //Append the remaining string
        for (var j=0; j<after.length; i++, j++) {
            uint8array[i] = after.charCodeAt(j) & 0xff;
        }
        return uint8array.buffer; //<-- This is an ArrayBuffer object!
    }
    Gran hallazgo! Gracias por el análisis detallado y la solución 🙂
    Por favor, tenga en cuenta que XMLHttpRequest.enviar(ArrayBuffer) está en desuso. Usted debe usar XMLHttpRequest.enviar(ArrayBufferView) (tomar de la .buffer) o el próximo XMLHttpRequest.sendAsBinary(). Consulte developer.mozilla.org/en-US/docs/Web/API/…
    Uso return uint8array; en lugar de return uint8array.buffer;.
    Muchas gracias por su en-profundidad de la respuesta 🙂

    OriginalEl autor Rob W

Kommentieren Sie den Artikel

Bitte geben Sie Ihren Kommentar ein!
Bitte geben Sie hier Ihren Namen ein

Pruebas en línea