Загрузка изображения с iOS 6 (iPhone/iPad) "Запрос на исчерпание основного потока" с проверкой подлинности NTLM/Windows
Я работаю над попыткой заставить iOS 6 использовать POST XMLHttpRequest для загрузки изображений. Это работает в настольных браузерах и браузерах Android, но с iOS 6 я получаю сообщение об ошибке на странице, на которой размещено сообщение: "Запрос исчерпан Body Body". (Использование iOS Simulator с Safari Web Inspector).
Вот основной код страницы:
function fileSelected() {
var file = document.getElementById('fileToUpload').files[0];
if (file) {
var fileSize = 0;
if (file.size > 1024 * 1024)
fileSize = (Math.round(file.size * 100 / (1024 * 1024)) / 100).toString() + 'MB';
else
fileSize = (Math.round(file.size * 100 / 1024) / 100).toString() + 'KB';
document.getElementById('fileName').innerHTML = 'Name: ' + file.name;
document.getElementById('fileSize').innerHTML = 'Size: ' + fileSize;
document.getElementById('fileType').innerHTML = 'Type: ' + file.type;
}
}
function uploadFile() {
var fd = new FormData();
fd.append("fileToUpload", document.getElementById('fileToUpload').files[0]);
var xhr = new XMLHttpRequest();
xhr.upload.addEventListener("progress", uploadProgress, false);
xhr.addEventListener("load", uploadComplete, false);
xhr.addEventListener("error", uploadFailed, false);
xhr.addEventListener("abort", uploadCanceled, false);
xhr.open("POST", "/UploadHandler.ashx");
xhr.send(fd);
}
function uploadProgress(evt) {
if (evt.lengthComputable) {
var percentComplete = Math.round(evt.loaded * 100 / evt.total);
document.getElementById('progressNumber').innerHTML = percentComplete.toString() + '%';
document.getElementById('prog').value = percentComplete;
}
else {
document.getElementById('progressNumber').innerHTML = 'unable to compute';
}
}
function uploadComplete(evt) {
/* This event is raised when the server send back a response */
alert(evt.target.responseText);
}
function uploadFailed(evt) {
alert("There was an error attempting to upload the file.");
}
function uploadCanceled(evt) {
alert("The upload has been canceled by the user or the browser dropped the connection.");
}
При этом в любом другом браузере обработчик корректно возвращается и загружает файл. Однако в iOS на странице ashx выдается сообщение об ошибке "исчерпан поток тела запроса".
Вот скриншот инспектора:
Есть идеи?
ОБНОВЛЕНИЕ: эта проблема возникает, только если для приложения в IIS включена проверка подлинности NTLM/Windows. С формами или анонимной аутентификацией загрузка работает нормально.
Спасибо,
Джон
3 ответа
В iOS 6 Safari отправляет файл с первоначальным постом, включая файл. Это означает, что поток файлов находится в конце или "исчерпан".
Тем не менее, с NTLM, он получит 401 вызов в ответ, а затем придется повторно отправить сообщение с информацией аутентификации. Так как он не сбрасывает поток файлов, он не может отправить файл снова со вторым сообщением. Вы можете увидеть это в журналах IIS.
Насколько я знаю, нет особенно хорошего способа обойти это. Я изменяю свое мобильное приложение, чтобы оно использовало аутентификацию формы. Я направляю мобильное приложение в отдельное приложение для входа в систему на том же сервере, который настроен на использование проверки подлинности Windows. Затем приложение входа в систему может перенаправить обратно в основное приложение с помощью файла cookie для проверки подлинности формы, и все снова в порядке.
Вы должны установить ключ компьютера для обоих приложений в файле web.config, чтобы оба они использовали одинаковые ключи для шифрования и проверки.
Код в приложении входа в систему так же прост, как
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) _
Handles Me.Load
With HttpContext.Current.User.Identity
If .IsAuthenticated Then
Dim sUser As String = .Name.ToLower.Trim
FormsAuthentication.RedirectFromLoginPage(s, False)
End If
End With
End Sub
Я решил эту проблему, не устанавливая самостоятельно определенную головку HTTP-аутентификации на iOS 7 и iOS 8. (Сначала наш сервис использует самоопределяемое значение для головы аутентификации). И после того, как вызов будет обработан делегатом, запрос будет автоматически иметь заголовок "Authenticate: NTLMxxx автоматически". И снова работает Put и POST.
Эта ошибка появляется в IOS, но вы можете использовать другой подход, например изменить строку кода в форме данных, куда вы добавляете файл
var fd = new FormData();
fd.append("fileToUpload", document.getElementById('fileToUpload').files[0]);
к строке ниже, в основном, не добавляйте элемент управления файлом, но используйте данные изображения base64
var reader = new FileReader();
reader.readAsDataURL(opmlFile.files[0]);
reader.onload = function () {
var base64DataImg = reader.result;
base64DataImg = base64DataImg.replace('data:'imagetype';base64,', '');
}