Upload de imagem do iOS 6 (iPhone/iPad) “Request Body Stream Exhausted” com autenticação NTLM/Windows
-
12-12-2019 - |
Pergunta
Estou tentando fazer com que o iOS 6 use POSTs XMLHttpRequest para fazer upload de imagens.Isso funciona em navegadores desktop e Android, mas com o iOS 6 estou recebendo um erro na página que está sendo postada em:"Solicitação de fluxo corporal esgotado" .(Usando o Simulador iOS com o Safari Web Inspector).
Aqui está o código básico da página:
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.");
}
Ao fazer isso em qualquer outro navegador, o manipulador retorna corretamente e carrega o arquivo.No entanto, com iOS, a página ashx apresenta o erro "solicitação de fluxo de corpo esgotado".
Aqui está uma captura de tela do inspetor:
Alguma ideia?
ATUALIZAR: Esse problema ocorre apenas quando a autenticação NTLM/Windows está habilitada para o aplicativo no IIS.Com formulários ou autenticação anônima, o upload funciona bem.
Obrigado,
John
Solução
No iOS 6, o Safari envia o arquivo com a postagem inicial, incluindo o arquivo.Isso significa que o fluxo de arquivos está no final ou "esgotado".
No entanto, com o NTLM, ele receberá um desafio 401 em resposta e terá que reenviar a postagem com as informações de autenticação.Como não redefine o fluxo de arquivos, não é possível enviar o arquivo novamente com a segunda postagem.Você pode ver isso nos logs do IIS.
Pelo que eu sei, não há uma maneira particularmente boa de contornar isso.Estou alterando meu aplicativo móvel para que ele use autenticação de formulário.Direciono o aplicativo móvel para um aplicativo de login separado no mesmo servidor, que está configurado para usar a autenticação do Windows.O aplicativo de login pode então redirecionar de volta para o aplicativo principal com um cookie de autenticação de formulário e tudo estará bem novamente.
Você deve definir a chave da máquina em ambos os aplicativos no arquivo web.config, para que ambos usem as mesmas chaves para criptografia e validação.
O código no aplicativo de login é tão simples quanto
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
Outras dicas
Resolvi o problema não configurando o HTTP Authenticate Head autodefinido no iOS 7 e iOS 8.(A princípio, nosso serviço usa o valor autodefinido para o Authenticate Head).E após o desafio ser tratado pelo delegado, a solicitação terá a opção "Autenticar:NTLMxxx automaticamente" automaticamente.E o Put e o POST funcionam novamente.
Este erro vem no iOS, mas você pode usar uma abordagem diferente, como alterar sua linha de código em formdata, onde você anexando o arquivo
var fd = new FormData();
fd.append("fileToUpload", document.getElementById('fileToUpload').files[0]);
na linha abaixo basicamente não anexe o controle de arquivo, mas use os dados da imagem base64
var reader = new FileReader();
reader.readAsDataURL(opmlFile.files[0]);
reader.onload = function () {
var base64DataImg = reader.result;
base64DataImg = base64DataImg.replace('data:'imagetype';base64,', '');
}