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

Contexte:

Je veux créer une «application» qui utilise JavaScript / HTML uniquement et peut être ouverte par un navigateur directement à partir du système de fichiers. Cette application doit pouvoir lire les données d'un autre fichier. Je vais ensuite utiliser JS pour l'analyser et rendre des pages. Comme exemple simplifié, imaginez que j'ai un fichier CSV (télécharger ici) :

Mark Rodgers,[email protected],Accounting [...] Melissa Jones,[email protected],CEO 

Je veux pouvoir lire le fichier à l'aide de JS et utiliser des données pour générer ma page.

Ce que j'ai accompli jusqu'ici:

Demo (clic droit -> "Enregistrer sous" pour enregistrer HTML sur votre ordinateur). Il est également disponible sur jsfiddle de manière semi-cassée (la mise en page est cassée, mais elle devrait toujours fonctionner correctement).

Il suffit de faire glisser et déposer le fichier texte CSV dans la zone de glisser-déposer ou de sélectionner le fichier texte à l'aide du menu de fichier, et JavaScript lira, analyse le fichier et remplit le tableau.

Cela repose sur l'API FileReader; La plupart des travaux de levage sont effectués 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 correct, mais cela gênait l'utilisateur de charger manuellement un fichier. Idéalement, il devrait pouvoir le charger automatiquement, mais pour des raisons de sécurité, aucun navigateur ne le permettra … encore.

Exigences de la solution:

  • Doit fonctionner hors ligne; C'est-à-dire: il ne peut pas compter sur un service en ligne. Cela inclut également les serveurs HTTP exécutés sur la machine locale. L'idée est de l'exécuter sur n'importe quel ordinateur avec un seul navigateur installé.

  • Doit fonctionner lorsque la page est ouverte en utilisant le file:/// protocole (c'est-à-dire: une page HTML sur le disque dur).

  • Ne doit pas compter sur des accessoires tiers (ex: Flash, Java, Shudders ActiveX). Je doute bien que cela ne fonctionnerait probablement pas si la page est en file:///

  • Il doit être en mesure d'accepter des données arbitraires . Cela exclut le chargement d'un fichier dans un format bien entretenu, prêt à être consommé, comme JSON.

  • Si cela fonctionne soit (idéalement à la fois) Firefox ou Chrome, ça va. Il est également bon de compter sur des API expérimentales

Je sais quel est le nom de fichier préalable, donc il pourrait être codé dans le HTML lui-même. Toute solution qui me permet de lire un fichier à partir du disque est bien, il ne doit pas utiliser l'API FileReader.

Donc, s'il y a un hack intelligent pour charger un fichier dans une page aussi bien (peut-être le charger dans un iframe invisible et avoir JS récupérer le contenu); C'est bien aussi.

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

Comme OP a commenté, enablePrivilege() a été obsolète, cela devrait être considéré comme utilisable. Mais comme mon Firefox utilisant le profil précédent fonctionne toujours avec mon code, alors je creuse un peu dans les prefs.jsabout:config cache ces paramètres), et voici les paramètres dont vous avez besoin pour que cela fonctionne.

 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; } }; 

Voici un exemple qui utilise les données JSON dans un fichier externe qui fonctionne localement ou sur un serveur. Cet exemple utilise simplement le paramètre de langue du navigateur pour charger un <script> avec html localisé puis traite son objet json pour réinitialiser les données dans les balises indiquées avec un 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 genre ressemblent à:

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

Mais vous pouvez utiliser n'importe quel paramètre pour charger conditionnellement le fichier approprié (il sera inséré comme première étiquette dans <head>, donc il sera utilisable n'importe où) et le format JSON est capable de gérer une grande variété de données. Vous voudrez peut-être renommer la fonction setLang à quelque chose de plus approprié et le modifier pour répondre à vos besoins tels que … pour chaque j'ajoute une ligne, puis ajoutez des champs avec les données (il semble que vous ayez déjà un contrôle sur cette 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 de prétraiter vos données, awk est très pratique – ce 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 est plus clair, seuls les navigateurs Mozilla permettent XMLHttpRequest dans le fichier: // hors de la boîte et chrome (peut-être d'autres navigateurs basés sur le Web) peuvent être configurés pour l'autoriser. Sachant qu'il ne PEUT PAS fonctionner 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 de la variation initiale de json-p pour d'autres qui peuvent avoir un problème similaire, mais ont un certain contrôle de leur format de données, car cela fonctionnera sur tous les navigateurs compatibles avec javascript. Cependant, si quelqu'un connaît un moyen de le faire fonctionner pour IE (autre que l'exécution d'un petit serveur web), modifiez-le.

Modifier 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> 

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

Si j'ai bien compris, le contenu du fichier est entièrement sous votre contrôle et il ne doit pas nécessairement ê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 devrait être comme ceci:

 handleFile('Mark Rodgers,[email protected],Accounting'); 

Pour "lire" le fichier, ajoutez simplement un élément de script avec un attribut src correspondant. Dans votre fonction "handleFile", vous obtenez votre contenu.

L'emplacement du fichier devrait normalement être défini par l'utilisateur, mais après cela, vous pouvez enregistrer l'emplacement dans localStorage ou quelque chose comme ça.

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

Contrairement à une étiquette de script, vous obtiendrez l'accès au contenu.

Cela peut être fait assez facilement en utilisant la classe JavaScript XMLHttpRequest ():

 function FileHelper() {} { FileHelper.readStringFromFileAtPath = function(pathOfFileToReadFrom) { var request = new XMLHttpRequest(); request.open("GET", pathOfFileToReadFrom, false); request.send(null); var returnValue = request.responseText; return returnValue; } } ... var text = FileHelper.readStringFromFileAtPath ( "mytext.txt" );