web-dev-qa-db-ja.com

JavaScriptでローカルファイルをデータソースとして使用する

背景:

JavaScript/HTMLのみを使用し、ブラウザでファイルシステムから直接開くことができる「アプリ」を作成したいと考えています。このアプリは別のファイルからデータを読み取ることができる必要があります。次に、JSを使用してそれを解析し、ページをレンダリングします。簡単な例として、CSVファイルがあると想像してください (ここからダウンロード)

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

JSを使用してファイルを読み取り、その中のデータを使用してページを生成できるようにしたいと考えています。

これまでに達成したこと:

デモ(右クリック-> [名前を付けて保存]をクリックして、HTMLをコンピュータに保存します)。 jsfiddle でも半壊状態で利用できます(レイアウトは壊れていますが、機能的には正しいはずです)。

CSVテキストファイルをドラッグアンドドロップボックスにドラッグアンドドロップするか、ファイルメニューを使用してテキストファイルを選択するだけで、JavaScriptがファイルを読み取って解析し、テーブルに入力します。

これはFileReader APIに依存しています。重労働のほとんどはこの関数によって行われます:

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

これはほぼOKですが、ユーザーが手動でファイルをロードするのに不便です。理想的には、自動的にロードできるはずですが、セキュリティ上の理由から、ブラウザはそれを許可しません...まだです。

ソリューション要件:

  • オフラインで作業する必要があります。つまり、オンラインサービスに依存することはできません。これには、ローカルマシンで実行されているHTTPサーバーも含まれます。これは、ブラウザがインストールされている任意のコンピュータでこれを実行するという考え方です。

  • file:///プロトコルを使用してページを開いたときに機能する必要があります(つまり、ハードドライブ上のHTMLページ)。

  • しないサードパーティのアドオンに依存する必要があります(例:Flash、Java、shuddersActiveX)。ページがfile:///にある場合、これらはおそらく機能しないと思います。

  • 任意のデータを受け入れることができる必要があります。これにより、JSONのようにすぐに使用できる適切な形式でファイルを読み込むことができなくなります。

  • FirefoxまたはChrome=)のいずれか(理想的には両方)で動作する場合は問題ありません。実験的なAPIに依存しても問題ありません。

ファイル名は事前にわかっているので、HTML自体にコーディングできます。 ディスクからファイルを読み取ることができるすべてのソリューションは問題ありません。FileReaderAPIを使用する必要はありません。

したがって、ファイルをページにロードするための巧妙なハックがある場合(おそらくそれを非表示のiframeにロードし、JSにコンテンツを取得させる)、それも大丈夫です。

30

これが私がFirefoxに使用したコードですnot移植可能ですが、 働く

OPがコメントしたように、enablePrivilege()は非推奨になりました。これは使用可能と見なされるべきです。しかし、以前のプロファイルを使用している私のFirefoxはまだ私のコードで動作するため、prefs.js (なので about:configはこれらの設定を非表示にしています。)そして、これを機能させるために必要な設定があります。

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", "");

そしてここにコードがあります:

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

ローカルまたはサーバーで動作する外部ファイルでJSONデータを使用する例を次に示します。この例では、ブラウザーの言語設定を使用して、ローカライズされたhtmlを含む<script>をロードし、そのjsonオブジェクトを処理して、ローカライズされたコンテンツを含む指定されたタグのデータをリセットします

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

このためのデータファイルは次のようになります。

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

ただし、任意のパラメーターを使用して適切なファイルを条件付きで読み込むことができ(<head>の最初のタグとして挿入されるため、どこでも使用できます)、JSON形式はさまざまなデータを処理できます。関数setLangの名前をより適切な名前に変更し、ニーズに合わせて変更することができます。たとえば、行を追加するたびに、データを含むフィールドを追加します(その部分に既にハンドルがあるようです)。 JSONは次のようになります。

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

データを前処理する必要がある場合、awkはかなり便利です次のようなゲスト評価)

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

編集:OPがより明確になったため、mozillaブラウザーのみがfile://でXMLHttpRequestをそのまま使用でき、chrome(おそらく他のWebkitベースのブラウザー))---(can[〜#〜] not [〜#〜] IE <10で動作する可能性があることを知っていると、次のことができます。

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 ','

Json-pの最初のバリエーションは、同様の問題が発生する可能性がある他のものに任せますが、すべてのJavaScript対応ブラウザで動作するため、データ形式をある程度制御できます。ただし、IE(小さなWebサーバーを実行する以外))で動作させる方法を知っている人がいる場合は、編集してください。

編集2:

Mozillaブラウザでは、iframeも使用できます

<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>
4
technosaurus

Csvファイルがアプリと同じディレクトリにあると想定して、AJAXでファイルをロードします。私の知る限り、ファイルをテキスト形式で取得して解析することができます。これはIEとFirefoxでは機能するはずですが、Chromeでは機能しません(chromeを_--allow-file-access-from-files_コマンドライン設定で実行しない限り)。

3
Inkbug

ファイルが同じディレクトリまたはサブディレクトリにあることを確認して、AJAXでファイルをロードします。

スクリプトタグとは異なり、コンテンツにアクセスできます。

1
Gerard Sexton

これは、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" );
1
user3375451

私が理解しているように、ファイルの内容は完全にあなたの管理下にあり、特定の形式である必要はありませんか?そして、あなたは読む方法だけが必要ですか?

グローバル関数「handleFile」を宣言できます。外部ファイルのコンテンツは次のようにする必要があります。

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

ファイルを「読み取る」には、対応するsrc属性を持つスクリプト要素を追加するだけです。関数「handleFile」では、コンテンツを取得します。

ファイルの場所は、おそらく最初はユーザーが設定する必要がありますが、その後、その場所をlocalStorageなどに保存できます。

1
dave