0

I need to test various web services which are posts that take an uploaded file as the content of the body. To do this I'd like to do quick tests using ajax call. I found the following page which describes how to do this: http://www.captain.at/ajax-file-upload.php However, it requires that the page have "UniversalXPConnect" privileges in firefox.

How do I enable that privilege? I tried editing prefs.js and adding:

user_pref("capability.principal.foo.id", "http://localhost:8080/access/index.html");
user_pref("capability.principal.foo.granted", "UniversalXPConnect");

which should give access to the page http://localhost:8080/access/index.html. But, it doesn't seem to work.

HappyEngineer
  • 4,017
  • 9
  • 46
  • 60

2 Answers2

1

If the user specifies the file you don't need UniversalXPConnect. The HTML5 File API is enough:

<html>
<head>
<title>HTML5 File API</title>
</head>
<script type="text/javascript">
// <!--
// See: http://dev.w3.org/2006/webapi/FileAPI/

function upload (input) {
    for (var i = 0; i < input.files.length; ++ i) {
        // makes multiple uploads
        uploadFile(input.files[i]);
    }
}

function uploadFile (file) {
    var reader = new FileReader();
    reader.onprogress = function (event) {
        var percent = 100 * event.loaded / event.total;
        // TODO: display progress
    };

    reader.onerror = function (event) {
        // display error
        alert(errorMessage(reader.error)+': '+file.name);
    };

    reader.onload = function (event) {
        if (reader.error) {
            // display error
            alert(errorMessage(reader.error)+': '+file.name);
        }
        else {
            // You could also use reader.readAsBinaryString(file)
            // and the mozilla specific function call btoa(reader.result).
            // For more mozilla specific stuff (e.g. sending data as binary)
            // see: https://developer.mozilla.org/en/using_xmlhttprequest
            var data = reader.result.substring(reader.result.search(',')+1);
            var text = document.getElementById('text').value;
            var request = new XMLHttpRequest();
            var boundaryString = guid();
            var boundary = '--' + boundaryString;

            while (text.search(boundary) != -1) {
                boundaryString = guid();
                boundary = '--' + boundaryString;
            }

            var requestbody = boundary + '\n' +
                'Content-Disposition: form-data; name="mytext"\n' +
                '\n' +
                text +
                '\n' +
                boundary + '\n' +
                'Content-Disposition: form-data; name="myfile"; filename="' +
                file.name.replace(/"/g, '') + '"\n' +
                'Content-Type: application/octet-stream\n' +
                'Content-Transfer-Encoding: base64\n' +
                '\n' +
                data + '\n' +
                boundary;

            request.onreadystatechange = function () {
                if (request.readyState == 4) {
                    if (request.status == 200) {
                        alert('Result: ' + request.responseText);
                    }
                    else {
                        alert(
                            'Error "' + request.statusText + '" occured while uploading: ' +
                            file.name);
                    }
                }
            };

            /* a non-standard variant (still supported by many browsers) would be:
            request.onuploadprogress = function () {
                // possibly only mozilla, but awesome! upload progress!
                var percent = 100 * event.loaded / event.total;
                // TODO: display progress
            };

            request.onload = function () {
                if (request.status == 200) {
                    alert('Result: ' + request.responseText);
                }
                else {
                    alert(
                        'Error "' + request.statusText + '" occured while uploading: ' +
                        file.name);
                }
            };

            request.onerror = function () {
                alert(
                    'There was a problem with the request when uploading file: ' +
                    file.name);
            };
            */

            request.open('POST', 'post.php', true);
            request.setRequestHeader('Content-type', 'multipart/form-data; boundary="' +
                boundaryString + '"');
            request.setRequestHeader('Connection', 'close');
            request.setRequestHeader('Content-Length', requestbody.length);
            request.send(requestbody);

        }
    };

    reader.readAsDataURL(file);

    // there would also be:
    // reader.readAsBinaryString(file);
    // reader.readAsText(file, 'UTF-8');
    // reader.readAsArrayBuffer(file);
}

function errorMessage (error) {
    switch (error.code) {
        case FileError.ABORT_ERR:
            return 'Aborted';

        case FileError.ENCODING_ERR:
            return 'Encoding Error';

        case FileError.NOT_FOUND_ERR:
            return 'File not found';

        case FileError.NOT_READABLE_ERR:
            return 'File is not readable';

        case FileError.NO_MODIFICATION_ALLOWED_ERR:
            return 'File is not writeable';

        case FileError.SECURITY_ERR:
            return 'Security Error';

        default:
            return 'Unknown error code: ' + error.code;
    }
}

// from: https://stackoverflow.com/questions/105034/how-to-create-a-guid-uuid-in-javascript
function S4() {
    return (((1+Math.random())*0x10000)|0).toString(16).substring(1);
}

function guid() {
    return (S4()+S4()+"-"+S4()+"-"+S4()+"-"+S4()+"-"+S4()+S4()+S4());
}

// -->
</script>
<body>
<input type="text" id="text" value="My text.."/>
<input type="file" onchange="upload(this);"/>
</body>
</html>

Still, I would recommend to use an iframe, because it is best supported by all browsers: Is it possible to use Ajax to do file upload?

Community
  • 1
  • 1
panzi
  • 7,517
  • 5
  • 42
  • 54
  • If you work for a company dealing with hundreds of thousands of dollars from clients, wouldn't iframes be insecure (from what I've heard)? The File API is the way to go, or good ol' fashioned page refreshes. – trusktr Dec 24 '11 at 01:52
  • @trusktr: In war way are iframes more insecure than UniversalXPConnect? And the security risks of iframes are not with using them, but with letting your page be used in one. Or do you disable iframes for your whole website and can't even enable it for a submit page? You could use some kind of authenticity token there to prevent abuse. Even a referer check might be enough. – panzi Feb 18 '12 at 02:57
  • We disable all iframes on the whole site and send stuff via post with AJAX using the super awesome FormData object. Check it out on the Mozilla Developer Network: https://developer.mozilla.org/en/DOM/XMLHttpRequest/Using_XMLHttpRequest#Using_FormData_objects The behavior you get is the browser behaving EXACTLY like good ol'-fashioned page refreshes, except now you can do it all in Ajax without refreshing the page to send a form (including file uploads). – trusktr Feb 18 '12 at 09:01
  • Now you can take normal measures like limiting requests so they originate only from the same domain and whatever other measures you'd take with normal page-refresh submissions. – trusktr Feb 18 '12 at 09:05
  • The method in your answer is pretty much the same as using FormData... Except you're creating the POST content manually. Instead of writing all that code, you could've just used FormData to automatically create the POST content... And it turns out to be much simpler than iFrames anyways. – trusktr Feb 18 '12 at 09:08
