При создании запроса Ajax для загруженного пользователем изображения я использую FormData()
следующим образом:
var form_data = new FormData();
form_data.append(img_field, img_to_send, img_name);
Обратите внимание, что img_to_send
является объектом типа Blob
.
Моя проблема заключается в этом небольшом предупреждении о совместимости браузера в веб-документах FormData MDN:
XHR в Android 4.0 отправляет пустой контент для FormData с blob.
Это относится к браузеру Android (версия 4.0). Это означает, что xhr, который я пытаюсь с помощью FormData
через append
, вероятно, будет терпеть неудачу в этом конкретном браузере.
Мой вопрос: какую альтернативу я могу использовать для обеспечения того, чтобы упомянутый браузер мог правильно обработать xhr (мое требование)? Будет здорово получить иллюстративный пример.
И если альтернатив здесь нет, как мне написать свой код, чтобы он выполнялся только для браузеров, поддерживающих append
с помощью объекта blob
? Что-то вроде этого?
if (window.FormData.append) {
// xhr code comes here
}
Я совершенно не уверен в этом.
ps, пожалуйста, придерживайтесь чистой JS для ответа на вопрос.
... Нелегкая задача...
Первый вопрос, который я задал себе, - это если мне действительно нужно поддерживать этот 7-летний браузер с 0% использования?
Обнаружение функции:
Мы можем с недавних пор проверить, что содержит FormData, с помощью методов get()
, has()
, entries()
, values()
и т.д. Но первые спецификации и реализации не имели этих методов и, следовательно, не предлагали никаких средств для обнаружения этого конкретного потока.
У меня нет такого браузера Android 4 для проверки, но я думаю, что у них не было ни одного из этих методов...
Другой современный и немного хакерский способ проверить - использовать ServiceWorker, который должен был бы перехватить фиктивный запрос, чтобы вы знали, был ли ваш Blob хорошо добавлен, но еще раз ServiceWorkers не существовало 7 лет назад.
Это оставляет нам уродливую идентификацию браузера, а не функцию обнаружения (например, синтаксический анализ navigator.userAgent
). Я не могу советоваться с этим, потому что он настолько подвержен ошибкам, что может быть лучше просто дать вашему пользователю понять, что это не сработало в ответе вашего сервера.
Временное решение
Возможность отправлять сгенерированный Blob как двоичный файл с помощью другого средства, отличного от FormData, появилась только несколько месяцев назад, и в настоящее время только браузеры Blink поддерживают его изначально, а FF - с помощью эксплойта.
Это означает, что единственным обходным решением для ваших пользователей в Android Browser 4.xxx и для всех браузеров, которые не поддерживают этот метод, было бы сохранить сгенерированный Blob на своем устройстве, а затем выбрать его из <input>
и отправьте его через обычный HTML <form>
, но это предполагает, что они смогут даже сохранить этот Blob на своем устройстве, и я не могу точно сказать...
Или вы могли бы отправить 30% -ное представление base64 этих данных.
Или, наверное, лучше всего сообщить им, что они должны обновить свой браузер, потому что действительно опасно иметь такой старый браузер, который сейчас стоит в Интернете.
Итак, небольшой пример менее плохих возможностей:
<form>
4.0.4
и 4.0.3
. Поэтому я подумал, что стоит задать вопрос, если есть удобное решение для этого. Кажется, нет. Я полагаю, что лучшим способом было бы отправить base64 (в случае v4
). Но тогда из твоего ответа кажется, что сложно определить v4
(или меньше) с точностью. Это означает, что реального «хорошего» решения здесь не существует. Но спасибо за ответ, я немного исправлю его.
Привет, это небольшой пример того, как отправить файл на сервер как BINARY STRING. С этим вы не нуждаетесь в formData. Вы можете отправить с помощью простой POST. Пожалуйста, измените URL-адрес uploadFile.php на свой URL-адрес. И прочитайте комментарии к примерам переменных, которые ваш сервер должен получить.
<!DOCTYPE html>
<html>
<head>
<title>TODO supply a title</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
</head>
<body>
<div>
<input id="btnFile" type="file" accept="image/*" />
</div>
<div style="margin-top: 20px; width: 100px; border: solid 1px red">
<div id="divProgress" style="background-color: red;width: 10px; height: 5px; margin: 1px"></div>
</div>
<div style="margin-top: 20px">
<input id="btnSend" type="button" value="Send File" />
<input id= "user_id" type="hidden" value="123"/>
</div>
<script>
var btnFile = document.getElementById("btnFile");
var btnSend = document.getElementById("btnSend");
var divProgress = document.getElementById("divProgress");
var selectedFile = null;
//Register event on file selected or changed.
addEvents(btnFile, "change", function (event) {
if (event.target.files.length !== 0) {
var file = event.target.files[0];
//Check if the file is IMAGE.
if (file.type.match("image.*")) {
selectedFile = file;
} else {
selectedFile = null;
alert("Please select a IMAGE FILE");
}
} else {
selectedFile = null;
}
});
//EVENT BTN SEND.
addEvents(btnSend, "click", function () {
if (selectedFile === null) {
//Please select a file to upload.
alert("Please select the file.");
return;
}
//File reader object.
var fl = new FileReader();
//Add event to read the content file.
addEvents(fl, "load", function (evt) {
//alert(evt.target.result);
try {
//CONVERT ARRAY BUFFER TO BASE64 STRING.
var binaryString = evt.target.result;
//NOW YOU CAN SEND SIMPLE POST DATA.
var xhr = new XMLHttpRequest();
if (supportProgress(xhr)) {
addEvents(xhr, "progress", onXHRProgress);
addEvents(xhr, "loadstart", onXHRLoadStart);
addEvents(xhr, "abort", onXHRAbort);
}
xhr.open("POST", "/uploadFile.php", true);
//xhr.setRequestHeader("Content-Type", "application/json");
var user_id = document.getElementById('user_id').value;
var myData = {
uid: user_id,
fileName: selectedFile.name,
mimeType: selectedFile.type,
extension: getFileExtension(selectedFile),
contentFile: binaryString
};
xhr.send(JSON.stringify(myData));
/*
* IN YOUR SERVER SIDE YOU GET THE POST VARIABLE.
* fileName = The name of the file.
* mimeType = example "image/png"
* extension = png
* conentFile = Binary String of the content file and you can convert the Binary String to File in your disk according extension or mimeType
*/
} catch (e) {
}
});
//Read the file as arraybuffer.
fl.readAsBinaryString(selectedFile);
});
function onXHRProgress(e) {
var loaded = 0;
if (e.lengthComputable) {
if (e.loaded === e.total) {
loaded = 100;
selectedFile = null;
} else {
loaded = Math.round((e.loaded * 100) / e.total);
}
//Change the progress here.
divProgress.style.width = loaded + "px";
}
}
function onXHRLoadStart() {
divProgress.style.width = "0px";
}
function onXHRAbort() {
selectedFile = null;
}
function getFileExtension(file) {
var fileName = file.name;
var i = fileName.toString().lastIndexOf(".");
if (i !== -1) {
return fileName.toString().substring((i + 1), fileName.toString().length).toLowerCase();
} else {
return "";
}
}
function supportProgress(xhr) {
return !!(xhr && ('upload' in xhr) && ('onprogress' in xhr.upload));
}
function addEvents(obj, evtName, func) {
if (obj.addEventListener !== undefined && obj.addEventListener !== null) {
obj.addEventListener(evtName, func, false);
} else if (obj.attachEvent !== undefined && obj.attachEvent !== null) {
obj.attachEvent(evtName, func);
} else {
if (this.getAttribute("on" + evtName) !== undefined) {
obj["on" + evtName] = func;
} else {
obj[evtName] = func;
}
}
}
function removeEvents(obj, evtName, func) {
if (obj.removeEventListener !== undefined && obj.removeEventListener !== null) {
obj.removeEventListener(evtName, func, false);
} else if (obj.detachEvent !== undefined && obj.detachEvent !== null) {
obj.detachEvent(evtName, func);
} else {
if (this.getAttribute("on" + evtName) !== undefined) {
obj["on" + evtName] = null;
} else {
obj[evtName] = null;
}
}
}
</script>
</body>
</html>
caption
к изображению и id
пользователя. В настоящее время я просто использую form_data.append("uid", document.getElementById('user_id').value);
для user_id (и аналогично для подписи). Как это будет возможно с помощью этого решения?