27 votes

Utilisation d'un fichier local comme source de données en JavaScript

Arrière-plan:

Je veux faire une "application" qui utilise JavaScript/HTML seulement et peut être ouvert par un navigateur, directement à partir du système de fichiers. Cette application doit être en mesure de lire les données à partir d'un autre fichier. Je vais ensuite utiliser JS pour analyser et afficher les pages. Comme un exemple simplifié, imaginez que j'ai un fichier CSV (à télécharger ici):

Mark Rodgers,mark.rodgers@company.com,Accounting
[...]
Melissa Jones,melissa@company.com,CEO

Je veux être en mesure de lire le fichier à l'aide de JS et de l'utilisation des données pour générer ma page.

Ce que j'ai accompli jusqu'à présent:

Démo (clic droit -> "Enregistrer sous" pour enregistrer le code HTML de votre ordinateur). Il est également disponible sur jsfiddle en semi-cassé de la mode (mise en page est cassé, mais il devrait encore être fonctionnellement correct).

Faites simplement glisser et déposez le fichier texte CSV dans le glisser-déposer la boîte, ou sélectionnez le fichier texte en utilisant le menu fichier, et JavaScript, lire, analyser le fichier et remplir le tableau.

Ceci repose sur la FileReader API; la plupart de la gros du travail est fait par cette fonction:

function handleFileSelect(evt) {
    evt.stopPropagation();
    evt.preventDefault();

    var files = evt.target.files || evt.dataTransfer.files; // FileList object.
    var file = files[0];

    // this creates the FileReader and reads stuff as text
    var fr = new FileReader();
    fr.onload = parse;
    fr.readAsText(file);

    // this is the function that actually parses the file
    // and populates the table
    function parse()
    {
        var table = document.getElementById('emps');
        var employees = fr.result.split('\n'); var c = 0;
        for (var i in employees)
        {
            var employee = employees[i].split(',');
            if (employee.length == 3)
            {
                var row = document.createElement('tr');
                row.innerHTML = "<td>" + employee.join("</td><td>") + "</td>";
                table.appendChild(row);
                c++;
            }
        }
        document.getElementById('result').innerHTML = '<span>Added ' + c + ' employees from file: ' + file.name + '</span>';
    }
}

C'est presque OK, mais il des inconvénients à l'utilisateur manuellement chargement d'un fichier. Idéalement, il devrait être en mesure de charger automatiquement, mais pour des raisons de sécurité, aucun navigateur n'autorise... pour l'instant.

Exigences De La Solution:

  • Doit travailler en mode hors connexion, c'est à dire: il ne peut pas compter sur un service en ligne. Cela inclut également les serveurs HTTP exécute sur l'ordinateur local. L'idée est d'avoir cet exécuter sur n'importe quel ordinateur avec un navigateur installé.

  • Doit fonctionner quand la page est ouverte à l'aide de l' file:/// protocole (c'est à dire: une page HTML sur le disque dur).

  • Devrait pas compter sur des tiers add-ons (par exemple: Flash, Java, frémit ActiveX). Je suis assez certain que ce ne serait probablement pas travailler de toute façon si la page est en file:///

  • Il doit être capable d'accepter l'arbitraire de données. Cela exclut le chargement d'un fichier dans un bien comportés format prêt à la consommation comme JSON.

  • Si cela fonctionne sur soit (idéalement deux) Firefox ou Chrome, c'est la fin. C'est aussi OK pour s'appuient sur les expériences des Api

Je sais ce que le nom de fichier est à l'avance, de sorte qu'il pourrait être codée dans le code HTML lui-même. Toute solution qui me permet de lire un fichier à partir du disque est très bien, il n'a pas à utiliser le FileReader API.

Donc si il y a un piratage intelligent pour charger un fichier dans une page qui est très bien aussi (peut-être charger dans une iframe invisible, et ont JS récupérer le contenu); c'est aussi OK.

