I am working on trying to get iOS 6 to use XMLHttpRequest POSTs to upload images. This works on desktop and Android web browsers, but with iOS 6 I am getting an error on the page being posted to: "Request Body Stream Exhausted". (Using iOS Simulator with the Safari Web Inspector).
Here is the basic code of the page:
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.");
}
When doing this on any other browser, the handler returns correctly and uploads the file. However, with iOS the ashx page has the error "request body stream exhausted".
Here is a screenshot of the inspector:
Any ideas?
UPDATE: This issue only occurs when NTLM/Windows authentication is enabled for the application in IIS. With forms or anonymous authentication, the upload works fine.
Thanks,
John
In iOS 6, Safari sends the file with the initial post, including the file. That means the file stream is at the end, or "exhausted."
However, with NTLM, it will get a 401 challenge in response, and then have to resend the post with the authentication information. Since it does not reset the file stream, it is unable to send the file again with the second post. You can see this in the IIS logs.
As far as I know, there is no particularly good way around it. I am changing my mobile app, so that it uses form authentication. I direct the mobile app to a separate login app on the same server, which is set to use Windows Authentication. The login app can then redirect back to the main app with a form authentication cookie, and all is well again.
You have to set the machine key on both apps in the web.config file, so that both are using the same keys for encryption and validation.
The code on the login app is as simple as
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