172

I'm attempting to provide a script-only solution for reading the contents of a file on a client machine through a browser.

I have a solution that works with Firefox and Internet Explorer. It's not pretty, but I'm only trying things at the moment:

function getFileContents() {
    var fileForUpload = document.forms[0].fileForUpload;
    var fileName = fileForUpload.value;

    if (fileForUpload.files) {
        var fileContents = fileForUpload.files.item(0).getAsBinary();
        document.forms[0].fileContents.innerHTML = fileContents;
    } else {
        // try the IE method
        var fileContents = ieReadFile(fileName);
        document.forms[0].fileContents.innerHTML = fileContents;
    }
}       

function ieReadFile(filename) 
{
    try
    {
        var fso  = new ActiveXObject("Scripting.FileSystemObject"); 
        var fh = fso.OpenTextFile(filename, 1); 
        var contents = fh.ReadAll(); 
        fh.Close();
        return contents;
    }
    catch (Exception)
    {
        return "Cannot open file :(";
    }
}

I can call getFileContents() and it will write the contents into the fileContents text area.

Is there a way to do this in other browsers?

I'm most concerned with Safari and Chrome at the moment, but I'm open to suggestions for any other browser.

Edit: In response to the question, "Why do you want to do this?":

Basically, I want to hash the file contents together with a one-time-password on the client side so I can send this information back as a verification.

Damovisa
  • 19,213
  • 14
  • 66
  • 88
  • not that I have an answer but just for clarity's sake, do you need to know the location of the file? If not, does the location of the file have to be read from a file input or can it be a textbox/textarea/whatever? – Darko Apr 15 '09 at 02:14
  • Good question. No, I don't really care about where the file comes from, only its contents. Using a file input seems sensible to me though as it's native html - there's one less thing I have to do. – Damovisa Apr 15 '09 at 02:45
  • why do you want to do this at all? the server is meant to do that. – geowa4 Apr 15 '09 at 23:48
  • Ok, in short: A user enters a password and selects a file. The password gets hashed with the file contents and this gets sent to the server along with the file. When it gets there, I can verify that the correct client password was used. – Damovisa Apr 16 '09 at 00:48
  • 1
    2021: `let a = await file.text();` – Harshal Parekh Oct 21 '21 at 11:18

5 Answers5

235

Edited to add information about the File API

Since I originally wrote this answer, the File API has been proposed as a standard and implemented in most browsers (as of IE 10, which added support for FileReader API described here, though not yet the File API). The API is a bit more complicated than the older Mozilla API, as it is designed to support asynchronous reading of files, better support for binary files and decoding of different text encodings. There is some documentation available on the Mozilla Developer Network as well as various examples online. You would use it as follows:

var file = document.getElementById("fileForUpload").files[0];
if (file) {
    var reader = new FileReader();
    reader.readAsText(file, "UTF-8");
    reader.onload = function (evt) {
        document.getElementById("fileContents").innerHTML = evt.target.result;
    }
    reader.onerror = function (evt) {
        document.getElementById("fileContents").innerHTML = "error reading file";
    }
}

Original answer

There does not appear to be a way to do this in WebKit (thus, Safari and Chrome). The only keys that a File object has are fileName and fileSize. According to the commit message for the File and FileList support, these are inspired by Mozilla's File object, but they appear to support only a subset of the features.

If you would like to change this, you could always send a patch to the WebKit project. Another possibility would be to propose the Mozilla API for inclusion in HTML 5; the WHATWG mailing list is probably the best place to do that. If you do that, then it is much more likely that there will be a cross-browser way to do this, at least in a couple years time. Of course, submitting either a patch or a proposal for inclusion to HTML 5 does mean some work defending the idea, but the fact that Firefox already implements it gives you something to start with.

Kat
  • 4,645
  • 4
  • 29
  • 81
