Моя команда и я недовольны тем, что наше удивительное анимационное приложение не работает в Safari из-за сочетания ограничений Safari и AppEngine. Мы надеемся, что один из вас поможет нам найти «магическое заклинание», чтобы решить его.
Это может быть чрезвычайно легко решить, но через два дня мы столкнулись только с кирпичными стенами, поскольку это необычный (хотя и очень полезный) сценарий, который в значительной степени недокументирован.
Позвольте мне объяснить детали проблемы.
Наше приложение должно сохранять данные холста в blobstore (изображения, которые пользователь нарисовал для своей анимации.) Обычно это можно сделать, разместив веб-форму динамически через ajax, в котором есть двоичное поле с данными изображения. Один из способов сделать это - использовать ArrayBuffer и BlobBuilder. Это работает с Chrome:
dataURItoBlob = function(dataURI, callback) {
var ab, bb, byteString, i, ia, mimeString, _ref;
if (!(typeof ArrayBuffer != "undefined" && ArrayBuffer !== null)) {
return null;
}
byteString = atob(dataURI.split(',')[1]);
mimeString = dataURI.split(',')[0].split(':')[1].split(';')[0];
ab = new ArrayBuffer(byteString.length);
ia = new Uint8Array(ab);
for (i = 0, _ref = byteString.length; (0 <= _ref ? i < _ref : i > _ref); (0 <= _ref ? i += 1 : i -= 1)) {
ia[i] = byteString.charCodeAt(i);
}
bb = window.BlobBuilder ? new BlobBuilder() : window.WebKitBlobBuilder ? new WebKitBlobBuilder() : window.MozBlobBuilder ? new MozBlobBuilder() : void 0;
if (bb != null) {
bb.append(ab);
return bb.getBlob(mimeString);
} else {
return null;
}
};
postCanvasToBlobstore = function(url, name, canvas) {
blob = dataURItoBlob(canvas.toDataURL());
formData = new FormData();
formData.append("file", blob);
xhr = new XMLHttpRequest();
xhr.open("POST", url);
return xhr.send(formData);
}
Другой способ сохранить данные в двоичной форме - использовать xhr.sendAsBinary (). Это работает для Firefox:
postCanvasToBlobstore = function(url, name, canvas) {
type='image/png'
var arr, boundary, data, j, xhr;
data = canvas.toDataURL(type);
data = data.replace('data:' + type + ';base64,', '');
xhr = new XMLHttpRequest();
xhr.open('POST', url, true);
boundary = 'imaboundary';
xhr.setRequestHeader('Content-Type', 'multipart/form-data; boundary=' + boundary);
arr = ['--' + boundary, 'Content-Disposition: form-data; name="' + name + '"; filename="' + name + '"', 'Content-Type: ' + type, '', atob(data), '--' + boundary + '--'];
j = arr.join('\r\n');
return xhr.sendAsBinary(j);
}
Кажется, что ни одна из этих возможностей не существует для Safari (хотя это очень возможно, но мы не достаточно умны, чтобы понять это). Одна альтернатива - просто использовать данные, закодированные в base64, что Safari может сделать наверняка. Вот как это будет выглядеть:
postCanvasToBlobstore = function(url, name, canvas, type) {
type='image/png'
var arr, boundary, data, j, xhr;
data = canvas.toDataURL(type);
data = data.replace('data:' + type + ';base64,', '');
xhr = new XMLHttpRequest();
xhr.open('POST', url, true);
boundary = 'imaboundary';
xhr.setRequestHeader('Content-Type', 'multipart/form-data; boundary=' + boundary);
arr = ['--' + boundary, 'Content-Disposition: form-data; name="' + name + '"; filename="' + name + '"', 'Content-Transfer-Encoding: base64','Content-Type: ' + type, '', data, '--' + boundary + '--'];
j = arr.join('\r\n');
return xhr.send(j);
}
Теперь это действительно работает! Однако он работает только с версией разработки инструментов AppEngine из-за известной ошибки в хранилище больших двоичных объектов: в тот момент, когда вы запускаете приложение в производство, оно перестает работать. Конечно, возможно, что где-то настройка кода POST может решить проблему, с которой сталкивается blobstore при интерпретации данных. См. http://code.google.com/p/googleappengine/issues/detail?id=4265 о том, что, по-видимому, является проблемой хранилища, связанной с этой проблемой.
Вы можете вставить любой из трех приведенных выше примеров кода в элемент управления редактирования и посмотреть, что происходит с каждой версией функции postCanvasToBlobstore: первый пример будет работать в Chrome, второй - в Firefox (и является версией отладочное приложение по умолчанию), и третий должен работать из всех трех (но не работает ни для одного из них при использовании этого производственного веб-сайта, вероятно, из-за ошибки в магазине.)