4voto

xiaoyi Points 3501

Voici le code que j'ai utilisé pour Firefox, ce qui n'est pas portable, mais fonctionne:

Que l'OP a commenté, enablePrivilege() a été abandonné, cela devrait être considéré comme utilisable. Mais comme mon Firefox à l'aide de profil précédent, toujours travailler avec mon code, donc je fouille un peu dans l' prefs.js (en tant que about:config est de cacher ces paramètres,) Et voici les paramètres que vous avez besoin de vous le faire fonctionner.

user_pref("capability.principal.codebase.p0.granted", "UniversalXPConnect");
user_pref("capability.principal.codebase.p0.id", "file://");  // path to the html file.
user_pref("capability.principal.codebase.p0.subjectName", "");

Et voici le code:

var File = function(file) {
  netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
  var ios = Components.classes["@mozilla.org/network/io-service;1"]
                            .getService(Components.interfaces.nsIIOService);
  if (!File.baseURI) {
    File.baseURI = ios.newURI(location.href.substring(0, location.href.lastIndexOf('/')+1), null, null);
    File.baseFolder = File.baseURI.QueryInterface(Components.interfaces.nsIFileURL).file.path;
  }
  var URL = ios.newURI(file, null, File.baseURI);
  this.fptr = URL.QueryInterface(Components.interfaces.nsIFileURL).file;
}

File.prototype = {
  write: function(data) {
    netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
    var foStream = Components.classes["@mozilla.org/network/file-output-stream;1"]
                             .createInstance(Components.interfaces.nsIFileOutputStream);
    foStream.init(this.fptr, 0x02 | 0x08 | 0x20, 0666, 0);
    var converter = Components.classes["@mozilla.org/intl/converter-output-stream;1"]
                              .createInstance(Components.interfaces.nsIConverterOutputStream);
    converter.init(foStream, null, 0, 0);
    converter.writeString(data);
    converter.close();
  },
  read: function() {
    netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
    var fstream = Components.classes["@mozilla.org/network/file-input-stream;1"]
                            .createInstance(Components.interfaces.nsIFileInputStream);
    var cstream = Components.classes["@mozilla.org/intl/converter-input-stream;1"]
                            .createInstance(Components.interfaces.nsIConverterInputStream);
    fstream.init(this.fptr, -1, 0, 0);
    cstream.init(fstream, null, 0, 0);
    var data = "";
    // let (str = {}) { // use this only when using javascript 1.8
    var str = {};
      cstream.readString(0xffffffff, str);
      data = str.value;
    // }
    cstream.close();
    return data;
  }
};

4voto

technosaurus Points 1980

Voici un exemple qui utilise les données JSON dans un fichier externe qui fonctionne en local ou sur un serveur. Cet exemple utilise le navigateur réglage de la langue pour charger un < script > avec localisées html puis traite son objet json pour réinitialiser les données de balises avec du contenu localisé

<html><meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<head>
<script>
    function setLang(){
        for (var i=0;i<items.length;i++){
            term=document.getElementById(items[i].id)
            if (term) term.innerHTML=items[i].value
        }
    }
    var lang=navigator.userLanguage || navigator.language;
    var script=document.createElement("script");
    script.src=document.URL+"-"+lang.substring(0,2)+".js"
    var head = document.getElementsByTagName('head')[0]
    head.insertBefore(script,head.firstChild)
</script>
</head>
<body onload='setLang()'>
<div id="string1" class="txt">This is the default text of string1.</div> 
<div id="string2" class="txt">This is the default text of string2.</div>
</body></html>

Les fichiers de données pour ce look, comme:

items=[
{"id":"string1","value":"Localized text of string1."},
{"id":"string2", "value":"Localized text of string2."}
];

