由于 Safari 和 AppEngine 的限制,我和我的团队对我们出色的动画应用无法在 Safari 上运行感到不满。我们希望你们中的一个可以帮助我们找到解决它的“魔法咒语”。
这可能是一个非常容易解决的问题,但两天后我们只遇到了砖墙,因为这是一个不常见(尽管非常有用)的场景,几乎没有记录。
让我解释一下问题的细节。
我们的应用程序需要将画布数据保存到 blobstore(用户为其动画绘制的图像)。通常,您可以通过 ajax 动态发布 Web 表单来实现此目的,其中包含图像数据的二进制字段。一种方法是使用 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()。这适用于火狐:
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 工具的开发版本,因为存在一个已知的 blobstore 错误:当您将应用部署到生产环境时,它就会停止工作。当然,在某处调整 POST 代码可能会解决 blobstore 解释数据的问题。请参阅http://code.google.com/p/googleappengine/issues/detail?id=4265了解与此问题相关的 Blobstore 问题。
您可以将上面三个代码示例中的任何一个粘贴到编辑控件中,并可以查看 postCanvasToBlobstore 函数的每个版本会发生什么情况:第一个示例将在 Chrome 中运行,第二个将在 Firefox 中运行(并且是调试应用程序默认的版本to),第三个应该适用于所有三个(但在使用这个生产网站时对它们都不适用,可能是因为 blobstore 错误。)