Question

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:

Request Body Stream Exhausted

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

Was it helpful?

Solution

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

OTHER TIPS

I solved the issue by not setting the self-defined HTTP Authenticate Head on iOS 7 and iOS 8. (At first, our service use the self-defined value for the Authenticate Head). And after the challenge being handled by the delegate, the request will have the "Authenticate: NTLMxxx automatically" header automatically. And the Put and POST works again.

This error Comes on IOS but you can use different approach like change your code line in formdata where you appending the file

var fd = new FormData();

fd.append("fileToUpload", document.getElementById('fileToUpload').files[0]);

to the below line basically don't append the file control but use the base64 image data

var reader = new FileReader();

reader.readAsDataURL(opmlFile.files[0]);

reader.onload = function () {

    var base64DataImg = reader.result;            

    base64DataImg = base64DataImg.replace('data:'imagetype';base64,', '');      

}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top