LoginSignup
98
104

More than 5 years have passed since last update.

Electronでファイルやフォルダの選択

Last updated at Posted at 2015-09-30

※こちらの記事の内容はElectronの現行のVersion 1.x系では動作しません
http://qiita.com/khirose/items/11dae9f4650767d20c4f
こちらにversion 1.x系でのの焼き直しをしていますので参照ください

はじめに

HTML5/CSS/JavaScriptといったWeb技術でおなじみの技術でネイティブアプリも作れるよ!という触れ込みのElectron
ネイティブアプリなら自分のPCの中にあるファイルやフォルダを扱いたいわけで

普通のHTML + JavaScriptでやろうとしたら失敗した

画面のHTMLに

<input type="file">
<input type="file" webkitdirectory directory>

を書いてこいつのchangeイベントをひっかけて・・・とやったけどダメ
あくまでもChromiumのサンドボックスの世界からは出ないブラウザとしての振る舞いになるので、入ってくるファイル名などは C:\fakeroot\ファイル名 となって、PCのファイルシステム上のパスではないのでそのままファイル名として使えない
ファイル名は重要じゃなく、中身だけが必要だという場合であればここからReader作って...でいいんだけど、今回は必要な要件の中にファイル名もあるのでこの方法は無理筋と判断

そこでプロセス間通信APIを使えばいい

画面提供側(レンダラプロセス)から

var remote = require('remote');

でnode.jsベースで動いているメインプロセスを取得できる
HTML側のclickイベントを補足して、そのハンドラ内でブラウザ側のjsではなくメインプロセス側のAPIを叩けばネイティブのファイルを選ぶダイアログ、フォルダを選ぶダイアログを表示したり、選択内容(PCのファイルシステム上のフルパス名)を引っ張れる
あとはこれをHTML側に統合されているnode.jsのfsモジュールとかで操作すればよい

以下はサンプル(jQueryとBootstrapをローカルに入れて使ってます)

package.json
{
  "name": "example",
  "version": "0.0.0",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "NYSL",
  "keywords": [],
  "description": ""
}
index.js
'use strict';

var app = require('app');
var BrowserWindow = require('browser-window');

require('crash-reporter').start();

var mainWindow = null;

app.on('window-all-closed', function() {
    if (process.platform != 'darwin') app.quit();
});

app.on('ready', function() {
    mainWindow = new BrowserWindow({width: 1024, height: 768});

    //  ↓を入れるとデフォルトで開発ツールが開いた状態になる
    mainWindow.openDevTools();

    mainWindow.loadUrl('file://' + __dirname + '/index.html');
    mainWindow.on('closed', function() {
        mainWindow = null;
    });
});
index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Example</title>
<link rel="stylesheet" href="./css/bootstrap.min.css">
</head>
<body>
<div class="container">
    <button id="fileSelect" type="button" class="btn btn-primary btn-lg btn-block">ファイル選択</button>
    <button id="folderSelect" type="button" class="btn btn-default btn-lg btn-block">フォルダ選択</button>
</div>

<script>
// node_integrationを行うとmodule.exports関係の事情でjQuery/$がundefinedになるので
// それの回避コード
window.jQuery = window.$ = require('./js/jquery-2.1.4.min.js');
</script>
<script src="./js/bootstrap.min.js"></script>
<script>
/**
* ここからメインディッシュ
*/
var remote = require('remote');
var dialog = remote.require('dialog');
var browserWindow = remote.require('browser-window');

//  以下選択されたファイルをいじりたい場合はnode.jsのfsが使える
var fs = require('fs');

$(function(){
    // ボタンが押されたときの挙動
    $('#fileSelect').on('click', function(){
        var focusedWindow = browserWindow.getFocusedWindow();

        dialog.showOpenDialog(focusedWindow, {
            properties: ['openFile'],
            filters: [{
                name: 'テキストファイル',
                extensions: ['txt']             
            }]
        }, function(files){
            files.forEach(function(file){
                console.log(file);
            });
        });
    });

    $('#folderSelect').on('click', function(){
        var focusedWindow = browserWindow.getFocusedWindow();

        dialog.showOpenDialog(focusedWindow, {
            properties: ['openDirectory']
        }, function(directories){
            directories.forEach(function(directory){
                console.log(directory);
            });
        });
    });
});
</script>
</body>
</html>

ダイアログを出した上で何も選択しないとfilesやdirectoriesがundefinedになり、undefined.forEachを呼ぼうとしてエラーが出るっぽい
しかしクライアントサイドのJavaScriptに何も考えずrequire()書けるのは不思議な感覚

98
104
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
98
104