Brian Campbell
  • 322,767
  • 57
  • 360
  • 340
  • Thanks for that - I don't think I'm dedicated enough at this point to submit a patch. It's something that you probably wouldn't want happening without your knowledge anyway. It kinda breaks the browser sandbox... – Damovisa Apr 16 '09 at 02:23
  • 5
    It doesn't break the browser sandbox, since you have deliberately chosen to upload that file; if it can get to the server, it can get back to the browser, just with an extra round trip. Given the work that is going into making offline mode work for web apps, this would be a reasonable feature. – Brian Campbell Apr 16 '09 at 02:31
  • Mm, actually that's a fair point. There was user interaction to choose that file. Thanks. – Damovisa Apr 17 '09 at 00:42
  • 1
    @Damovisa I don't know if you still care about this, but I figured that I would update my answer to mention the new File API that does what you're looking for, and is implemented in Firefox, Chrome, and nightly builds of Safari. – Brian Campbell Apr 19 '11 at 15:47
  • @Brian Campbell: Thanks for the post! However, there’s something I don’t understand: why isn’t `reader` or `this` used instead of `evt.target` while they all refer to the `FileReader` object: **[demo](http://jsfiddle.net/Mori/tJBHZ/)**. – Mori May 20 '14 at 09:48
  • Great! FileReader works! Tested under FireFox, MS Edge and MS Internet Explorer. – José Carlos PHP Feb 15 '17 at 12:01
  • 2
    shouldn't the event handlers be attached before calling `readAsText` ? – goofballLogic Jan 19 '18 at 14:19
  • @BrianCampbell I would say it could break the browser sandbox because, unfortunately, not everyone is as computer literate as me and you. Such people are prone to actions unthinkable by us. Hypothetically, in an alternate reality where the browsers do not explicitly prevent doing so, these individuals would be prone to naively uploading the C-drive simply because the website told them to. Likewise, such humans can do other actions that are allowed, but not wise. That is why browsers are proceeding with such caution implementing a file upload API, and especially the recommended folder uploads. – Jack G Apr 04 '18 at 21:21
  • I can confirm that the event handlers must be attached *before* calling `readAsText`! – BenMorel Feb 11 '21 at 16:57
33

In order to read a file chosen by the user, using a file open dialog, you can use the <input type="file"> tag. You can find information on it from MSDN. When the file is chosen you can use the FileReader API to read the contents.

function onFileLoad(elementId, event) {
    document.getElementById(elementId).innerText = event.target.result;
}

function onChooseFile(event, onLoadFileHandler) {
    if (typeof window.FileReader !== 'function')
        throw ("The file API isn't supported on this browser.");
    let input = event.target;
    if (!input)
        throw ("The browser does not properly implement the event object");
    if (!input.files)
        throw ("This browser does not support the `files` property of the file input.");
    if (!input.files[0])
        return undefined;
    let file = input.files[0];
    let fr = new FileReader();
    fr.onload = onLoadFileHandler;
    fr.readAsText(file);
}
<input type='file' onchange='onChooseFile(event, onFileLoad.bind(this, "contents"))' />
<p id="contents"></p>
cdiggins
  • 17,602
  • 7
  • 105
  • 102
32

There's a modern native alternative: File implements Blob, so we can call Blob.text().

async function readText(event) {
  const file = event.target.files.item(0)
  const text = await file.text();
  
  document.getElementById("output").innerText = text
}
<input type="file" onchange="readText(event)" />
<pre id="output"></pre>

Currently (September 2020) this is supported in Chrome and Firefox, for other Browser you need to load a polyfill, e.g. blob-polyfill.

wollnyst
  • 1,683
  • 1
  • 17
  • 20
4

Happy coding!
If you get an error on Internet Explorer, Change the security settings to allow ActiveX

var CallBackFunction = function(content) {
  alert(content);
}
ReadFileAllBrowsers(document.getElementById("file_upload"), CallBackFunction);
//Tested in Mozilla Firefox browser, Chrome
function ReadFileAllBrowsers(FileElement, CallBackFunction) {
  try {
    var file = FileElement.files[0];
    var contents_ = "";

    if (file) {
      var reader = new FileReader();
      reader.readAsText(file, "UTF-8");
      reader.onload = function(evt) {
        CallBackFunction(evt.target.result);
      }
      reader.onerror = function(evt) {
        alert("Error reading file");
      }
    }
  } catch (Exception) {
    var fall_back = ieReadFile(FileElement.value);
    if (fall_back != false) {
      CallBackFunction(fall_back);
    }
  }
}
///Reading files with Internet Explorer
function ieReadFile(filename) {
  try {
    var fso = new ActiveXObject("Scripting.FileSystemObject");
    var fh = fso.OpenTextFile(filename, 1);
    var contents = fh.ReadAll();
    fh.Close();
    return contents;
  } catch (Exception) {
    alert(Exception);
    return false;
  }
}
Parth Raval
  • 4,097
  • 3
  • 23
  • 36
Mnyikka
  • 1,223
  • 17
  • 12
  • 9
    [Acxtive X is now (thankfully) dead](https://blogs.windows.com/msedgedev/2015/05/06/a-break-from-the-past-part-2-saying-goodbye-to-activex-vbscript-attachevent/) – Liam Jul 14 '16 at 10:00
1

This works fine

function onClick(event) {
    filecontent = "";
    var myFile = event.files[0];
    var reader = new FileReader();

    reader.addEventListener('load', function (e) {
      filecontent = e.target.result;

    });
    reader.readAsBinaryString(myFile);
  }
Deepak N
  • 1,408
  • 3
  • 15
  • 37