Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

create file upload that work in IE

I want to write a file upload script that works in IE but the two types of code that I'm writing have problems in IE.

Please help. How can you write a file upload script that works in IE?

Type 1
Problem Not Support File Api In IE (Is the trick not to use it?)

    <!DOCTYPE html>

<html>
<head runat="server">
    <title></title>
    <script src="Scripts/jquery-1.6.2.js" type="text/javascript"></script>
    <script type="text/javascript">
        function updateSize() {
            var nBytes = 0;
            var nFiles=0;
            oFiles = document.getElementById("uploadInput").files;
            nFiles = oFiles.length;
            for (var nFileId = 0; nFileId < nFiles; nFileId++) {
                nBytes += oFiles[nFileId].size;
            }
            var sOutput = nBytes + " bytes";
            // optional code for multiples approximation
            for (var aMultiples = ["KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB"], nMultiple = 0, nApprox = nBytes / 1024; nApprox > 1; nApprox /= 1024, nMultiple++) {
                sOutput = nApprox.toFixed(3) + " " + aMultiples[nMultiple] + " (" + nBytes + " bytes)";
            }
            document.getElementById("fileNum").innerHTML = nFiles;
            document.getElementById("fileSize").innerHTML = sOutput;
        }
        // end of optional code
    </script>
</head>
<body>
    <form id="form1" runat="server">
    <p><input id="uploadInput" type="file" name="myFiles" onchange="updateSize();" multiple /> selected files: <span id="fileNum">0</span>; total size: <span id="fileSize">0</span></p>
<p><input type="submit" value="Send file"></p>
    </form>
</body>
</html>

Type 2
Problem Not Support document.getElementById('fileToUpload').files[0](Is the trick not to Get Files[0]?)

 <script src="Scripts/jquery-1.4.1.js" type="text/javascript"></script>
    <script type="text/javascript">
        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);
            $.post("UploadHandler.ashx");
            //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.");
        }
    </script>

</head>
<body>
    <form id="form1">
    <div>
        <label for="fileToUpload">
            Select a File to Upload</label>
        <input type="file" name="fileToUpload[]" id="fileToUpload" onchange="fileSelected();" />
    </div>
    <div id="fileName">
    </div>
    <div id="fileSize">
    </div>
    <div id="fileType">
    </div>
    <div>
        <input type="button" onclick="uploadFile()" value="Upload" />
    </div>
    <div id="progressNumber">
    </div>
    <progress id="prog" value="0" max="100.0"></progress>
    </form>
</body>

