Read file:// URLs in IE XMLHttpRequest

2019-01-18 11:48发布

问题:

I'm developing a JavaScript application that's meant to be run either from a web server (over http) or from the file system (on a file:// URL).

As part of this code, I need to use XMLHttpRequest to load files in the same directory as the page and in subdirectories of the page.

This code works fine ("PASS") when executed on a web server, but doesn't work ("FAIL") in Internet Explorer 8 when run off the file system:

<html><head>
<script>
window.onload = function() {
  var xhr = new XMLHttpRequest();
  xhr.open("GET", window.location.href, false);
  xhr.send(null);
  if (/TestString/.test(xhr.responseText)) {
    document.body.innerHTML="<p>PASS</p>";
  }
}
</script>
<body><p>FAIL</p></body>

Of course, at first it fails because no scripts can run at all on the file system; the user is prompted a yellow bar, warning that "To help protect your security, Internet Explorer has restricted this webpage from running scripts or ActiveX controls that could access your computer."

But even once I click on the bar and "Allow Blocked Content" the page still fails; I get an "Access is Denied" error on the xhr.open call.

This puzzles me, because MSDN says that "For development purposes, the file:// protocol is allowed from the Local Machine zone." This local file should be part of the Local Machine Zone, right?

How can I get code like this to work? I'm fine with prompting the user with security warnings; I'm not OK with forcing them to turn off security in the control panel.

EDIT: I am not, in fact, loading an XML document in my case; I'm loading a plain text file (.txt).

回答1:

Hmm, could it be the difference between the native XMLHttpRequest object and the ActiveX one? I seem to remember something about that. That is, instead of

var xhr = new XMLHttpRequest();

try

var xhr = new ActiveXObject("MSXML2.XMLHTTP");

Obviously, put some checks in place to see if the browser supports ActiveX. Of course, this is limited to IE only, as well.



回答2:

I just happened to stumble across exactly the same problem. As suggested above, the non-native ActiveX "constructor" works. I’m not really sure whether there are different policies applied to the two objects, but since jQuery mentions the same problem as well, it may be a genuine bug. Here is the relevant piece of code from the jQuery source (1.4.2, line 4948):

// Create the request object; Microsoft failed to properly
// implement the XMLHttpRequest in IE7 (can't request local files),
// so we use the ActiveXObject when it is available
// This function can be overriden by calling jQuery.ajaxSetup
xhr: window.XMLHttpRequest && (window.location.protocol !== "file:" || !window.ActiveXObject) ?
    function() {
        return new window.XMLHttpRequest();
    } :
    function() {
        try {
            return new window.ActiveXObject("Microsoft.XMLHTTP");
        } catch(e) {}
    }


回答3:

How can I get code like this to work?

As suggested above, this looks like a fault in Microsoft XMLHttpRequest. jQuery (Jul 2011) also writes:-

Microsoft failed to properly implement the XMLHttpRequest in IE7 (can't request local files)

I confirm this failure for IE8 too.

A solution is to use new window.ActiveXObject( "Microsoft.XMLHTTP" ) for local files if XMLHttpRequest doesn't work.

The failure is in the xhr.open line so it can be caught there and then try ActiveXObject as follows:-

var xhr = new XMLHttpRequest()
try {
    xhr.open('GET', url, true)
}
catch(e) {
    try {
        xhr = new ActiveXObject('Microsoft.XMLHTTP')
        xhr.open('GET', url, true)
    }
    catch (e1) {
        throw new Error("Exception during GET request: " + e1)
    }
}

This code will at least use standard XMLHttpRequest for IE9 (untested) and future IE browsers if/when Microsoft fixes the fault. With the jQuery code above, non standard Microsoft.XMLHTTP will be used whenever ActiveXObject is available, even if Microsoft fix the fault.



回答4:

I know this is old - but wanted to clarify what is going on with a little more detail, and offer a couple of potential options.

First off this is actually not a flaw within IE, but a security feature that exists in Chrome as well.

Basically, any resource URI with file:// prefix is not allowed to load any other resource URI with file:// prefix using XMLHttpRequest.

In IE you will see an Access Denied message. In Chrome you will see "Failed to load resource: Origin null is not allowed by Access-Control-Allow-Origin" More info -> Information on IE and Information on Chrome (look for --allow-file-access-from-files)

An interesting thing with IE is that if you are using the .NET Browser Control inside of a WinForm or Silverlight App, this feature is disabled, and you will not have the same issues.

There are a couple of workarounds that I am aware of - none of which are ideal, as they disable a measure of security protection

  • IE: The first, and most "benign", is that you can add the URI of the calling page to the Trusted Sites zone (Uncheck "Enable Protected Mode")
  • IE: following the link above there is a registry setting that you can modify which will disable this feature - but this would have to be done on each machine trying to load the resources
  • Chrome: The link above refers to using the command line switch when starting Chrome to disable the feature.

Once again, this is a security feature within browsers to mitigate potential threat vectors - similar to the Cross Domain scripting blocks.