1

Improving on panzi's answer, you can use the FormData object to send files with Ajax in a very simple manner:

<html>
<head>
<title>HTML5 File API</title>
</head>
<script type="text/javascript">
// <!--
// See: https://developer.mozilla.org/En/XMLHttpRequest/Using_XMLHttpRequest#Using_FormData_objects
function upload() {
    var uploadRequest = new XMLHttpRequest,
        uploadForm = document.getElementById('file_upload');

    function transferProgress(progressEvent) {
    /*Executes For each update of the progress of the Ajax transfer.*/
        // show progress bar or something....
    }
    function transferComplete() {
    /*Executes when the transfer is complete.*/
        //Do something like show a nice message...
    }
    function transferFailed() {
    /*Executes when the transfer fails.*/
        alert('Upload failed!');
    }
    function transferCanceled() {
    /*Executes when the transfer is canceled.*/
        alert('Upload canceled!');
    }

    uploadRequest.upload.addEventListener('progress', transferProgress, false);
    //uploadRequest.upload.addEventListener('load', transferComplete, false); // transfer complete, but this doesn't mean a response from the server has been received.
    uploadRequest.addEventListener('load', transferComplete, false); // ajax request complete, response from the server has been received and all has been processed.
    uploadRequest.upload.addEventListener('error', transferFailed, false);
    uploadRequest.upload.addEventListener('abort', transferCanceled, false);

    uploadRequest.open('POST', action, true);
    uploadRequest.send(new FormData(uploadForm));
}
// -->
</script>
<body>
<form id="file_upload" enctype="multipart/form-data">
    <input type="text" id="text" value="blah blah blah"/>
    <input type="file" onchange="upload();"/>
</form>
</body>
</html>
trusktr
  • 44,284
  • 53
  • 191
  • 263