I'm posting a file in Javascript using the new FormData interface. When I send a file using Safari 5.1.5 using "multipart/form-data", Safari coerces the File into a string, and instead of sending the actual file contents, it sends [object Object]
.
Example:
var formdata = new FormData();
formdata.append("file", file);
var xhr = new XMLHttpRequest();
xhr.open("POST", "https://example.com/upload", true);
xhr.send(formdata);
What Safari ends up sending:
Origin: https://example.com/
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_3) AppleWebKit/534.55.3 (KHTML, like Gecko) Version/5.1.5 Safari/534.55.3
Content-Type: multipart/form-data; boundary=----WebKitFormBoundarycLc5AIMWzGxu58n8
Referer: https://example.com/upload
------WebKitFormBoundarycLc5AIMWzGxu58n8
Content-Disposition: form-data; name="file"
[object Object]
My file is therefore uploaded, but the contents of the file are, you guessed it, [object Object]
.
What in the world is going on here? Is this a Safari bug?
For those curious how to dynamically generate a JS Blob, here's an example:
var Builder = (window.MozBlobBuilder || window.WebKitBlobBuilder || window.BlobBuilder);
var builder = new Builder();
builder.append("hello, world");
var file = builder.getBlob("text/plain")
Unfortunately this does not work on Safari, so it didn't really help to include it in the question.
The file object I reference is from a drop action on a DOM element. Here's an example for how to retrieve a file. Run the following after the DOM has loaded.
function cancel(e) {
if (e.stopPropagation) {
e.stopPropagation();
}
if (e.preventDefault) {
e.preventDefault();
}
}
function drop(e) {
cancel(e);
for (var i=0; i<e.dataTransfer.files.length; i++) {
file = e.dataTransfer.files[i];
}
}
var elem = document.getElementById("upload-area");
elem.addEventListener("drop", drop, false);
This didn't seem relevant when I was asking the question, but I figured this one out. Before uploading the file using XMLHttopRequest, I called jQuery's $.ajax
method to call an endpoint in our backend to prep the file.
In short: this is a bug in jQuery on Safari. I was using a for loop to process a list of files, and upload them. I passed a file object as a parameter to the jQuery $.ajax
method so that the file object I wanted wouldn't be rewritten as multiple loops were executed. E.g.
for (i in files) {
var file = files[i];
$.ajax({
method: "POST",
myFile: file,
success: function(response) {
var file = this.myFile;
// ...
});
}
Turns out that jQuery happens to clone the file
object incorrectly in Safari. So instead of casting it to a file when set to this.myFile
, it casts it into an object, thus making it lose all of its special "file-like" capabilities. The other browsers appear to understand that the object is still a file despite this.
The answer is to write a callback method to handle the file uploads.
function handleFile(file) {
$.ajax({
method: "POST",
success: function(response) {
// ...
});
}
for (var i in files) {
handleFile(files[i]);
}
P.S. Going to file this to the jQuery bug tracker, but just wanted to keep this here in case anyone else has the same issue.