XMLHttpRequest: multipart / Related POST with XML and image as payload
Próbuję opublikować obraz (z metadanymi) do Picasa Webalbums z poziomu rozszerzenia Chrome. Zauważ, że zwykły post z Content-Type image / xyz działa, jak opisałem tutaj . Jednakże, chciałbym dołączyć opis / słowa kluczowe i Specyfikacja protokołu opisuje multipart / related format z częścią XML i danych.
Pobieram dane przez HTML5 FileReader i wejście pliku użytkownika. I retrieve a binary String using
FileReader.readAsBinaryString(file);
Załóżmy, że jest to mój kod zwrotny, gdy FileReader załaduje ciąg znaków:
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);
}
Funkcja gen_multipart po prostu generuje multipart z wartości wejściowych i szablonu XML i produkuje dokładnie to samo wyjście jak Specyfikacja (oprócz ..binarne dane obrazu..), ale ze względu na kompletność, oto ona:
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("");
}
Problem polega na tym, że ładunek POST różni się od surowych danych obrazu, a tym samym prowadzi do złego żądania (Picasa nie będzie accept the image), chociaż działało dobrze przy użyciu
xhr.send(file) // With content-type set to file.type
Moje pytanie brzmi, jak uzyskać prawdziwy binarny obraz, aby umieścić go w multipart? Zakładam, że jest zniekształcony, po prostu dołączając go do łańcucha xml, ale nie mogę go naprawić.
Zauważ, że ze względu na stary błądw Picasie , base64 nie jest rozwiązaniem.
1 answers
Specyfikacja XMLHttpRequest stwierdza, że dane wysyłane za pomocą metody .send()
są konwertowane do unicode i zakodowane jako UTF-8.
Zalecanym sposobem przesyłania danych binarnych jest FormData
API. Ponieważ jednak nie tylko przesyłasz plik, ale pakujesz dane binarne w XML, ta opcja nie jest przydatna.
Rozwiązanie można znaleźć w kodzie źródłowym FormData for Web Workers Polyfill , które napisałem, gdy napotkałem podobny problem. Aby zapobiec konwersji Unicode, wszystkie dane są dodawane do tablicy i ostatecznie przesyłane jako ArrayBuffer
. Sekwencje bajtów nie są dotykane podczas transmisji, zgodnie ze specyfikacją .
Poniższy kod jest specyficzną pochodną, opartą na FormData for Web Workers Polyfill :
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!
}
Skrypt staje się bardziej wydajny, gdy używasz .readAsArrayBuffer
zamiast .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!
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:00:10