Please Help :(

like image 247
user1045472 Avatar asked Mar 27 '13 16:03

user1045472


People also ask

Can I upload files to Internet?

The most popular way to publish files to the web is by using an FTP (file transfer protocol) program. These programs create a link between your computer and the server that hosts your account, allowing you to copy and paste (or click and drag) your website files to your HostPapa hosting space.

Can we upload file using AJAX?

File upload is not possible through AJAX. You can upload file, without refreshing page by using IFrame .


1 Answers

You can't use these functions unless you're using IE10 or another modern browser. Workarounds are possible for earlier versions of Internet Explorer (and other browsers), but you'll need to adjust your back-end code too.

Why it doesn't work

Internet Explorer up until version 10 doesn't support a number of these features, the key ones being the FormData and FileReader APIs. Both of your code snippets rely on the FileReader API, and the second one also relies on FormData to upload the file dynamically.

How to determine whether to execute the code or not

I recently wrote a file upload widget that detected these features and served different code depending on support. I used the feature detections from Modernizr, because it's tests are regularly put to the test by the open source community:

var support  = {
  // Are files exposed to JS?
  // As used by Modernizr @
  // https://github.com/Modernizr/Modernizr/blob/master/feature-detects/file/api.js
  'fileReader'  : (function testFileReader(){
    // Test: look for global file class.
    return !!(window.File && window.FileList && window.FileReader);
  }()),

  // AJAX file upload via formData?
  'formData'    : window.FormData !== void 0
};

For your fileSelected function, you'll need support.fileReader to evaluate true; for uploadFile, you need support.formData.

A workaround for browsers that don't support these features

Without these features it's impossible to read a file from the front-end or to send a file using AJAX. What you can do, though, is send your file via a hidden <iframe/> inside your current page, and get UploadHandler.ashx to respond differently to non-XHR requests.

This solution is technically synchronous (just happening in another, hidden page), so you won't get updates — the only feedback is when the upload is complete and the server has responded. So you will only be able to inform the user as to file name, size, and success once they have completely uploaded it — which might take a while!

Anyway, the HTML for this would look as follows:

<form 
  id="form1" 
  runat="server" 
  action="UploadHandler.ashx"
  target="fileIframe">
  <iframe 
    name="fileIframe"
    style="display:none"
    onload="parseIframeResponse"
    tabindex="-1">
  </iframe>
  <p>
    <input id="uploadInput" type="file" name="myFiles" onchange="updateSize();" multiple />
    selected files: 
    <span id="fileNum">
      0
    </span>
    ; total size: 
    <span id="fileSize">
      0
    </span>
  </p>
  <p>
    <input type="submit" value="Send file">
  </p>
</form>

A few changes:

  1. The form now has a target, which means that when it posts its content to the URI in action, it will load the response in there, as opposed to on the current page.
  2. The target is a name reference to an iframe we've included. It is hidden with display:none, and given a negative tabindex just to make sure the user doesn't stumble into it. It also has an onload property specified. This is the only way of binding functions to the load event in older versions of IE.

So when the form is submitted, we stay on the current page and the server response loads in our hidden iframe. When that happens, the browser will execute the function named in the onload attribute. Sadly, this means that function needs to be in the global scope!

Back-end stuff

I don't know how your back-end works, but if the iframe is to load the response instead of downloading it, it will need to be HTML or plain text (and that will need to be specified in the mime-type). You can tell whether the form was posted via AJAX from the back-end by looking for the X-Requested-With header, which should have a value of XMLHttpRequest — if that isn't there, then the iframe is asking for the response and you need to send text or HTML. You may want to stringify a JSON response that exposes the values you wanted to feed back to the user like fileName, fileSize & fileType. I'm hoping you can do this yourself or get a colleague to handle it.

Capturing the iframe response

As mentioned, the response handler function will need to be in the global scope for the onload attribute to bind to, because of old IE being very quirky. I see you're using jQuery, so if you went down the route of stringifying the server response, you could write this function as follows:

function parseIframeResponse(){
  var response = $('#fileIframe').contents().find('body').text();
  var object   = $.parseJSON(response);
}

Issues with the iframe load event binding

As mentioned earlier, the iframe load event needs to be bound inline as an attribute of the iframe itself — this is because IE will simply fail to register it otherwise. But this is problematic in and of itself because even an empty iframe (an empty or non-present src will default to about:blank) fires a load event. To mitigate this you will need to discard any response that evaluates to an empty string as a false positive, and make sure your back-end responds with some content even if it encounters a fault.

Presumably, you would then want to use whatever information is in there to execute some of the code you've currently got in the functions fileSelected, uploadProgress, etc.

Hope this helps.

EDIT 1: Out-of-the-box solutions

In hindsight, despite writing this off the back of having developed my own solution to the problem, it could be considered negligent not to mention Fine Uploader, a heavily tested (over 700 closed issues!) and well maintained stand-alone plugin that aims to achieve the best file upload experience possible for IE7 and up. There's also a good selection of back-end server components — including ASP.NET — to parse the upload. You might find it easier to tweak this than roll your own!

EDIT 2: Neglected to mention issues with iframe's load event. Amended answer in place.

like image 155
Barney Avatar answered Sep 19 '22 21:09

Barney