Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Chrome - Fetch API cannot load file. How to workaround?

I have the following two files:

index.html

<html>
<head>
<meta charset="utf-8" />
<title>Web Page</title>
<style type="text/css">
.text {
    display: inline-block;
    font-family: tahoma;
    font-size: 14px;
    max-width: 400px;
    background-color: #ddedff;
    padding: 10px;
    text-align: justify;
}
</style>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script type="text/javascript">
$(function(){
    get_data('info.txt');
});
function get_data(file) {
    var request = new Request(file);
    fetch(request).then(function(response) {
        return response.text().then(function(text) {
            $('.text').html(text);
        });
    });
}
</script>
</head>
<body>
    <div class="text"></div>
</body>
<html>

info.txt

Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.

When I open on Mozilla Firefox the file: README.html through this local URI:

file:///C:/testing/README.html

it works as expected, I mean, the text on file: info.txt is displayed properly.

But when I open the same URI on Google Chrome I get a blank screen and the following error on the console:

README.html:26 Fetch API cannot load file:///C:/testing/README.md. URL scheme must be "http" or "https" for CORS request.
get_data @ README.html:26
README.html:26 Uncaught (in promise) TypeError: Failed to fetch
    at get_data (README.html:26)
    at HTMLDocument.<anonymous> (README.html:21)
    at l (jquery.min.js:2)
    at c (jquery.min.js:2)

Do you have what can I do so I can open local files on Google Chrome as I can do on Mozilla Firefox?

If I have to do some tweak on:

chrome://flags/

that's acceptable for me.

EDIT

I tried launching Google Chrome from the command line with the flag: --allow-file-access-from-files as recommended here but now I get the following error:

README.html:26 Fetch API cannot load file:///C:/testing/README.md. URL scheme "file" is not supported.
get_data @ README.html:26
README.html:26 Uncaught (in promise) TypeError: Failed to fetch
    at get_data (README.html:26)
    at HTMLDocument.<anonymous> (README.html:21)
    at l (jquery.min.js:2)
    at c (jquery.min.js:2)

Thanks!

like image 641
davidesp Avatar asked Apr 22 '18 23:04

davidesp


People also ask

Can you use fetch on a local file?

Fetch is an API to request data through networks using Http request, and we could also use this to request local files!

How do you fetch a file in JavaScript?

JavaScript | fetch() Method. The fetch() method in JavaScript is used to request to the server and load the information on the webpages. The request can be of any APIs that return the data of the format JSON or XML. This method returns a promise.

How do I use fetch API to get data?

The Fetch API allows you to asynchronously request for a resource. Use the fetch() method to return a promise that resolves into a Response object. To get the actual data, you call one of the methods of the Response object e.g., text() or json() . These methods resolve into the actual data.

Is fetch a browser API?

The Fetch API provides an interface for fetching resources (including across the network). It will seem familiar to anyone who has used XMLHttpRequest , but the new API provides a more powerful and flexible feature set.


1 Answers

For chrome you still need --allow-file-access-from-files (and I recommend installing a separate chrome and using it solely for these projects to stay secure), but just shim fetch() for XMLHttpRequest for file:// requests:

if (/^file:\/\/\//.test(location.href)) {
    let path = './';
    let orig = fetch;
    window.fetch = (resource) => ((/^[^/:]*:/.test(resource)) ?
        orig(resource) :
        new Promise(function(resolve, reject) {
            let request = new XMLHttpRequest();

            let fail = (error) => {reject(error)};
            ['error', 'abort'].forEach((event) => { request.addEventListener(event, fail); });

            let pull = (expected) => (new Promise((resolve, reject) => {
                if (
                    request.responseType == expected ||
                    (expected == 'text' && !request.responseType)
                )
                    resolve(request.response);
                else
                    reject(request.responseType);
            }));

            request.addEventListener('load', () => (resolve({
                arrayBuffer : () => (pull('arraybuffer')),
                blob        : () => (pull('blob')),
                text        : () => (pull('text')),
                json        : () => (pull('json'))
            })));
            request.open('GET', resource.replace(/^\//, path));
            request.send();
        })
    );
}

This shim will;

  • only activate for html files opened locally (outer if statement),
  • call the normal fetch() for any url that doesn't specify protocol (and thus non-file:// requests), and
  • will replace absolute paths (/root/bob.html) with ones relative to the current path (since that would dangerously evaluate to C:\ or equivalent)

Set path to something else if your index.html isn't actually at the root for the project.
If you need support for init, or anything other than text(), you'll need to add it.
Explicit file:// requests wont be fulfilled, that's on purpose, but if you really do know what you're doing, you'll know how to make this work for you, and if you don't you shouldn't.


The following is useful if you're going to be doing this for multiple files. Swap out './' for document.currentScript.getAttribute('data-root'). Now you can put that snippet into its own file, say filesystemHelper.js, and call like so in the various files:

<script src="../filesystemHelper.js" data-root="../"></script>

Pretty snazzy.

like image 155
Hashbrown Avatar answered Sep 28 '22 07:09

Hashbrown