mais vous pouvez utiliser n'importe quel paramètre à conditionnellement charger le fichier approprié (il sera inséré à la première balise < head >, de sorte qu'il sera utilisable dans n'importe où) et le format JSON est capable de traiter une grande variété de données. Vous pouvez renommer la fonction setLang quelque chose de plus approprié et de le modifier pour répondre à vos besoins, tels que les ... pour chaque j'ai ajouter une ligne, puis ajouter les champs avec les données (on dirait que vous avez déjà une poignée sur la partie) et votre JSON ressemblerait à:

items=[
{"fname":"john","lname":"smith","address":"1 1st St","phone":"555-1212"},
{"fname":"jane","lname":"smith","address":"1 1st St","phone":"555-1212"}
];

si vous avez besoin pour traiter vos données, awk est très pratique - il serait quelque chose comme: (non testé guestimate)

awk 'BEGIN{FS=",";print "items=[\n"}
{printf "{\"fname\":\"%s\",\"lname\":\"smith\",\"address\":\"1 1st St\",\"phone\":\"555-1212\"},\n", $1, $2, $3, $4}
END{print "];"}' file.csv > file.js

Edit: maintenant que l'opération est plus clair, seuls les navigateurs mozilla permettre XMLHttpRequest sur file:// sortir de la boîte et de chrome (peut-être d'autres navigateurs basés sur webkit) peut être configuré de manière à autoriser. Sachant qu'il peut PAS travailler sur IE<10, vous pouvez:

var filePath = "your_file.txt";
xmlhttp = new XMLHttpRequest();
xmlhttp.open("GET",filePath,false);
xmlhttp.overrideMimeType('text/plain');
xmlhttp.send(null);
//maybe check status !=404 here
var fileContent = xmlhttp.responseText;
var fileArray = fileContent.split('\n')
var n = fileArray.length;
//process your data from here probably using split again for ','

Je pars la première json-p variation pour les autres qui peuvent avoir un problème similaire, mais avoir un certain contrôle de leur format de données, car il fonctionne sur tous les navigateur compatible javascript. Cependant, si quelqu'un connaît un moyen pour le faire fonctionner pour IE (autres que l'exécution d'un petit serveur web), veuillez modifier.

Edit 2:

Avec les navigateurs mozilla, vous pouvez également utiliser des iframes

<html>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<head>
<script>
function showContents(frameObject){
    alert(frameObject.contentDocument.body.innerHTML);
    //replace with your code
}
</script>
</head>
<body onload='showContents()'>
<iframe id="frametest" src="data.txt" onload="showContents(this);" 
    style="visibility:hidden;display:none"></iframe>
</body></html>

2voto

Inkbug Points 980

En supposant que le fichier csv se trouve dans le même répertoire que l'application, je chargerais le fichier avec AJAX. Autant que je sache, on peut obtenir le fichier au format texte, puis l'analyser. Cela devrait fonctionner dans IE et Firefox, mais ne fonctionne pas dans Chrome (sauf si l'on exécute Chrome avec le paramètre de ligne de commande --allow-file-access-from-files ).

1voto

dave Points 1448

Ce que je comprends, le contenu du fichier est entièrement sous votre contrôle, et il n'a pas à être un format spécifique? Et vous avez seulement besoin d'un moyen de lire?

Vous pouvez déclarer une fonction globale "handleFile". Dans votre fichier externe le contenu pourrait être comme ceci:

handleFile('Mark Rodgers,mark.rodgers@company.com,Accounting');

Pour "lire" le fichier, il suffit d'ajouter un élément de script avec un correspondant attribut src. Dans votre fonction "handleFile", vous obtenez votre contenu.

L'emplacement du fichier serait probablement où elles doivent être définies par l'utilisateur, mais après cela, vous pouvez enregistrer la position dans localStorage ou quelque chose comme ça.

1voto

Gerard Sexton Points 1569

Assurez-vous que le fichier se trouve dans le même répertoire ou dans un sous-répertoire, chargez le fichier avec AJAX.

Contrairement à une balise de script, vous aurez accès au contenu.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X